From cddb66027e39adae589585e444ff6485fcd597f6 Mon Sep 17 00:00:00 2001 From: cemycc Date: Fri, 29 Apr 2011 22:58:59 +0300 Subject: [PATCH 002/215] Create directory for the Translation Manager plugin --HG-- branch : gsoc2011-translationovqt --- .../object_viewer_qt/src/plugins/translation_manager/README | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/README diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/README b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/README new file mode 100644 index 000000000..b182bfdcf --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/README @@ -0,0 +1,5 @@ +Translation Manager Plugin +-------------------------- +GSoC 2011 Project +http://dev.ryzom.com/wiki/ryzom/OVQTTranslationPluginGSoc2011 + From f5a42f16de89523acf50bf5fdb5d87dd9c4cd9a3 Mon Sep 17 00:00:00 2001 From: sfb Date: Sun, 8 May 2011 14:59:27 -0500 Subject: [PATCH 003/215] Changed: Started fixing up the menus. --- .../zone_painter/zone_painter_model.cpp | 40 ++++++++++++++++++ .../plugins/zone_painter/zone_painter_model.h | 42 +++++++++++++++++++ .../zone_painter/zone_painter_plugin.cpp | 32 ++++++++------ .../zone_painter/zone_painter_plugin.h | 5 +++ 4 files changed, 107 insertions(+), 12 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_model.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_model.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_model.cpp new file mode 100644 index 000000000..4ab93d62d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_model.cpp @@ -0,0 +1,40 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "zone_painter_model.h" + +// STL includes + +// Qt includes + +// NeL includes + +namespace Plugin +{ + +CZonePainterModel::CZonePainterModel() +{ + +} + +CZonePainterModel::~CZonePainterModel() +{ + +} + +} /* namespace Plugin */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_model.h new file mode 100644 index 000000000..259634b63 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_model.h @@ -0,0 +1,42 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef ZONE_PAINTER_MODEL_H +#define ZONE_PAINTER_MODEL_H + +// NeL includes +#include +#include +#include + +// Qt includes + +namespace Plugin +{ + +class CZonePainterModel +{ +public: + CZonePainterModel(); + virtual ~CZonePainterModel(); + +}; /* class CZonePainterModel */ + +} /* namespace Plugin */ + + +#endif // ZONE_PAINTER_MODEL_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.cpp index 7b8237ccb..bfd310e95 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.cpp @@ -16,6 +16,7 @@ #include #include #include +#include namespace Plugin { @@ -37,6 +38,7 @@ bool ZonePainterPlugin::initialize(ExtensionSystem::IPluginManager *pluginManage addAutoReleasedObject(new CZonePainterSettingsPage(this)); addAutoReleasedObject(new CZonePainterContext(this)); //addAutoReleasedObject(new CCoreListener(this)); + return true; } @@ -44,20 +46,26 @@ void ZonePainterPlugin::extensionsInitialized() { Core::ICore *core = Core::ICore::instance(); Core::IMenuManager *menuManager = core->menuManager(); - //menuManager = _plugMan->getObject(); - QAction *exampleAction1 = new QAction("Zone1", this); - QAction *exampleAction2 = new QAction("Zone2", this); - QAction *aboutQtAction = menuManager->action(Core::Constants::ABOUT_QT); - QMenu *helpMenu = menuManager->menu(Core::Constants::M_HELP); - helpMenu->insertAction(aboutQtAction, exampleAction1); - helpMenu->addSeparator(); - helpMenu->addAction(exampleAction2); - QMenu *zoneMenu = menuManager->menuBar()->addMenu("ZoneMenu"); - zoneMenu->insertAction(aboutQtAction, exampleAction1); - zoneMenu->addSeparator(); - zoneMenu->addAction(exampleAction2); + QAction *loadZoneAction = new QAction("Load Zone", this); + QAction *saveZoneAction = new QAction("Save Zone", this); + + QMenu *toolsMenu = menuManager->menu(Core::Constants::M_TOOLS); + QMenu *zoneMenu = toolsMenu->addMenu("Zone Painter"); + zoneMenu->addAction(loadZoneAction); + connect(loadZoneAction, SIGNAL(triggered()), this, SLOT(clickLoadZoneAction())); + zoneMenu->addAction(saveZoneAction); } +/****** SLOTS ******/ +void ZonePainterPlugin::clickLoadZoneAction() { + QString zoneFile = QFileDialog::getOpenFileName(NULL, tr("Open Zone File"), ".", tr("Zone Files (*.zone);;")); +} + +void ZonePainterPlugin::clickSaveZoneAction() { + +} +/****** END SLOTS ******/ + void ZonePainterPlugin::setNelContext(NLMISC::INelContext *nelContext) { #ifdef NL_OS_WINDOWS diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.h index aeae39ba6..2731637ed 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.h @@ -65,7 +65,10 @@ public: QObject *objectByName(const QString &name) const; ExtensionSystem::IPluginSpec *pluginByName(const QString &name) const; +public Q_SLOTS: + void clickLoadZoneAction(); + void clickSaveZoneAction(); protected: NLMISC::CLibraryContext *_LibContext; @@ -73,6 +76,8 @@ protected: private: ExtensionSystem::IPluginManager *_plugMan; QList _autoReleaseObjects; + + NL3D::CLandscapeModel *m_Landscape; }; class CZonePainterContext: public Core::IContext From f47f5a3799b315b06cdaab759c421b265cb58ef5 Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 25 May 2011 00:06:07 +0300 Subject: [PATCH 004/215] first stage for my plugin --HG-- branch : gsoc2011-translationovqt --- .../tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt | 3 ++- .../src/plugins/object_viewer/scheme_manager.h | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt index d5a792acd..125c0c8c4 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt @@ -4,4 +4,5 @@ ADD_SUBDIRECTORY(ovqt_sheet_builder) ADD_SUBDIRECTORY(log) ADD_SUBDIRECTORY(disp_sheet_id) ADD_SUBDIRECTORY(object_viewer) -ADD_SUBDIRECTORY(zone_painter) \ No newline at end of file +ADD_SUBDIRECTORY(zone_painter) +ADD_SUBDIRECTORY(translation_manager) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_manager.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_manager.h index 01a2abb35..7b06734f2 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_manager.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_manager.h @@ -27,8 +27,8 @@ namespace NL3D { class CPSAttribMakerBase; } - -namespace NLQT + +namespace NLQT { class CSchemeManager @@ -51,11 +51,11 @@ public: // rename a scheme, given a pointer on it void rename(NL3D::CPSAttribMakerBase *am, const std::string &newName); protected: - typedef std::pair TSchemeInfo; + // typedef std::pair TSchemeInfo; typedef std::multimap TSchemeMap; TSchemeMap _SchemeMap; }; } /* namespace NLQT */ -#endif \ No newline at end of file +#endif From 9dab149983def5b077e99fa0924c310fb24a1915 Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 25 May 2011 00:07:32 +0300 Subject: [PATCH 005/215] first stage for my plugin --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/CMakeLists.txt | 41 ++++ .../translation_manager/qnel_widget.cpp | 197 ++++++++++++++++++ .../plugins/translation_manager/qnel_widget.h | 130 ++++++++++++ .../translation_manager/simple_viewer.cpp | 54 +++++ .../translation_manager/simple_viewer.h | 54 +++++ .../translation_manager_plugin.cpp | 122 +++++++++++ .../translation_manager_plugin.h | 94 +++++++++ .../translation_manager_settings_page.cpp | 67 ++++++ .../translation_manager_settings_page.h | 58 ++++++ .../translation_manager_settings_page.ui | 92 ++++++++ 10 files changed, 909 insertions(+) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt new file mode 100644 index 000000000..ce85c682e --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -0,0 +1,41 @@ +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBXML2_INCLUDE_DIR} + ${QT_INCLUDES}) + +FILE(GLOB SRC *.cpp *.h) + +SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_manager.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_spec.h) + +SET(OVQT_PLUG_TRANSLATION_MANAGER_HDR translation_manager_plugin.h + qnel_widget.h + simple_viewer.h + translation_manager_settings_page.h) + +SET(OVQT_PLUG_TRANSLATION_MANAGER_UIS translation_manager_settings_page.ui) + +SET(QT_USE_QTGUI TRUE) +SET(QT_USE_QTOPENGL TRUE) + +QT4_WRAP_CPP(OVQT_PLUG_TRANSLATION_MANAGER_MOC_SRC ${OVQT_PLUG_TRANSLATION_MANAGER_HDR}) +QT4_WRAP_UI(OVQT_PLUG_TRANSLATION_MANAGER_UI_HDRS ${OVQT_PLUG_TRANSLATION_MANAGER_UIS}) + +SOURCE_GROUP(QtResources FILES ${OVQT_PLUG_TRANSLATION_MANAGER_UIS}) +SOURCE_GROUP(QtGeneratedUiHdr FILES ${OVQT_PLUG_TRANSLATION_MANAGER_UI_HDRS}) +SOURCE_GROUP(QtGeneratedMocSrc FILES ${OVQT_PLUG_TRANSLATION_MANAGER_MOC_SRC}) +SOURCE_GROUP("Translation Manager Plugin" FILES ${SRC}) +SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) + +ADD_LIBRARY(ovqt_plugin_translation_manager MODULE ${SRC} ${OVQT_PLUG_TRANSLATION_MANAGER_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_TRANSLATION_MANAGER_UI_HDRS}) + +TARGET_LINK_LIBRARIES(ovqt_plugin_translation_manager ovqt_plugin_core nelmisc nel3d ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY}) + +NL_DEFAULT_PROPS(ovqt_plugin_translation_manager "NeL, Tools, 3D: Object Viewer Qt Plugin: Translation Manager") +NL_ADD_RUNTIME_FLAGS(ovqt_plugin_translation_manager) +NL_ADD_LIB_SUFFIX(ovqt_plugin_translation_manager) + +ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} -DQT_PLUGIN -DQT_SHARED ${QT_DEFINITIONS}) + +INSTALL(TARGETS ovqt_plugin_translation_manager LIBRARY DESTINATION lib RUNTIME DESTINATION bin ARCHIVE DESTINATION lib COMPONENT tools3d) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.cpp new file mode 100644 index 000000000..9a67abb80 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.cpp @@ -0,0 +1,197 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "qnel_widget.h" + +// STL includes + +// Qt includes +#include +#include + +// NeL includes +#include +#include +#include +#include + +namespace NLQT +{ + +QNLWidget::QNLWidget(QWidget *parent) + : QNeLWidget(parent), + m_driver(NULL), + m_initialized(false), + m_interval(25) +{ + setMouseTracking(true); + setFocusPolicy(Qt::StrongFocus); + + init(); +#ifdef Q_OS_LINUX + makeCurrent(); +#endif + m_mainTimer = new QTimer(this); + connect(m_mainTimer, SIGNAL(timeout()), this, SLOT(updateRender())); +} + +QNLWidget::~QNLWidget() +{ + release(); +} + +void QNLWidget::init() +{ + // create the driver + m_driver = NL3D::UDriver::createDriver(NULL, false, NULL); + nlassert(m_driver); + + // initialize the nel 3d viewport + m_driver->setDisplay((nlWindow)winId(), NL3D::UDriver::CMode(width(), height(), 32)); + + // set the cache size for the font manager(in bytes) + m_driver->setFontManagerMaxMemory(2097152); + + m_initialized = true; +} + +void QNLWidget::release() +{ + m_mainTimer->stop(); + delete m_mainTimer; + if (m_initialized) + { + m_driver->release(); + delete m_driver; + m_driver = NULL; + } +} + +void QNLWidget::setInterval(int msec) +{ + m_interval = msec; + m_mainTimer->setInterval(msec); +} + +void QNLWidget::setBackgroundColor(NLMISC::CRGBA backgroundColor) +{ + m_backgroundColor = backgroundColor; +} + +void QNLWidget::updateRender() +{ + if (isVisible()) + { + if (m_initialized) + m_driver->EventServer.pump(); + Q_EMIT updateData(); + + // Calc FPS + static sint64 lastTime = NLMISC::CTime::getPerformanceTime (); + sint64 newTime = NLMISC::CTime::getPerformanceTime (); + m_fps = float(1.0 / NLMISC::CTime::ticksToSecond (newTime-lastTime)); + lastTime = newTime; + + if (m_initialized && !m_driver->isLost()) + { + //_driver->activate(); + m_driver->clearBuffers(m_backgroundColor); + Q_EMIT updatePreRender(); + + Q_EMIT updatePostRender(); + // swap 3d buffers + m_driver->swapBuffers(); + } + } +} + +void QNLWidget::showEvent(QShowEvent *showEvent) +{ + QWidget::showEvent(showEvent); + m_driver->activate(); + m_mainTimer->start(m_interval); +} + +void QNLWidget::hideEvent(QHideEvent *hideEvent) +{ + m_mainTimer->stop(); + QWidget::hideEvent(hideEvent); +} + +#if defined(NL_OS_WINDOWS) + +typedef bool (*winProc)(NL3D::IDriver *driver, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); + +bool QNLWidget::winEvent(MSG *message, long *result) +{ + if (m_driver && m_driver->isActive()) + { + NL3D::IDriver *driver = dynamic_cast(m_driver)->getDriver(); + if (driver) + { + winProc proc = (winProc)driver->getWindowProc(); + return proc(driver, message->hwnd, message->message, message->wParam, message->lParam); + } + } + + return false; +} + +#elif defined(NL_OS_MAC) + +typedef bool (*cocoaProc)(NL3D::IDriver *, const void *e); + +bool QNLWidget::macEvent(EventHandlerCallRef caller, EventRef event) +{ + if(caller) + nlerror("You are using QtCarbon! Only QtCocoa supported, please upgrade Qt"); + + if (m_driver && m_driver->isActive()) + { + NL3D::IDriver *driver = dynamic_cast(m_driver)->getDriver(); + if (driver) + { + cocoaProc proc = (cocoaProc)driver->getWindowProc(); + return proc(driver, event); + } + } + + return false; +} + +#elif defined(NL_OS_UNIX) + +typedef bool (*x11Proc)(NL3D::IDriver *drv, XEvent *e); + +bool QNLWidget::x11Event(XEvent *event) +{ + if (m_driver && m_driver->isActive()) + { + NL3D::IDriver *driver = dynamic_cast(m_driver)->getDriver(); + if (driver) + { + x11Proc proc = (x11Proc)driver->getWindowProc(); + return proc(driver, event); + } + } + + return false; +} +#endif + +} /* namespace NLQT */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.h new file mode 100644 index 000000000..a54e6bb8a --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.h @@ -0,0 +1,130 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef QNEL_WIDGET_H +#define QNEL_WIDGET_H + +// NeL includes +#include +#include +#include + +// Qt includes +#include +#include + +class QAction; + +/* TODO every platform should use QWidget */ +#if defined(NL_OS_WINDOWS) +typedef QWidget QNeLWidget; +#elif defined(NL_OS_MAC) +typedef QWidget QNeLWidget; +#elif defined(NL_OS_UNIX) +typedef QGLWidget QNeLWidget; +#endif // NL_OS_UNIX + +namespace NL3D +{ +class UDriver; +class UScene; +} + +namespace NLQT +{ + +/** +@class QNLWidget +@brief Responsible for interaction between Qt and NeL. +@details Automatically begins to update the render if the widget is visible +or suspends the updating of render if the widget is hidden. +*/ +class QNLWidget : public QNeLWidget +{ + Q_OBJECT + +public: + QNLWidget(QWidget *parent); + virtual ~QNLWidget(); + + /// Set the update interval renderer + void setInterval(int msec); + + /// Set the background color. + void setBackgroundColor(NLMISC::CRGBA backgroundColor); + + float fps() const + { + return m_fps; + } + + inline NLMISC::CRGBA backgroundColor() const + { + return m_backgroundColor; + } + + NL3D::UDriver *driver() const + { + return m_driver; + } + + virtual QPaintEngine* paintEngine() const + { + return NULL; + } +Q_SIGNALS: + void updateData(); + void updatePreRender(); + void updatePostRender(); + +private Q_SLOTS: + void updateRender(); + +protected: + virtual void showEvent(QShowEvent *showEvent); + virtual void hideEvent(QHideEvent *hideEvent); + +#if defined(NL_OS_WINDOWS) + virtual bool winEvent(MSG *message, long *result); +#elif defined(NL_OS_MAC) + virtual bool macEvent(EventHandlerCallRef caller, EventRef event); +#elif defined(NL_OS_UNIX) + virtual bool x11Event(XEvent *event); +#endif + +private: + void init(); + void release(); + + QNLWidget(const QNLWidget &); + QNLWidget &operator=(const QNLWidget &); + + NL3D::UDriver *m_driver; + NLMISC::CRGBA m_backgroundColor; + + QTimer *m_mainTimer; + + bool m_initialized; + int m_interval; + float m_fps; + +}; /* class QNLWidget */ + +} /* namespace NLQT */ + + +#endif // QNEL_WIDGET_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp new file mode 100644 index 000000000..e128710c4 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp @@ -0,0 +1,54 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "simple_viewer.h" + +// Qt includes +#include +#include +#include + +// NeL includes + +// Project includes + +namespace Plugin +{ + +CSimpleViewer::CSimpleViewer(QWidget *parent) + : QWidget(parent) +{ + QGridLayout *gridLayout = new QGridLayout(this); + gridLayout->setObjectName(QString::fromUtf8("gridLayoutSimpleViewer")); + gridLayout->setContentsMargins(0, 0, 0, 0); + NLQT::QNLWidget *_nelWidget = new NLQT::QNLWidget(this); + gridLayout->addWidget(_nelWidget, 0, 0, 1, 1); +} + +bool CCoreListener::closeMainWindow() const +{ + int ret = QMessageBox::question(0, tr("Example close event hook"), + tr("Do you want to close window?"), + QMessageBox::Yes | QMessageBox::No); + + if (ret == QMessageBox::Yes) + return true; + else + return false; +} + +} /* namespace Plugin */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h new file mode 100644 index 000000000..bbff7e9e0 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h @@ -0,0 +1,54 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#ifndef SIMPLE_VIEWER_H +#define SIMPLE_VIEWER_H + +// Project includes +#include "qnel_widget.h" +#include "../core/icore_listener.h" + +// Qt includes +#include + +class QWidget; + +namespace Plugin +{ + +class CSimpleViewer : public QWidget +{ + Q_OBJECT +public: + CSimpleViewer(QWidget *parent = 0); + virtual ~CSimpleViewer() {} +}; + +class CCoreListener : public Core::ICoreListener +{ + Q_OBJECT +public: + CCoreListener(QObject *parent = 0): ICoreListener(parent) {} + virtual ~CCoreListener() {} + + virtual bool closeMainWindow() const; +}; + +} // namespace Plugin + +#endif // SIMPLE_VIEWER_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp new file mode 100644 index 000000000..6140a3985 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -0,0 +1,122 @@ +// Project includes +#include "translation_manager_plugin.h" +#include "translation_manager_settings_page.h" +#include "simple_viewer.h" +// Project system includes +#include "../core/icore.h" +#include "../core/core_constants.h" +#include "../core/imenu_manager.h" +#include "../../extension_system/iplugin_spec.h" + +// NeL includes +#include "nel/misc/debug.h" + +// Qt includes +#include +#include +#include +#include +#include +#include + +namespace Plugin +{ +TranslationManagerPlugin::~TranslationManagerPlugin() +{ + Q_FOREACH(QObject *obj, _autoReleaseObjects) + { + _plugMan->removeObject(obj); + } + qDeleteAll(_autoReleaseObjects); + _autoReleaseObjects.clear(); +} + +bool TranslationManagerPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) +{ + Q_UNUSED(errorString); + _plugMan = pluginManager; + + addAutoReleasedObject(new CTranslationManagerSettingsPage(this)); + addAutoReleasedObject(new CTranslationManagerContext(this)); + addAutoReleasedObject(new CCoreListener(this)); + return true; +} + +void TranslationManagerPlugin::extensionsInitialized() +{ + Core::ICore *core = Core::ICore::instance(); + Core::IMenuManager *menuManager = core->menuManager(); + //menuManager = _plugMan->getObject(); + // Menu Actions for plugin + QAction *aboutTManPlugin = new QAction("Translation Manager", this); + // Locations + QMenu *helpMenu = menuManager->menu(Core::Constants::M_HELP); + QAction *aboutQtAction = menuManager->action(Core::Constants::ABOUT_QT); + helpMenu->addSeparator(); + helpMenu->insertAction(aboutQtAction, aboutTManPlugin); + menuManager->menuBar()->addMenu("Translation Manager"); +} + +void TranslationManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) +{ +#ifdef NL_OS_WINDOWS + // Ensure that a context doesn't exist yet. + // This only applies to platforms without PIC, e.g. Windows. + nlassert(!NLMISC::INelContext::isContextInitialised()); +#endif // NL_OS_WINDOWS + _LibContext = new NLMISC::CLibraryContext(*nelContext); +} + +QString TranslationManagerPlugin::name() const +{ + return "Translation Manager"; +} + +QString TranslationManagerPlugin::version() const +{ + return "0.1"; +} + +QString TranslationManagerPlugin::vendor() const +{ + return "cemycc"; +} + +QString TranslationManagerPlugin::description() const +{ + return "OVQT plugin for translation files."; +} + +QStringList TranslationManagerPlugin::dependencies() const +{ + QStringList list; + list.append(Core::Constants::OVQT_CORE_PLUGIN); + list.append("ObjectViewer"); + return list; +} + +void TranslationManagerPlugin::addAutoReleasedObject(QObject *obj) +{ + _plugMan->addObject(obj); + _autoReleaseObjects.prepend(obj); +} + +QObject* TranslationManagerPlugin::objectByName(const QString &name) const +{ + Q_FOREACH (QObject *qobj, _plugMan->allObjects()) + if (qobj->objectName() == name) + return qobj; + return 0; +} + +ExtensionSystem::IPluginSpec *TranslationManagerPlugin::pluginByName(const QString &name) const +{ + Q_FOREACH (ExtensionSystem::IPluginSpec *spec, _plugMan->plugins()) + if (spec->name() == name) + return spec; + return 0; +} + +} + +Q_EXPORT_PLUGIN(Plugin::TranslationManagerPlugin) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h new file mode 100644 index 000000000..c1bfc2548 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -0,0 +1,94 @@ +#ifndef TRANSLATION_MANAGER_PLUGIN_H +#define TRANSLATION_MANAGER_PLUGIN_H + +// Project includes +#include "../../extension_system/iplugin.h" +#include "../core/icontext.h" +#include "simple_viewer.h" + +// NeL includes +#include "nel/misc/app_context.h" + +// Qt includes +#include +#include + +namespace NLMISC +{ +class CLibraryContext; +} + +namespace ExtensionSystem +{ +class IPluginSpec; +} + +namespace Plugin +{ + +class TranslationManagerPlugin : public QObject, public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_INTERFACES(ExtensionSystem::IPlugin) +public: + + virtual ~TranslationManagerPlugin(); + + bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); + void extensionsInitialized(); + + void setNelContext(NLMISC::INelContext *nelContext); + + QString name() const; + QString version() const; + QString vendor() const; + QString description() const; + QStringList dependencies() const; + + void addAutoReleasedObject(QObject *obj); + + QObject *objectByName(const QString &name) const; + ExtensionSystem::IPluginSpec *pluginByName(const QString &name) const; + +protected: + NLMISC::CLibraryContext *_LibContext; + +private: + ExtensionSystem::IPluginManager *_plugMan; + QList _autoReleaseObjects; +}; + +class CTranslationManagerContext: public Core::IContext +{ + Q_OBJECT +public: + CTranslationManagerContext(QObject *parent = 0): IContext(parent) + { + m_simpleViewer = new CSimpleViewer(); + } + + virtual ~CTranslationManagerContext() {} + + virtual QString id() const + { + return QLatin1String("TranslationManagerContext"); + } + virtual QString trName() const + { + return tr("Translation Manager"); + } + virtual QIcon icon() const + { + return QIcon(); + } + virtual QWidget *widget() + { + return m_simpleViewer; + } + + CSimpleViewer *m_simpleViewer; +}; + +} // namespace Plugin + +#endif // TRANSLATION_MANAGER_PLUGIN_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp new file mode 100644 index 000000000..c3e4883d4 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp @@ -0,0 +1,67 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "translation_manager_settings_page.h" + +// Qt includes +#include + +// NeL includes + +// Project includes + +namespace Plugin +{ + +CTranslationManagerSettingsPage::CTranslationManagerSettingsPage(QObject *parent) + : IOptionsPage(parent), + _currentPage(NULL) +{ +} + +QString CTranslationManagerSettingsPage::id() const +{ + return QLatin1String("TranslationManagerPage"); +} + +QString CTranslationManagerSettingsPage::trName() const +{ + return tr("Translation Manager page"); +} + +QString CTranslationManagerSettingsPage::category() const +{ + return QLatin1String("General"); +} + +QString CTranslationManagerSettingsPage::trCategory() const +{ + return tr("General"); +} + +QWidget *CTranslationManagerSettingsPage::createPage(QWidget *parent) +{ + _currentPage = new QWidget(parent); + _ui.setupUi(_currentPage); + return _currentPage; +} + +void CTranslationManagerSettingsPage::apply() +{ +} + +} /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h new file mode 100644 index 000000000..53f9068e1 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h @@ -0,0 +1,58 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#ifndef TRANSLATION_MANAGER_SETTINGS_PAGE_H +#define TRANSLATION_MANAGER_SETTINGS_PAGE_H + +#include + +#include "../core/ioptions_page.h" + +#include "ui_translation_manager_settings_page.h" + +class QWidget; + +namespace Plugin +{ +/** +@class CTranslationManagerSettingsPage +*/ +class CTranslationManagerSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT +public: + CTranslationManagerSettingsPage(QObject *parent = 0); + virtual ~CTranslationManagerSettingsPage() {} + + virtual QString id() const; + virtual QString trName() const; + virtual QString category() const; + virtual QString trCategory() const; + virtual QWidget *createPage(QWidget *parent); + + virtual void apply(); + virtual void finish() {} + +private: + QWidget *_currentPage; + Ui::CTranslationManagerSettingsPage _ui; +}; + +} // namespace Plugin + +#endif // TRANSLATION_MANAGER_SETTINGS_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui new file mode 100644 index 000000000..951be0615 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui @@ -0,0 +1,92 @@ + + + CTranslationManagerSettingsPage + + + + 0 + 0 + 458 + 479 + + + + Form + + + + 0 + + + + + GroupBox + + + + + + PushButton + + + + + + + + + + PushButton + + + + + + + RadioButton + + + + + + + RadioButton + + + + + + + CheckBox + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + + From 0371b04509d46fb237373dcadc8fb1c57dfdd426 Mon Sep 17 00:00:00 2001 From: cemycc Date: Mon, 30 May 2011 20:56:22 +0300 Subject: [PATCH 006/215] Changed: #1307 Added extract bot names and UI dialog for plugin settings. --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/CMakeLists.txt | 2 +- .../translation_manager/extract_bot_names.cpp | 756 ++++++++++++++++++ .../translation_manager_plugin.cpp | 47 +- .../translation_manager_plugin.h | 8 + .../translation_manager_settings_page.cpp | 243 ++++++ .../translation_manager_settings_page.h | 16 +- .../translation_manager_settings_page.ui | 485 ++++++++--- 7 files changed, 1462 insertions(+), 95 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt index ce85c682e..827db7487 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -30,7 +30,7 @@ SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) ADD_LIBRARY(ovqt_plugin_translation_manager MODULE ${SRC} ${OVQT_PLUG_TRANSLATION_MANAGER_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_TRANSLATION_MANAGER_UI_HDRS}) -TARGET_LINK_LIBRARIES(ovqt_plugin_translation_manager ovqt_plugin_core nelmisc nel3d ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY}) +TARGET_LINK_LIBRARIES(ovqt_plugin_translation_manager ovqt_plugin_core nelmisc nel3d nelligo nelgeorges ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY}) NL_DEFAULT_PROPS(ovqt_plugin_translation_manager "NeL, Tools, 3D: Object Viewer Qt Plugin: Translation Manager") NL_ADD_RUNTIME_FLAGS(ovqt_plugin_translation_manager) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp new file mode 100644 index 000000000..75a97d3bb --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp @@ -0,0 +1,756 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "nel/misc/types_nl.h" +#include "nel/misc/config_file.h" +#include "nel/misc/sheet_id.h" +#include "nel/misc/path.h" +#include "nel/misc/diff_tool.h" +#include "nel/georges/u_form.h" +#include "nel/georges/u_form_elm.h" +#include "nel/georges/load_form.h" +#include "nel/ligo/ligo_config.h" +#include "nel/ligo/primitive.h" +#include "nel/ligo/primitive_utils.h" + +using namespace std; +using namespace NLMISC; +using namespace NLLIGO; +using namespace STRING_MANAGER; + +vector Filters; + +static CLigoConfig LigoConfig; +static bool RemoveOlds = false; + +struct TCreatureInfo +{ + CSheetId SheetId; + bool ForceSheetName; + bool DisplayName; + + + void readGeorges (const NLMISC::CSmartPtr &form, const NLMISC::CSheetId &sheetId) + { + const NLGEORGES::UFormElm &item=form->getRootNode(); + + SheetId=sheetId; + item.getValueByName(ForceSheetName, "3d data.ForceDisplayCreatureName"); + item.getValueByName(DisplayName, "3d data.DisplayName"); + } + + void serial(NLMISC::IStream &f) + { + f.serial(SheetId); + f.serial(ForceSheetName); + f.serial(DisplayName); + } + + + static uint getVersion () + { + return 1; + } + + void removed() + { + } + +}; + +std::map Creatures; + +TCreatureInfo *getCreature(const std::string &sheetName) +{ + CSheetId id(sheetName+".creature"); + + if (Creatures.find(id) != Creatures.end()) + return &(Creatures.find(id)->second); + else + return NULL; +} + +string cleanupName(const std::string &name) +{ + string ret; + + for (uint i=0; i= 2) + { + if ( *ret.begin() == ucchar('$')) + { + ret=ret.substr(1); + } + if ( *ret.rbegin() == ucchar('$')) + { + ret = ret.substr(0, ret.size()-1); + } + } + ret = cleanupUcName(ret); + return ret; +} + +struct TEntryInfo +{ + string SheetName; +}; + +set GenericNames; +map SimpleNames; +set Functions; + + +string removeAndStoreFunction(const std::string &fullName) +{ + string::size_type pos = fullName.find("$"); + if (pos == string::npos) + return fullName; + else + { + // extract and store the function name + string ret; + + ret = fullName.substr(0, pos); + string::size_type pos2 = fullName.find("$", pos+1); + + string fct = fullName.substr(pos+1, pos2-(pos+1)); + + ret += fullName.substr(pos2+1); + + if (Functions.find(fct) == Functions.end()) + { + nldebug("Adding function '%s'", fct.c_str()); + Functions.insert(fct); + } + + return ret; + } +} + + +void addGenericName(const std::string &name, const std::string &sheetName) +{ + TCreatureInfo *c = getCreature(sheetName); + if (!c || c->ForceSheetName || !c->DisplayName) + return; + + if (SimpleNames.find(name) != SimpleNames.end()) + { + nldebug("Name '%s' is now a generic name", name.c_str()); + GenericNames.insert(name); + SimpleNames.erase(name); + + } + else if (GenericNames.find(name) == GenericNames.end()) + { + nldebug("Adding generic name '%s'", name.c_str()); + GenericNames.insert(name); + } +} + +void addSimpleName(const std::string &name, const std::string &sheetName) +{ + TCreatureInfo *c = getCreature(sheetName); + if (!c || c->ForceSheetName || !c->DisplayName) + return; + + if (SimpleNames.find(name) != SimpleNames.end()) + { + addGenericName(name, sheetName); + } + else if (GenericNames.find(name) != GenericNames.end()) + { + return; + } + else + { + nldebug("Adding simple name '%s'", name.c_str()); + + TEntryInfo ei; + ei.SheetName = sheetName; + + SimpleNames.insert(make_pair(name, ei)); + } +} + +int extractBotNamesAll(map > config_paths, string ligo_class_file, string trans_path, string work_path) +{ + //------------------------------------------------------------------- + // read the parameters + /*for (int i=2; i::iterator it = config_paths["paths"].begin(); it != config_paths["paths"].end(); ++it) + { + CPath::addSearchPath(*it, true, false); + } + for (std::list::iterator it = config_paths["pathsR"].begin(); it != config_paths["pathsR"].end(); ++it) + { + CPath::addSearchPath(*it, false, false); + } + + for (std::list::iterator it = config_paths["filters"].begin(); it != config_paths["filters"].end(); ++it) + { + Filters.push_back(*it); + } + + + //------------------------------------------------------------------- + // init the sheets + CSheetId::init(false); + const string PACKED_SHEETS_NAME = "bin/translation_tools_creature.packed_sheets"; + loadForm("creature", PACKED_SHEETS_NAME, Creatures, false, false); + + if (Creatures.empty()) + { + for (std::list::iterator it = config_paths["georges"].begin(); it != config_paths["georges"].end(); ++it) + CPath::addSearchPath((*it).c_str(), true, false); + + loadForm("creature", PACKED_SHEETS_NAME, Creatures, true); + } + + + //------------------------------------------------------------------- + // init ligo config + string ligoPath = CPath::lookup(ligo_class_file, true, true); + LigoConfig.readPrimitiveClass(ligoPath.c_str(), false); + NLLIGO::Register(); + + CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; + + //------------------------------------------------------------------- + // ok, ready for the real work, + // first, read the primitives files and parse the primitives + vector files; + CPath::getFileList("primitive", files); + + for (uint i=0; i ps; + ps.buildSet(primDoc.RootNode, pred, result); + + for (uint i=0; igetPropertyByName("name", name); + result[i]->getPropertyByName("count", countStr); + result[i]->getPropertyByName("bot_sheet_look", sheetStr); + + uint32 count; + NLMISC::fromString(countStr, count); + + if (count != 0) + { + if (sheetStr.empty()) + { + nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str()); + } + else + { + addGenericName(removeAndStoreFunction(name), sheetStr); + } + } + } + } + // look for bot template + { + TPrimitiveClassPredicate pred("bot_template_npc"); + TPrimitiveSet result; + + CPrimitiveSet ps; + ps.buildSet(primDoc.RootNode, pred, result); + + for (uint i=0; igetPropertyByName("name", name); + result[i]->getPropertyByName("sheet_look", sheetStr); + + if (sheetStr.empty()) + { + // take the sheet in the parent + result[i]->getParent()->getPropertyByName("bot_sheet_look", sheetStr); + } + + if (sheetStr.empty()) + { + nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str()); + } + else + { + addGenericName(removeAndStoreFunction(name), sheetStr); + } + } + } + // look for npc_group + { + TPrimitiveClassPredicate pred("npc_group"); + TPrimitiveSet result; + + CPrimitiveSet ps; + ps.buildSet(primDoc.RootNode, pred, result); + + for (uint i=0; igetPropertyByName("name", name); + result[i]->getPropertyByName("count", countStr); + result[i]->getPropertyByName("bot_sheet_client", sheetStr); + + uint32 count; + NLMISC::fromString(countStr, count); + + if (count > 0 && sheetStr.empty()) + { + nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str()); + } + else + { + if (count == 1) + { + addSimpleName(removeAndStoreFunction(name), sheetStr); + } + else if (count > 1) + { + addGenericName(removeAndStoreFunction(name), sheetStr); + } + } + } + } + // look for bot + { + TPrimitiveClassPredicate pred("npc_bot"); + TPrimitiveSet result; + + CPrimitiveSet ps; + ps.buildSet(primDoc.RootNode, pred, result); + + for (uint i=0; igetPropertyByName("name", name); + result[i]->getPropertyByName("sheet_client", sheetStr); + + if (sheetStr.empty()) + { + // take the sheet in the parent + result[i]->getParent()->getPropertyByName("bot_sheet_client", sheetStr); + } + + if (sheetStr.empty()) + { + nlwarning("In '%s', empty sheet !", buildPrimPath(result[i]).c_str()); + } + else + { + TEntryInfo ei; + addSimpleName(removeAndStoreFunction(name), sheetStr); + } + } + } + } + + //------------------------------------------------------------------- + // step 2 : load the reference file + + nlinfo("Looking for missing translation:"); + + TWorksheet botNames; + loadExcelSheet(work_path, botNames, true); + TWorksheet transBotNames; + loadExcelSheet(trans_path, transBotNames, true); + + TWorksheet fcts; + loadExcelSheet(work_path, fcts, true); + + + // add missing element + + uint nbAddSimpleName = 0; + uint nbAddFunction = 0; + uint nbAddGenericName = 0; + + uint botIdCol; + nlverify(botNames.findId(botIdCol)); + uint transIdCol; + nlverify(transBotNames.findId(transIdCol)); + uint fctsIdCol; + nlverify(fcts.findId(fctsIdCol)); + + // special treatment to add the sheet_name col + { + uint sheetCol; + if (!botNames.findCol(ucstring("sheet_name"), sheetCol)) + { + botNames.insertColumn(botNames.ColCount); + botNames.setData(0, botNames.ColCount-1, ucstring("sheet_name")); + } + + if (!transBotNames.findCol(ucstring("sheet_name"), sheetCol)) + { + transBotNames.insertColumn(transBotNames.ColCount); + transBotNames.setData(0, transBotNames.ColCount-1, ucstring("sheet_name")); + } + } + // 1 - simple names + { + nlinfo(" Simple names..."); + + + map::iterator first(SimpleNames.begin()), last(SimpleNames.end()); + for (; first != last; ++first) + { + uint rowIdx; + if (!botNames.findRow(botIdCol, first->first, rowIdx)) + { + // we need to add the entry + rowIdx = botNames.size(); + botNames.resize(botNames.size()+1); + + botNames.setData(rowIdx, ucstring("bot name"), first->first); + botNames.setData(rowIdx, ucstring("translated name"), first->first); + botNames.setData(rowIdx, ucstring("sheet_name"), first->second.SheetName); + + nbAddSimpleName++; + } + else + { + // set/update the sheet name info + // try to restore the existing translation + uint transRowIdx; + if (transBotNames.findRow(transIdCol, first->first, transRowIdx)) + { + ucstring wkBotName = botNames.getData(rowIdx, ucstring("bot name")); + ucstring wkSheetName = botNames.getData(rowIdx, ucstring("sheet_name")); + ucstring wkTranslationName = botNames.getData(rowIdx, ucstring("translated name")); + ucstring ucWkHash; + uint64 hash = CI18N::makeHash(wkBotName + wkTranslationName +wkSheetName); + CI18N::hashToUCString(hash, ucWkHash); + ucstring trUcHash = transBotNames[transRowIdx][0]; + bool isWkTranslationNameAGroupName = wkTranslationName.find(ucstring("$")) != ucstring::npos; + bool hashIsValide = std::equal(ucWkHash.begin(), ucWkHash.end(), trUcHash.begin()+1); + // Hash is equal get the translation + if (hashIsValide && !isWkTranslationNameAGroupName) + { + wkTranslationName = transBotNames.getData(transRowIdx, ucstring("translated name")); + wkSheetName = transBotNames.getData(transRowIdx, ucstring("sheet_name")); + botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); + botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); + hash = CI18N::makeHash(wkBotName + wkTranslationName + wkSheetName); + // update the hash code + CI18N::hashToUCString(hash, transBotNames[transRowIdx][0]); + } + // bots_name.txt has been manually changed. We trust what the Level Designer has done. We don't destroy is work. + // or it is a simple + else + { + //use the "translated name" of the manually changed work/bot_name.txt + botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); + botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); + } + } + } + } + } + + // 2 - generic names + + { + nlinfo(" Generic names..."); + + set::iterator first(GenericNames.begin()), last(GenericNames.end()); + for (; first != last; ++first) + { + string gnName = "gn_" + cleanupName(*first); + + ucstring fctsTitleId; + ucstring fctsName; + // add or modify the bot names + uint rowIdx; + if (!botNames.findRow(botIdCol, *first, rowIdx)) + { + // we need to add the entry + rowIdx = botNames.size(); + botNames.resize(botNames.size()+1); + + botNames.setData(rowIdx, ucstring("bot name"), *first); + botNames.setData(rowIdx, ucstring("translated name"), ucstring("$") + gnName + "$"); + botNames.setData(rowIdx, ucstring("sheet_name"), ucstring()); + fctsTitleId = gnName; + fctsName = *first; + + nbAddSimpleName++; + } + else + { + // look in the translated table to remember the translated name to write it in the string file + ucstring wkBotName = botNames.getData(rowIdx, ucstring("bot name")); + ucstring wkTranslationName = botNames.getData(rowIdx, ucstring("translated name")); + ucstring wkSheetName = botNames.getData(rowIdx, ucstring("sheet_name")); + + + nlinfo("Bot name:%s\n",wkBotName.toString().c_str()); + bool isWkTranslationNameAGroupName = wkTranslationName.find(ucstring("$")) != ucstring::npos; + + if ( isWkTranslationNameAGroupName ) //work name looks like "$gn_***$: do not modify + { + + //Do not change work/bot_name.txt + // update work/world_title.txt + + ucstring transName; + fctsTitleId = makeGroupName(wkTranslationName); + uint transRowIdx; + if (transBotNames.findRow(transIdCol, *first, transRowIdx)) + { + transName = transBotNames.getData(transRowIdx, ucstring("translated name")); + + if (transName.find(ucstring("$")) != ucstring::npos) + { + transName = fctsTitleId; + } + } + else + { + transName = fctsTitleId; + } + //Do not touch anything + botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); + botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); + // fctsTitleId = makeGroupName(wkTranslationName); + fctsName = transName; + + } + else // WkTranslationName != "$gn*$" + { + uint transRowIdx; + ucstring transName; + ucstring wkSheetName; + // Get the translation as a simple name. + if (transBotNames.findRow(transIdCol, *first, transRowIdx)) + { + + transName = transBotNames.getData(transRowIdx, ucstring("translated name")); + ucstring trSheetName = transBotNames.getData(transRowIdx, ucstring("sheet_name")); + + //tr."translation name" is + if (transName.find(ucstring("$")) != ucstring::npos) + { + //get Translation, update hash + botNames[rowIdx][1] = transName; + botNames[rowIdx][2] = trSheetName; + fctsTitleId = makeGroupName(transName); + fctsName = makeGroupName(transName); + ucstring trNewUcHash; + uint64 hash = CI18N::makeHash(wkBotName + transName +trSheetName); + CI18N::hashToUCString(hash, trNewUcHash); + transBotNames[transRowIdx][0] = ucstring("_") + trNewUcHash; + } + else //botNames."translated name" != $gn_$ && tansName."translated name" != $gn_$ + { + + // get the translation back + //update work/bot_name.txt + wkTranslationName = ucstring("$")+gnName+"$"; + botNames[rowIdx][0] = wkBotName; + botNames[rowIdx][1] = wkTranslationName; + botNames[rowIdx][2] = wkSheetName; + + //update translated/bot_name.txt + + fctsName = transName; //transName + fctsTitleId = gnName; + ucstring trNewUcHash; + uint64 hash = CI18N::makeHash(botNames[rowIdx][0] + botNames[rowIdx][1] +botNames[rowIdx][2]); + CI18N::hashToUCString(hash, trNewUcHash); + transBotNames[transRowIdx][0] = ucstring("_") + trNewUcHash; + } + + } + else //There is no translation yet + { + fctsName = wkTranslationName; + wkTranslationName = ucstring("$")+gnName+"$"; + botNames[rowIdx][0] = wkBotName; + botNames[rowIdx][1] = wkTranslationName; + botNames[rowIdx][2] = wkSheetName; + fctsTitleId = gnName; + + + } + } + + } + + + // look for a corresponding entry + uint gnNameRow; + + + if (!fcts.findRow(fctsIdCol, fctsTitleId, gnNameRow)) + { + + // not found, add it + gnNameRow = fcts.size(); + fcts.resize(fcts.size()+1); + fcts.setData(gnNameRow, ucstring("title_id"), fctsTitleId); + fcts.setData(gnNameRow, ucstring("name"), fctsName); + nbAddGenericName++; + + } + else //Update + { + + } + } + } + + + // 3 - functions + { + nlinfo(" Functions..."); + + set::iterator first(Functions.begin()), last(Functions.end()); + for (; first != last; ++first) + { + string fctName = *first; + // look for a corresponding entry + uint functionRow; + if (!fcts.findRow(fctsIdCol, fctName, functionRow)) + { + // not found, add it + functionRow = fcts.size(); + fcts.resize(fcts.size()+1); + + fcts.setData(functionRow, ucstring("title_id"), fctName); + fcts.setData(functionRow, ucstring("name"), *first); + + nbAddFunction++; + } + } + } + + // display resum\E9 + nlinfo("Adding %u new simple name", nbAddSimpleName); + nlinfo("Adding %u new generic name", nbAddGenericName); + nlinfo("Adding %u new function name", nbAddFunction); + + // saving the modified files + + ucstring s = prepareExcelSheet(botNames); + CI18N::writeTextFile(work_path, s, false); + s = prepareExcelSheet(transBotNames); + CI18N::writeTextFile(trans_path, s, false); + s = prepareExcelSheet(fcts); + CI18N::writeTextFile(work_path, s, false); + + return 0; +} + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index 6140a3985..233780ca5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -19,6 +19,8 @@ #include #include +int extractBotNamesAll(map > config_paths, string ligo_class_file, string trans_path, string work_path); + namespace Plugin { TranslationManagerPlugin::~TranslationManagerPlugin() @@ -54,7 +56,39 @@ void TranslationManagerPlugin::extensionsInitialized() QAction *aboutQtAction = menuManager->action(Core::Constants::ABOUT_QT); helpMenu->addSeparator(); helpMenu->insertAction(aboutQtAction, aboutTManPlugin); - menuManager->menuBar()->addMenu("Translation Manager"); + QMenu *transMenu = menuManager->menuBar()->addMenu("Translation Manager"); + /* Words extraction*/ + QAction *botnamesAct = new QAction("Extract bot_names", this); + connect(botnamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); + transMenu->addAction(botnamesAct); +} + +void TranslationManagerPlugin::extractBotNames() +{ + // prepare the config paths + list paths,pathsR, georges, filters, languages; + string ligo, translation, work; + map > config_paths; + + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("translationmanager"); + + paths = ConvertQStringList(settings->value("paths").toStringList()); /* paths */ + config_paths["paths"] = paths; + pathsR = ConvertQStringList(settings->value("pathsR").toStringList()); /* pathsR */ + config_paths["pathsR"] = pathsR; + georges = ConvertQStringList(settings->value("georges").toStringList()); /* georges */ + config_paths["georges"] = georges; + filters = ConvertQStringList(settings->value("filters").toStringList()); /* filters */ + config_paths["filters"] = filters; + languages = ConvertQStringList(settings->value("languages").toStringList()); /* languages */ + ligo = settings->value("ligo").toString().toStdString(); + translation = settings->value("translation").toString().toStdString(); + work = settings->value("work").toString().toStdString(); + settings->endGroup(); + + extractBotNamesAll(config_paths, ligo, translation, work); + } void TranslationManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) @@ -67,6 +101,17 @@ void TranslationManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) _LibContext = new NLMISC::CLibraryContext(*nelContext); } +list TranslationManagerPlugin::ConvertQStringList(QStringList listq) +{ + std::list stdlist; + Q_FOREACH(QString text, listq) + { + stdlist.push_back(text.toStdString()); + } + + return stdlist; +} + QString TranslationManagerPlugin::name() const { return "Translation Manager"; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h index c1bfc2548..07bf0d434 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -13,6 +13,8 @@ #include #include +using namespace std; + namespace NLMISC { class CLibraryContext; @@ -56,6 +58,11 @@ protected: private: ExtensionSystem::IPluginManager *_plugMan; QList _autoReleaseObjects; + list ConvertQStringList(QStringList list); + + +private Q_SLOTS: + void extractBotNames(); }; class CTranslationManagerContext: public Core::IContext @@ -87,6 +94,7 @@ public: } CSimpleViewer *m_simpleViewer; + }; } // namespace Plugin diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp index c3e4883d4..b3a731ae5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp @@ -18,15 +18,21 @@ #include "translation_manager_settings_page.h" // Qt includes +#include #include +#include +#include // NeL includes // Project includes +#include "../core/icore.h" namespace Plugin { +QString lastDir = "."; + CTranslationManagerSettingsPage::CTranslationManagerSettingsPage(QObject *parent) : IOptionsPage(parent), _currentPage(NULL) @@ -57,11 +63,248 @@ QWidget *CTranslationManagerSettingsPage::createPage(QWidget *parent) { _currentPage = new QWidget(parent); _ui.setupUi(_currentPage); + readSettings(); + connect(_ui.paths_add, SIGNAL(clicked()), this, SLOT(pathAdd())); + connect(_ui.paths_del, SIGNAL(clicked()), this, SLOT(pathDel())); + connect(_ui.pathsR_add, SIGNAL(clicked()), this, SLOT(pathRAdd())); + connect(_ui.pathsR_del, SIGNAL(clicked()), this, SLOT(pathRDel())); + connect(_ui.georges_add, SIGNAL(clicked()), this, SLOT(georgeAdd())); + connect(_ui.georges_del, SIGNAL(clicked()), this, SLOT(georgeDel())); + connect(_ui.filter_add, SIGNAL(clicked()), this, SLOT(filterAdd())); + connect(_ui.filter_del, SIGNAL(clicked()), this, SLOT(filterDel())); + connect(_ui.lang_add, SIGNAL(clicked()), this, SLOT(languageAdd())); + connect(_ui.lang_del, SIGNAL(clicked()), this, SLOT(languageDel())); + connect(_ui.translation_add, SIGNAL(clicked()), this, SLOT(translationAdd())); + connect(_ui.work_add, SIGNAL(clicked()), this, SLOT(workAdd())); + return _currentPage; } +void CTranslationManagerSettingsPage::pathAdd() +{ + QString newPath = QFileDialog::getExistingDirectory(_currentPage, "", lastDir); + if (!newPath.isEmpty()) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(newPath); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.paths_list->addItem(newItem); + lastDir = newPath; + } +} + +void CTranslationManagerSettingsPage::pathDel() +{ + QListWidgetItem *removeItem = _ui.paths_list->takeItem(_ui.paths_list->currentRow()); + if (!removeItem) + delete removeItem; +} + +void CTranslationManagerSettingsPage::pathRAdd() +{ + QString newPath = QFileDialog::getExistingDirectory(_currentPage, "", lastDir); + if (!newPath.isEmpty()) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(newPath); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.pathsR_list->addItem(newItem); + lastDir = newPath; + } +} + +void CTranslationManagerSettingsPage::pathRDel() +{ + QListWidgetItem *removeItem = _ui.pathsR_list->takeItem(_ui.pathsR_list->currentRow()); + if (!removeItem) + delete removeItem; +} + +void CTranslationManagerSettingsPage::georgeAdd() +{ + QString newPath = QFileDialog::getExistingDirectory(_currentPage, "", lastDir); + if (!newPath.isEmpty()) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(newPath); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.georges_list->addItem(newItem); + lastDir = newPath; + } +} + +void CTranslationManagerSettingsPage::georgeDel() +{ + QListWidgetItem *removeItem = _ui.georges_list->takeItem(_ui.georges_list->currentRow()); + if (!removeItem) + delete removeItem; +} + +void CTranslationManagerSettingsPage::filterAdd() +{ + QString newValue = _ui.filter_edit->text(); + if (!newValue.isEmpty()) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(newValue); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.filter_list->addItem(newItem); + } +} + +void CTranslationManagerSettingsPage::filterDel() +{ + QListWidgetItem *removeItem = _ui.filter_list->takeItem(_ui.filter_list->currentRow()); + if (!removeItem) + delete removeItem; +} + +void CTranslationManagerSettingsPage::languageAdd() +{ + QString newValue = _ui.lang_edit->text(); + if (!newValue.isEmpty()) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(newValue); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.lang_list->addItem(newItem); + } +} + +void CTranslationManagerSettingsPage::languageDel() +{ + QListWidgetItem *removeItem = _ui.lang_list->takeItem(_ui.lang_list->currentRow()); + if (!removeItem) + delete removeItem; +} + +void CTranslationManagerSettingsPage::translationAdd() +{ + QString newPath = QFileDialog::getExistingDirectory(_currentPage, ""); + if (!newPath.isEmpty()) + { + _ui.translation_edit->setText(newPath); + } +} + +void CTranslationManagerSettingsPage::workAdd() +{ + QString newPath = QFileDialog::getExistingDirectory(_currentPage, ""); + if (!newPath.isEmpty()) + { + _ui.work_edit->setText(newPath); + } +} + void CTranslationManagerSettingsPage::apply() { + writeSettings(); } +void CTranslationManagerSettingsPage::readSettings() +{ + QStringList paths, pathsR, georges, filters, languages; + QString ligo, translation, work; + + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("translationmanager"); + + paths = settings->value("paths").toStringList(); /* paths */ + pathsR = settings->value("pathsR").toStringList(); /* pathsR */ + georges = settings->value("georges").toStringList(); /* georges */ + filters = settings->value("filters").toStringList(); /* filters */ + languages = settings->value("languages").toStringList(); /* languages */ + ligo = settings->value("ligo").toString(); + translation = settings->value("translation").toString(); + work = settings->value("work").toString(); + + settings->endGroup(); + /* paths */ + Q_FOREACH(QString path, paths) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(path); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.paths_list->addItem(newItem); + } + /* pathsR */ + Q_FOREACH(QString pathR, pathsR) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(pathR); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.pathsR_list->addItem(newItem); + } + /* georges */ + Q_FOREACH(QString george, georges) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(george); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.georges_list->addItem(newItem); + } + /* filter */ + Q_FOREACH(QString filter, filters) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(filter); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.filter_list->addItem(newItem); + } + /* languages */ + Q_FOREACH(QString lang, languages) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(lang); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + _ui.lang_list->addItem(newItem); + } + /* ligo */ + _ui.ligo_edit->setText(ligo); + /* translation */ + _ui.translation_edit->setText(translation); + /* work */ + _ui.work_edit->setText(work); + +} + +void CTranslationManagerSettingsPage::writeSettings() +{ + QStringList paths, pathsR, georges, filters, languages; + QString ligo, translation, work; + /* paths */ + for (int i = 0; i < _ui.paths_list->count(); ++i) + paths << _ui.paths_list->item(i)->text(); + /* pathsR */ + for (int i = 0; i < _ui.pathsR_list->count(); ++i) + pathsR << _ui.pathsR_list->item(i)->text(); + /* georges */ + for (int i = 0; i < _ui.georges_list->count(); ++i) + georges << _ui.georges_list->item(i)->text(); + /* filters */ + for (int i = 0; i < _ui.filter_list->count(); ++i) + filters << _ui.filter_list->item(i)->text(); + /* languages */ + for (int i = 0; i < _ui.lang_list->count(); ++i) + languages << _ui.lang_list->item(i)->text(); + /* ligo path */ + ligo = _ui.ligo_edit->text(); + /* translations path*/ + translation = _ui.translation_edit->text(); + work = _ui.work_edit->text(); + + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("translationmanager"); + settings->setValue("paths", paths); + settings->setValue("pathsR", pathsR); + settings->setValue("georges", georges); + settings->setValue("filters", filters); + settings->setValue("languages", languages); + settings->setValue("ligo", ligo); + settings->setValue("translation", translation); + settings->setValue("work", work); + settings->endGroup(); +} + + } /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h index 53f9068e1..4fec51fb6 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h @@ -47,10 +47,24 @@ public: virtual void apply(); virtual void finish() {} - +private Q_SLOTS: + void pathAdd(); + void pathDel(); + void pathRAdd(); + void pathRDel(); + void georgeAdd(); + void georgeDel(); + void filterAdd(); + void filterDel(); + void languageAdd(); + void languageDel(); + void translationAdd(); + void workAdd(); private: QWidget *_currentPage; Ui::CTranslationManagerSettingsPage _ui; + void writeSettings(); + void readSettings(); }; } // namespace Plugin diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui index 951be0615..c58533185 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui @@ -1,92 +1,393 @@ - - - CTranslationManagerSettingsPage - - - - 0 - 0 - 458 - 479 - - - - Form - - - - 0 - - - - - GroupBox - - - - - - PushButton - - - - - - - - - - PushButton - - - - - - - RadioButton - - - - - - - RadioButton - - - - - - - CheckBox - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - - - + + + CTranslationManagerSettingsPage + + + + 0 + 0 + 490 + 495 + + + + Form + + + + + + 1 + + + + Core paths + + + + + + + + + + Paths + + + + + + + Qt::Horizontal + + + + 318 + 20 + + + + + + + + dwadwadwa + + + + :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png + + + Qt::ToolButtonIconOnly + + + true + + + + + + + ... + + + + :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png + + + true + + + + + + + + + + + + + + + + + + Paths non recursives + + + + + + + Qt::Horizontal + + + + 218 + 20 + + + + + + + + + + + + :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png + + + true + + + + + + + ... + + + + :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png + + + true + + + + + + + + + + + + + + + + + + Georges Paths + + + + + + + Qt::Horizontal + + + + 258 + 20 + + + + + + + + + + + + :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png + + + true + + + + + + + + + + + :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png + + + true + + + + + + + + + + + + + + + Translation files paths + + + + + 9 + 230 + 450 + 201 + + + + + + + Ligo class file - This is the name of the world_editor_classes.xml file. + + + + + + + + + + Work directory + + + + + + + + + + + + ... + + + + + + + + + Translation directory + + + + + + + + + + + + ... + + + + + + + + + + + 9 + 10 + 211 + 221 + + + + + + + Filters + + + + + + + + + + + + + + :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png + + + true + + + + + + + + + + + :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png + + + true + + + + + + + + + + + + 240 + 10 + 221 + 221 + + + + + + + Languages + + + + + + + + + + + + + + :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png + + + true + + + + + + + + + + + :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png + + + true + + + + + + + + + + + + + + + + + + + From bab4b3af21e747babc42cd86a8d8b8600d84424d Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Tue, 31 May 2011 16:22:57 +0200 Subject: [PATCH 007/215] Changed: #1304: mission_compiler objectives: check and script generation mission compiler now checks if we are not asking several guild members to complete an objective whereas the mission is not a guild mission. It also generates the 'nb_guild_members_needed' parameter for objectives in the script. --HG-- branch : gsoc2011-guildmissions --- .../world_editor_classes.xml | 4 +- .../mission_compiler_lib/mission_compiler.h | 5 ++ .../leveldesign/mission_compiler_lib/step.h | 5 ++ .../mission_compiler_lib/step_content.cpp | 76 ++++++++++++++++--- 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml index 1e4f9d723..238158bbf 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml @@ -1304,9 +1304,9 @@ - + diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/mission_compiler.h b/code/ryzom/tools/leveldesign/mission_compiler_lib/mission_compiler.h index 756b3859d..6f993679b 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/mission_compiler.h +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/mission_compiler.h @@ -318,6 +318,11 @@ public: bool isThereAJumpTo(const std::string &stepName); + bool isGuildMission() const + { + return _Guild; + } + private: std::string genPreRequisites(); diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h b/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h index e93db4524..9d2a053df 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h @@ -192,6 +192,11 @@ protected: std::vector _RoleplayObj; CPhrase _RoleplayPhrase; bool _HideObj; + + // Option nb_guild_members_needed, available for each objective + int _NbGuildMembersNeeded; + + std::string genNbGuildMembersNeededOption(CMissionData &md); public: void init(CMissionData &md, NLLIGO::IPrimitive *prim); diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp index 19d210f0f..c18709b1d 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp @@ -1630,6 +1630,20 @@ void CContentObjective::init(CMissionData &md, IPrimitive *prim) _OverloadPhrase.initPhrase(md, prim, _OverloadObj, numEntry, params); // init the roleplay phrase _RoleplayPhrase.initPhrase(md, prim, _RoleplayObj, numEntry, params); + + // check for the 'nb_guild_members_needed' option and see if it's correct for this mission + string nbGuildMembersNeeded = md.getProperty(prim, "nb_guild_members_needed", false, true); + if (nbGuildMembersNeeded.empty()) + nbGuildMembersNeeded = "1"; + if (!fromString(nbGuildMembersNeeded.c_str(), _NbGuildMembersNeeded)) + _NbGuildMembersNeeded = 1; + + // Check: + if (!md.isGuildMission() && _NbGuildMembersNeeded != 1) + { + string err = toString("primitive(%s): nb_guild_members_needed != 1 for non guild mission.", prim->getName().c_str()); + throw EParseException(prim, err.c_str()); + } } // --------------------------------------------------------------------------- @@ -1649,6 +1663,20 @@ string CContentObjective::genCode(CMissionData &md) return ret; } +// --------------------------------------------------------------------------- +std::string CContentObjective::genNbGuildMembersNeededOption(CMissionData &md) +{ + string ret = ""; + // If we are in a guild mission we add the 'nb_guild_members_needed' option to the script + if (md.isGuildMission()) + { + ret = "; nb_guild_members_needed: "; + ret += toString(_NbGuildMembersNeeded); + } + + return ret; +} + // --------------------------------------------------------------------------- string CContentObjective::genPhrase() { @@ -1949,6 +1977,9 @@ public: if (!_Place.empty()) ret += " : "+_Place; + + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2004,6 +2035,8 @@ public: if (!_Phrase.isEmpty()) ret += " : "+_Phrase.genScript(md); + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2078,6 +2111,8 @@ public: { ret += ": "+_Place; } + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2151,6 +2186,8 @@ public: if (i < _Mps.size()-1) ret += "; "; } + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2276,6 +2313,8 @@ public: if (i < _Items.size()-1) ret += "; "; } + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2349,6 +2388,8 @@ public: if (i < _Items.size()-1) ret += "; "; } + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2479,7 +2520,8 @@ public: if (!_Place.empty()) ret += " : " + _Place; - + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2564,6 +2606,8 @@ public: { ret += " : "+_Npc; } + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2645,6 +2689,8 @@ public: { ret += " : "+_Npc; } + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2755,7 +2801,10 @@ public: if (i < _Items.size()-1) ret += "; "; }; - ret += " : "+_Npc +NL; + ret += " : "+_Npc; + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); + ret += NL; return ret; } @@ -2790,7 +2839,10 @@ public: string ret; ret = CContentObjective::genCode(md); - ret += "give_money : "+_Amount+" : "+_Npc+NL; + ret += "give_money : "+_Amount+" : "+_Npc; + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); + ret += NL; return ret; } @@ -2841,7 +2893,8 @@ public: ret += "; "; } } - + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2877,7 +2930,8 @@ public: if (_SaveAll) ret += " : save_all"; - + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2950,7 +3004,8 @@ public: if (i < _Skills.size()-1) ret += "; "; } - + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2988,7 +3043,8 @@ public: if (i < _Missions.size()-1) ret += "; "; } - + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -3026,7 +3082,8 @@ public: if (i < _MsgContent.size()-1) ret += " "; } - + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -3242,7 +3299,8 @@ public: ret += "ring_scenario : "; ret += _ScenarioTag; - + // Add the 'nb_guild_members_needed' parameter if needed + ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; From 91fef65450c56f8607a8c5bfc11986c04de58367 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Tue, 31 May 2011 22:00:27 +0200 Subject: [PATCH 009/215] Changed: #1304: mission_compiler actions: add new ones, check and script generation Add 2 new actions for the mission compiler: recv_charge_point and give_control. Add the 'guild' option for some actions: check if the mission is valid, and change the script generation to add this option. --HG-- branch : gsoc2011-guildmissions --- .../mission_compiler_lib/step_content.cpp | 134 +++++++++++++++++- 1 file changed, 132 insertions(+), 2 deletions(-) diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp index c18709b1d..456f7e286 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp @@ -142,6 +142,7 @@ REGISTER_STEP_CONTENT(CActionJumpTo, "jump_to"); class CActionRecvMoney : public IStepContent { string _Amount; + bool _Guild; void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) { @@ -152,12 +153,27 @@ public: { IStepContent::init(md, prim); _Amount = md.getProperty(prim, "amount", true, false); + + _Guild = md.getProperty(prim, "guild", false, true) == "true"; + // Check: if _Guild is true then check if we are in a guild mission + if (_Guild && !md.isGuildMission()) + { + string err = toString("primitive(%s): 'guild' option true 1 for non guild mission.", prim->getName().c_str()); + throw EParseException(prim, err.c_str()); + } } string genCode(CMissionData &md) { if (!_Amount.empty()) - return "recv_money : "+_Amount+NL; + { + string ret; + ret = "recv_money : "+_Amount; + if (_Guild) + ret += "; guild"; + ret += NL; + return ret; + } else return string(); } @@ -166,6 +182,72 @@ public: REGISTER_STEP_CONTENT(CActionRecvMoney, "recv_money"); +// --------------------------------------------------------------------------- +class CActionRecvChargePoint : public IStepContent +{ + string _Amount; + + void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) + { + numEntry = 0; + } +public: + void init(CMissionData &md, IPrimitive *prim) + { + IStepContent::init(md, prim); + _Amount = md.getProperty(prim, "charge_points", true, false); + } + + string genCode(CMissionData &md) + { + if (!_Amount.empty()) + { + string ret; + ret = "recv_charge_point : "+_Amount; + ret += NL; + return ret; + } + else + return string(); + } + +}; +REGISTER_STEP_CONTENT(CActionRecvChargePoint, "recv_charge_point"); + + +// --------------------------------------------------------------------------- +class CActionGiveOutpostControl : public IStepContent +{ + string _OutpostName; + + void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) + { + numEntry = 0; + } +public: + void init(CMissionData &md, IPrimitive *prim) + { + IStepContent::init(md, prim); + _OutpostName = md.getProperty(prim, "outpost_name", true, false); + } + + string genCode(CMissionData &md) + { + if (!_OutpostName.empty()) + { + string ret; + ret = "give_control : "+_OutpostName; + ret += NL; + return ret; + } + else + return string(); + } + +}; +REGISTER_STEP_CONTENT(CActionGiveOutpostControl, "give_control"); + + // --------------------------------------------------------------------------- class CActionSpawnMission : public IStepContent { @@ -590,6 +672,7 @@ class CActionRecvFame : public IStepContent { string _Faction; string _Fame; + bool _Guild; void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) { @@ -602,12 +685,27 @@ public: _Faction = md.getProperty(prim, "faction", true, false); _Fame = md.getProperty(prim, "value", true, false); + + _Guild = md.getProperty(prim, "guild", false, true) == "true"; + // Check: if _Guild is true then check if we are in a guild mission + if (_Guild && !md.isGuildMission()) + { + string err = toString("primitive(%s): 'guild' option true 1 for non guild mission.", prim->getName().c_str()); + throw EParseException(prim, err.c_str()); + } } string genCode(CMissionData &md) { if (!_Faction.empty() && !_Fame.empty()) - return string("recv_fame : ")+_Faction+" "+_Fame+NL; + { + string ret; + ret = "recv_fame : "+_Faction+" "+_Fame; + if (_Guild) + ret += "; guild"; + ret += NL; + return ret; + } else return string(); } @@ -628,6 +726,7 @@ class CActionRecvItem : public IStepContent vector _Items; bool _QualSpec; bool _Group; + bool _Guild; void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) { @@ -675,6 +774,13 @@ public: s = md.getProperty(prim, "group", true, false); _Group = (strlwr(s) == "true"); + _Guild = md.getProperty(prim, "guild", false, true) == "true"; + // Check: if _Guild is true then check if we are in a guild mission + if (_Guild && !md.isGuildMission()) + { + string err = toString("primitive(%s): 'guild' option true 1 for non guild mission.", prim->getName().c_str()); + throw EParseException(prim, err.c_str()); + } IStepContent::init(md, prim); } @@ -693,6 +799,8 @@ public: ret += " : "+_BotGiver; if (_Group) ret += " : group"; + if (_Guild) + ret += "; guild"; ret += NL; } @@ -707,6 +815,7 @@ class CActionRecvNamedItem : public IStepContent { vector _Items; bool _Group; + bool _Guild; void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) { @@ -743,6 +852,14 @@ public: s = md.getProperty(prim, "group", true, false); _Group = (strlwr(s) == "true"); + _Guild = md.getProperty(prim, "guild", false, true) == "true"; + // Check: if _Guild is true then check if we are in a guild mission + if (_Guild && !md.isGuildMission()) + { + string err = toString("primitive(%s): 'guild' option true 1 for non guild mission.", prim->getName().c_str()); + throw EParseException(prim, err.c_str()); + } + IStepContent::init(md, prim); } @@ -756,6 +873,8 @@ public: ret += "recv_named_item : "+item.ItemName+" "+item.ItemQuant; if (_Group) ret += " : group"; + if (_Guild) + ret += "; guild"; ret += NL; } @@ -777,6 +896,7 @@ class CActionDestroyItem : public IStepContent string _BotDestroyer; vector _Items; + bool _Guild; void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) { @@ -821,6 +941,14 @@ public: _Items.push_back(item); } } + + _Guild = md.getProperty(prim, "guild", false, true) == "true"; + // Check: if _Guild is true then check if we are in a guild mission + if (_Guild && !md.isGuildMission()) + { + string err = toString("primitive(%s): 'guild' option true 1 for non guild mission.", prim->getName().c_str()); + throw EParseException(prim, err.c_str()); + } IStepContent::init(md, prim); } @@ -839,6 +967,8 @@ public: ret +=" "+item.Desc.ItemQual; if (!_BotDestroyer.empty()) ret += " : "+_BotDestroyer; + if (_Guild) + ret += "; guild"; ret += NL; } From 5be81c6487d1c43638797c304d8dfffdecdc2616 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 1 Jun 2011 07:44:44 -0500 Subject: [PATCH 010/215] Changed: Fixed a bug with the log plugin causing crashes when the apply button was clicked for settings. --HG-- branch : branch-mission-compiler-qt --- .../object_viewer_qt/src/plugins/log/log_plugin.cpp | 2 +- .../src/plugins/log/log_settings_page.cpp | 11 +++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp index 2f88200ec..b1f3cbc18 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp @@ -103,7 +103,7 @@ namespace Plugin QString CLogPlugin::name() const { - return "NeL Log"; + return "LogPlugin"; } QString CLogPlugin::version() const diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_settings_page.cpp index f75d6ab37..02d6cd24d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_settings_page.cpp @@ -99,11 +99,14 @@ namespace Plugin writeSettings(); ExtensionSystem::IPluginManager *p = Core::ICore::instance()->pluginManager(); - ExtensionSystem::IPlugin *plugin = p->pluginByName("LogPlugin")->plugin(); - CLogPlugin* lp = dynamic_cast(plugin); - if (lp) + ExtensionSystem::IPluginSpec *spec = p->pluginByName("LogPlugin"); + + if(spec) { - lp->setDisplayers(); + ExtensionSystem::IPlugin *plugin = spec->plugin(); + CLogPlugin* lp = dynamic_cast(plugin); + if (lp) + lp->setDisplayers(); } } From 947d69c97e5406889f5b2c61e745d6956ef7c166 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 1 Jun 2011 07:45:20 -0500 Subject: [PATCH 011/215] Changed: Added the primitive file path and ligo config file to the "data section" - automatically applied these paths to addSearchPath. --HG-- branch : branch-mission-compiler-qt --- .../src/plugins/core/core_constants.h | 2 + .../plugins/core/general_settings_page.cpp | 36 +++++++++++++++++ .../src/plugins/core/general_settings_page.h | 2 + .../src/plugins/core/general_settings_page.ui | 40 +++++++++++++++++-- 4 files changed, 77 insertions(+), 3 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h index 7f0b5cd15..745812c2a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h @@ -78,6 +78,8 @@ const char * const SEARCH_PATHS = "SearchPaths"; const char * const RECURSIVE_SEARCH_PATHS = "RecursiveSearchPathes"; const char * const LEVELDESIGN_PATH = "LevelDesignPath"; const char * const ASSETS_PATH = "AssetsPath"; +const char * const PRIMITIVES_PATH = "PrimitivesPath"; +const char * const LIGOCONFIG_FILE = "LigoConfigFile"; const char * const REMAP_EXTENSIONS = "RemapExtensions"; const char * const LOG_SECTION = "LogSettings"; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp index 51da80f67..a050f4789 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp @@ -82,6 +82,15 @@ void GeneralSettingsPage::applyGeneralSettings() else QApplication::setPalette(m_originalPalette); settings->endGroup(); + + // Add primitives path and ligo config file to CPath + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + QString primitivePath = settings->value(Core::Constants::PRIMITIVES_PATH, "l:/primitives").toString(); + QString ligoConfigFile = settings->value(Core::Constants::LIGOCONFIG_FILE, "l:/leveldesign/world_editor_files/world_editor_classes.xml").toString(); + NLMISC::CPath::addSearchPath(primitivePath.toStdString(), true, false); + NLMISC::CPath::display(); + NLMISC::CPath::addSearchFile(ligoConfigFile.toStdString()); + settings->endGroup(); } QWidget *GeneralSettingsPage::createPage(QWidget *parent) @@ -94,6 +103,8 @@ QWidget *GeneralSettingsPage::createPage(QWidget *parent) connect(m_ui.pluginsPathButton, SIGNAL(clicked()), this, SLOT(setPluginsPath())); connect(m_ui.leveldesignPathButton, SIGNAL(clicked()), this, SLOT(setLevelDesignPath())); connect(m_ui.assetsPathButton, SIGNAL(clicked()), this, SLOT(setAssetsPath())); + connect(m_ui.primitivesPathButton, SIGNAL(clicked()), this, SLOT(setPrimitivesPath())); + connect(m_ui.ligoConfigFileButton, SIGNAL(clicked()), this, SLOT(setLigoConfigFile())); return m_page; } @@ -135,6 +146,27 @@ void GeneralSettingsPage::setLevelDesignPath() } } +void GeneralSettingsPage::setPrimitivesPath() +{ + QString newPath = QFileDialog::getExistingDirectory(0, tr("Set the primitives path"), + m_ui.primitivesPathLineEdit->text()); + if (!newPath.isEmpty()) + { + m_ui.primitivesPathLineEdit->setText(newPath); + } +} + +void GeneralSettingsPage::setLigoConfigFile() +{ + QString newFile = QFileDialog::getOpenFileName(0, tr("Set the ligo config file"), + m_ui.ligoConfigFileLineEdit->text()); + if (!newFile.isEmpty()) + { + m_ui.ligoConfigFileLineEdit->setText(newFile); + } +} + + void GeneralSettingsPage::setAssetsPath() { QString newPath = QFileDialog::getExistingDirectory(0, tr("Set the assets path"), @@ -165,6 +197,8 @@ void GeneralSettingsPage::readSettings() settings->beginGroup(Core::Constants::DATA_PATH_SECTION); m_ui.leveldesignPathLineEdit->setText(settings->value(Core::Constants::LEVELDESIGN_PATH, "l:/leveldesign").toString()); m_ui.assetsPathLineEdit->setText(settings->value(Core::Constants::ASSETS_PATH, "w:/database").toString()); + m_ui.primitivesPathLineEdit->setText(settings->value(Core::Constants::PRIMITIVES_PATH, "l:/primitives").toString()); + m_ui.ligoConfigFileLineEdit->setText(settings->value(Core::Constants::LIGOCONFIG_FILE, "l:/leveldesign/world_editor_files/world_editor_classes.xml").toString()); settings->endGroup(); } @@ -185,6 +219,8 @@ void GeneralSettingsPage::writeSettings() settings->beginGroup(Core::Constants::DATA_PATH_SECTION); settings->setValue(Core::Constants::LEVELDESIGN_PATH, m_ui.leveldesignPathLineEdit->text()); settings->setValue(Core::Constants::ASSETS_PATH, m_ui.assetsPathLineEdit->text()); + settings->setValue(Core::Constants::PRIMITIVES_PATH, m_ui.primitivesPathLineEdit->text()); + settings->setValue(Core::Constants::LIGOCONFIG_FILE, m_ui.ligoConfigFileLineEdit->text()); settings->endGroup(); settings->sync(); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.h index 2fbcb842a..22ab30e45 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.h @@ -57,6 +57,8 @@ private Q_SLOTS: void setPluginsPath(); void setLevelDesignPath(); void setAssetsPath(); + void setPrimitivesPath(); + void setLigoConfigFile(); private: void readSettings(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.ui index d2aa042e3..7d40b65df 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.ui @@ -105,7 +105,7 @@ - Plugins path: + Plugins @@ -129,7 +129,7 @@ - Level design path: + Sheets @@ -153,7 +153,7 @@ - Assets path: + Assets Database: @@ -174,6 +174,40 @@ + + + + Primitives + + + + + + + + + + ... + + + + + + + + + + Ligo Config File + + + + + + + ... + + + From bdbeaa29e80e9f22cd3595b200d011e3719b959a Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 1 Jun 2011 07:45:55 -0500 Subject: [PATCH 012/215] Changed: Initial footwork on OVQT-based mission compiler plugin. --HG-- branch : branch-mission-compiler-qt --- .../src/plugins/CMakeLists.txt | 1 + .../plugins/mission_compiler/CMakeLists.txt | 41 ++++ .../mission_compiler_main_window.cpp | 63 ++++++ .../mission_compiler_main_window.h | 36 ++++ .../mission_compiler_main_window.ui | 195 ++++++++++++++++++ .../mission_compiler_plugin.cpp | 132 ++++++++++++ .../mission_compiler_plugin.h | 105 ++++++++++ 7 files changed, 573 insertions(+) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt index bec71cf94..c5d49418d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt @@ -7,3 +7,4 @@ ADD_SUBDIRECTORY(disp_sheet_id) ADD_SUBDIRECTORY(object_viewer) ADD_SUBDIRECTORY(zone_painter) ADD_SUBDIRECTORY(georges_editor) +ADD_SUBDIRECTORY(mission_compiler) \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt new file mode 100644 index 000000000..70f4d0642 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt @@ -0,0 +1,41 @@ +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBXML2_INCLUDE_DIR} + ${QT_INCLUDES}) + +FILE(GLOB SRC *.cpp *.h) + +SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_manager.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_spec.h) + +SET(OVQT_PLUG_MISSION_COMPILER_HDR mission_compiler_plugin.h + mission_compiler_main_window.h) + +SET(OVQT_PLUG_MISSION_COMPILER_UIS mission_compiler_main_window.ui) + +#SET(OVQT_PLUG_ZONE_PAINTER_RCS zone_painter.qrc) + +SET(QT_USE_QTGUI TRUE) + +QT4_WRAP_CPP(OVQT_PLUG_MISSION_COMPILER_MOC_SRC ${OVQT_PLUG_MISSION_COMPILER_HDR}) +#QT4_ADD_RESOURCES( OVQT_PLUG_ZONE_PAINTER_RC_SRCS ${OVQT_PLUG_ZONE_PAINTER_RCS}) +QT4_WRAP_UI(OVQT_PLUG_MISSION_COMPILER_UI_HDRS ${OVQT_PLUG_MISSION_COMPILER_UIS}) + +SOURCE_GROUP(QtResources FILES ${OVQT_PLUG_MISSION_COMPILER_UIS}) +SOURCE_GROUP(QtGeneratedUiHdr FILES ${OVQT_PLUG_MISSION_COMPILER_UI_HDRS}) +SOURCE_GROUP(QtGeneratedMocSrc FILES ${OVQT_PLUG_MISSION_COMPILER_MOC_SRC} ) +SOURCE_GROUP("Mission Compiler Plugin" FILES ${SRC}) +SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) + +ADD_LIBRARY(ovqt_plugin_mission_compiler MODULE ${SRC} ${OVQT_PLUG_MISSION_COMPILER_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_MISSION_COMPILER_UI_HDRS}) + +TARGET_LINK_LIBRARIES(ovqt_plugin_mission_compiler ovqt_plugin_core nelmisc nelligo ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY}) + +NL_DEFAULT_PROPS(ovqt_plugin_mission_compiler "NeL, Tools, 3D: Object Viewer Qt Plugin: Mission Compiler") +NL_ADD_RUNTIME_FLAGS(ovqt_plugin_mission_compiler) +NL_ADD_LIB_SUFFIX(ovqt_plugin_mission_compiler) + +ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} -DQT_PLUGIN -DQT_SHARED ${QT_DEFINITIONS}) + +INSTALL(TARGETS ovqt_plugin_mission_compiler LIBRARY DESTINATION lib RUNTIME DESTINATION bin ARCHIVE DESTINATION lib COMPONENT tools3d) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp new file mode 100644 index 000000000..dcf17ba34 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -0,0 +1,63 @@ +#include "mission_compiler_main_window.h" +#include "ui_mission_compiler_main_window.h" + +#include +#include +#include +#include +#include + +#include "../core/icore.h" +#include "../core/imenu_manager.h" +#include "../core/core_constants.h" + +#include + +MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MissionCompilerMainWindow) +{ + ui->setupUi(this); + + // Load the settings. + loadConfig(); + + m_undoStack = new QUndoStack(this); + + // Populate the "all" primitives box. + std::vector paths; + NLMISC::CPath::getFileList("primitive", paths); + + std::vector::iterator itr = paths.begin(); + while( itr != paths.end() ) + { + const char *path2 = (*itr).c_str(); + ui->allPrimitivesList->insertItem(0,path2); + ++itr; + } +} + +void MissionCompilerMainWindow::loadConfig() { + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("MissionCompiler"); + + //QColor color; + //color = settings->value("BackgroundColor", QColor(80, 80, 80)).value(); + //m_nelWidget->setBackgroundColor(NLMISC::CRGBA(color.red(), color.green(), color.blue(), color.alpha())); +} + +void MissionCompilerMainWindow::saveConfig() { + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("MissionCompiler" ); + + //QColor color(m_nelWidget->backgroundColor().R, m_nelWidget->backgroundColor().G, m_nelWidget->backgroundColor().B, m_nelWidget->backgroundColor().A); + //settings->setValue("BackgroundColor", color); + + settings->endGroup(); + settings->sync(); +} + +MissionCompilerMainWindow::~MissionCompilerMainWindow() +{ + delete ui; +} diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h new file mode 100644 index 000000000..9e94ea9fb --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h @@ -0,0 +1,36 @@ +#ifndef MISSION_COMPILER_MAIN_WINDOW_H +#define MISSION_COMPILER_MAIN_WINDOW_H + +#include +#include +#include +#include +#include +#include + +namespace Ui { + class MissionCompilerMainWindow; +} + +class MissionCompilerMainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MissionCompilerMainWindow(QWidget *parent = 0); + ~MissionCompilerMainWindow(); + + void loadConfig(); + void saveConfig(); + QUndoStack *getUndoStack() { return m_undoStack; } + + +private: + Ui::MissionCompilerMainWindow *ui; + + QMenu *_toolModeMenu; + QUndoStack *m_undoStack; + QStringListModel *m_allPrimitivesModel; +}; + +#endif // MISSION_COMPILER_MAIN_WINDOW_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui new file mode 100644 index 000000000..30756f91c --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui @@ -0,0 +1,195 @@ + + + MissionCompilerMainWindow + + + + 0 + 0 + 794 + 600 + + + + MainWindow + + + + + + + 0 + + + + + 0 + 0 + 776 + 398 + + + + Mission Compiler Options + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + >> + + + + + + + ALL >> + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + << ALL + + + + + + + << + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Publish Options + + + + + + + + + + + 0 + 0 + 776 + 398 + + + + Compilation Output + + + + + + true + + + + + + + + + + + Actions + + + + + + Validate + + + + + + + Compile + + + + + + + Compile and Publish + + + + + + + + + + + + 0 + 0 + 794 + 21 + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp new file mode 100644 index 000000000..cfea63959 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp @@ -0,0 +1,132 @@ +// Project includes +#include "mission_compiler_plugin.h" +#include "../core/icore.h" +#include "../core/core_constants.h" +#include "../core/imenu_manager.h" +#include "../../extension_system/iplugin_spec.h" + +// NeL includes +#include "nel/misc/debug.h" + +// Qt includes +#include +#include +#include +#include +#include +#include + +namespace Plugin +{ + +MissionCompilerPlugin::~MissionCompilerPlugin() +{ + Q_FOREACH(QObject *obj, _autoReleaseObjects) + { + _plugMan->removeObject(obj); + } + qDeleteAll(_autoReleaseObjects); + _autoReleaseObjects.clear(); +} + +bool MissionCompilerPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) +{ + Q_UNUSED(errorString); + _plugMan = pluginManager; + + //addAutoReleasedObject(new CZonePainterSettingsPage(this)); + addAutoReleasedObject(new CMissionCompilerContext(this)); + //addAutoReleasedObject(new CCoreListener(this)); + return true; +} + +void MissionCompilerPlugin::extensionsInitialized() +{ + Core::ICore *core = Core::ICore::instance(); + QSettings *settings = Core::ICore::instance()->settings(); + Core::IMenuManager *menuManager = core->menuManager(); + //menuManager = _plugMan->getObject(); + //QAction *exampleAction1 = new QAction("Zone1", this); + //QAction *exampleAction2 = new QAction("Zone2", this); + //QMenu *toolsMenu = menuManager->menu(Core::Constants::M_TOOLS); + //helpMenu->insertAction(aboutQtAction, exampleAction1); + //helpMenu->addSeparator(); + //helpMenu->addAction(exampleAction2); + //QMenu *zoneMenu = menuManager->menuBar()->addMenu("ZoneMenu"); + //zoneMenu->insertAction(aboutQtAction, exampleAction1); + //zoneMenu->addSeparator(); + //zoneMenu->addAction(exampleAction2); + + // Initialize Ligo. + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + QString ligoConfigFile = settings->value(Core::Constants::DATA_PATH_SECTION).toString(); + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + + NLLIGO::Register(); + LigoConfig.readPrimitiveClass(ligoConfigFile.toAscii().data(), false); + NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; +} + +void MissionCompilerPlugin::setNelContext(NLMISC::INelContext *nelContext) +{ +#ifdef NL_OS_WINDOWS + // Ensure that a context doesn't exist yet. + // This only applies to platforms without PIC, e.g. Windows. + nlassert(!NLMISC::INelContext::isContextInitialised()); +#endif // NL_OS_WINDOWS + _LibContext = new NLMISC::CLibraryContext(*nelContext); +} + +QString MissionCompilerPlugin::name() const +{ + return "MissionCompilerPlugin"; +} + +QString MissionCompilerPlugin::version() const +{ + return "0.1"; +} + +QString MissionCompilerPlugin::vendor() const +{ + return "Ryzom Core"; +} + +QString MissionCompilerPlugin::description() const +{ + return "Mission Compiler Plugin"; +} + +QStringList MissionCompilerPlugin::dependencies() const +{ + QStringList list; + list.append(Core::Constants::OVQT_CORE_PLUGIN); + //list.append("ObjectViewer"); + return list; +} + +void MissionCompilerPlugin::addAutoReleasedObject(QObject *obj) +{ + _plugMan->addObject(obj); + _autoReleaseObjects.prepend(obj); +} + +QObject* MissionCompilerPlugin::objectByName(const QString &name) const +{ + Q_FOREACH (QObject *qobj, _plugMan->allObjects()) + if (qobj->objectName() == name) + return qobj; + return 0; +} + +ExtensionSystem::IPluginSpec *MissionCompilerPlugin::pluginByName(const QString &name) const +{ + Q_FOREACH (ExtensionSystem::IPluginSpec *spec, _plugMan->plugins()) + if (spec->name() == name) + return spec; + return 0; +} + +} + +Q_EXPORT_PLUGIN(Plugin::MissionCompilerPlugin) \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h new file mode 100644 index 000000000..a9d34bd13 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h @@ -0,0 +1,105 @@ +#ifndef MISSION_COMPILER_PLUGIN_H +#define MISSION_COMPILER_PLUGIN_H + +// Project includes +#include "../../extension_system/iplugin.h" +#include "../core/icontext.h" +#include "mission_compiler_main_window.h" + +// NeL includes +#include +#include +#include +#include + +// Qt includes +#include +#include + +namespace NLMISC +{ +class CLibraryContext; +} + +namespace ExtensionSystem +{ +class IPluginSpec; +} + +namespace Plugin +{ + +class MissionCompilerPlugin : public QObject, public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_INTERFACES(ExtensionSystem::IPlugin) +public: + + virtual ~MissionCompilerPlugin(); + + bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); + void extensionsInitialized(); + + void setNelContext(NLMISC::INelContext *nelContext); + + QString name() const; + QString version() const; + QString vendor() const; + QString description() const; + QStringList dependencies() const; + + void addAutoReleasedObject(QObject *obj); + + QObject *objectByName(const QString &name) const; + ExtensionSystem::IPluginSpec *pluginByName(const QString &name) const; + + NLLIGO::CLigoConfig LigoConfig; + +protected: + NLMISC::CLibraryContext *_LibContext; + +private: + ExtensionSystem::IPluginManager *_plugMan; + QList _autoReleaseObjects; +}; + +class CMissionCompilerContext: public Core::IContext +{ + Q_OBJECT +public: + CMissionCompilerContext(QObject *parent = 0): IContext(parent) + { + m_missionCompilerMainWindow = new MissionCompilerMainWindow(); + } + virtual ~CMissionCompilerContext() {} + + virtual QString id() const + { + return QLatin1String("MissionCompilerContext"); + } + virtual QString trName() const + { + return tr("Mission Compiler"); + } + virtual QIcon icon() const + { + return QIcon(); + } + virtual QWidget *widget() + { + return m_missionCompilerMainWindow; + } + + virtual QUndoStack *undoStack() + { + return m_missionCompilerMainWindow->getUndoStack(); + } + virtual void open() {} + + + MissionCompilerMainWindow *m_missionCompilerMainWindow; +}; + +} // namespace Plugin + +#endif // MISSION_COMPILER_PLUGIN_H From 00e65b101557e11cd2458e4f77acb3946d3ed20d Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 1 Jun 2011 18:41:01 -0500 Subject: [PATCH 013/215] Changed: Added some more UI tweaks - most of the UI design is now done. --HG-- branch : branch-mission-compiler-qt --- .../plugins/mission_compiler/CMakeLists.txt | 10 +- .../mission_compiler/images/arrow-left-2.png | Bin 0 -> 1512 bytes .../images/arrow-left-double-2.png | Bin 0 -> 1893 bytes .../mission_compiler/images/arrow-right-2.png | Bin 0 -> 1478 bytes .../images/arrow-right-double-2.png | Bin 0 -> 1899 bytes .../images/document-export-4.png | Bin 0 -> 1604 bytes .../images/news-subscribe-2.png | Bin 0 -> 1805 bytes .../mission_compiler/images/run-build-2.png | Bin 0 -> 2228 bytes .../mission_compiler/mission_compiler.qrc | 11 + .../mission_compiler_main_window.cpp | 25 ++- .../mission_compiler_main_window.h | 7 + .../mission_compiler_main_window.ui | 212 ++++++++++++++---- .../mission_compiler_plugin.cpp | 11 +- 13 files changed, 222 insertions(+), 54 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-left-2.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-left-double-2.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-right-2.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-right-double-2.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/document-export-4.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/news-subscribe-2.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/run-build-2.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler.qrc diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt index 70f4d0642..af39fc18a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt @@ -14,21 +14,21 @@ SET(OVQT_PLUG_MISSION_COMPILER_HDR mission_compiler_plugin.h SET(OVQT_PLUG_MISSION_COMPILER_UIS mission_compiler_main_window.ui) -#SET(OVQT_PLUG_ZONE_PAINTER_RCS zone_painter.qrc) +SET(OVQT_PLUG_MISSION_COMPILER_RCS mission_compiler.qrc) SET(QT_USE_QTGUI TRUE) QT4_WRAP_CPP(OVQT_PLUG_MISSION_COMPILER_MOC_SRC ${OVQT_PLUG_MISSION_COMPILER_HDR}) -#QT4_ADD_RESOURCES( OVQT_PLUG_ZONE_PAINTER_RC_SRCS ${OVQT_PLUG_ZONE_PAINTER_RCS}) +QT4_ADD_RESOURCES( OVQT_PLUG_MISSION_COMPILER_RC_SRCS ${OVQT_PLUG_MISSION_COMPILER_RCS}) QT4_WRAP_UI(OVQT_PLUG_MISSION_COMPILER_UI_HDRS ${OVQT_PLUG_MISSION_COMPILER_UIS}) -SOURCE_GROUP(QtResources FILES ${OVQT_PLUG_MISSION_COMPILER_UIS}) +SOURCE_GROUP(QtResources FILES ${OVQT_PLUG_MISSION_COMPILER_UIS} ${OVQT_PLUG_MISSION_COMPILER_RCS}) SOURCE_GROUP(QtGeneratedUiHdr FILES ${OVQT_PLUG_MISSION_COMPILER_UI_HDRS}) -SOURCE_GROUP(QtGeneratedMocSrc FILES ${OVQT_PLUG_MISSION_COMPILER_MOC_SRC} ) +SOURCE_GROUP(QtGeneratedMocSrc FILES ${OVQT_PLUG_MISSION_COMPILER_MOC_SRC} ${OVQT_PLUG_MISSION_COMPILER_RC_SRCS}) SOURCE_GROUP("Mission Compiler Plugin" FILES ${SRC}) SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) -ADD_LIBRARY(ovqt_plugin_mission_compiler MODULE ${SRC} ${OVQT_PLUG_MISSION_COMPILER_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_MISSION_COMPILER_UI_HDRS}) +ADD_LIBRARY(ovqt_plugin_mission_compiler MODULE ${SRC} ${OVQT_PLUG_MISSION_COMPILER_MOC_SRC} ${OVQT_PLUG_MISSION_COMPILER_RC_SRCS} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_MISSION_COMPILER_UI_HDRS}) TARGET_LINK_LIBRARIES(ovqt_plugin_mission_compiler ovqt_plugin_core nelmisc nelligo ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY}) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-left-2.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-left-2.png new file mode 100644 index 0000000000000000000000000000000000000000..8f153292c3c8f4e41e5ce87e5eb8e72fad7ba126 GIT binary patch literal 1512 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmUzPnffIy#(^vlDyqr zfHV;NblzPLq&N#aB8!2v2N=7Z%(epwmK8Xr18J~%3>7|0moYFfKk#&M4DmR=_3G{X z5LcN4AK#yuv-x?>cPD*s?+GQEsh$fWc5+S>*lFkRC@VvWtIPkGxT)5p2b1-WnHyd_ zvgBHey@RliNUvb>qKgp+JiJ4t8lw`TS1r|f8+R`CyyatNI{~e{wZFX8Ci^`5{yW8J z^3Q|+Ws>gAd|j=0Va@BfGnby`)_nQt+9dFz{*yg}*%!x!JZsCOJ91Bj%=D1FpP4yt z@&AN^-(ipC#rN;$*9o4qG5mPaG4Jf%w>~unbRLZFQfJnk&%Evo|BVlNKNMYVEqapY zzkKcV?fH54e;v73eRe&&x}?;~6wZ=Q=eF!*l>WUSidpsYkB9rN`8;aA6#h>v%6$ww|%^%?$keVOKjY3&Rt??x7=Dl zqG)AH`eyHFzGvm@vtoCu9XDs~E@thGpZDpc9+cr<`*MHLY zTcCO&A*#(Va`TU}2qP0dRsJQb1qF}zT>k8I_@J4x%jf%cPNvzZh4&cSy3J#MA9_); ze06e}b8;6)r+B1J+>~jjqmFFql3rI-`&Bwf^o5Na zZ_CRGj+-=DFD;O|Wn#KKGJes2wPj5Xuj>Vxa2b}m21 zw)MrP;8kS;Y~7Q%rCcn=&N(ZSk*1|tIlOI<@FT?2~{LnA9wV=F^rT?2C~1B3agXKY~_ zKq{>ux_}xC4Rj5Rbd3x`jEt>}%&ZJeAsX1MV;WI308-w%HO3D+9QW+dm@{>{(JaZG%Q-e|y zQz{EjrrH1%@j?`p7A2=L_?4FB=OnK2H~k2dFhNMfhX(n;oLpXBu9umdpQoFhnU}5y jbhv&&QGQlxa!Ij%9$346ne7%(SgTe~DWM4fC9QN+ literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-left-double-2.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-left-double-2.png new file mode 100644 index 0000000000000000000000000000000000000000..8b6d62c7a6cfa1ff82302ff120359a54b7af657d GIT binary patch literal 1893 zcmai#c|04~7RNJM#!`E=YORW;LskhDd#PB$SZfa&p+Uq%BvMPLdc;=KD%Az;h<#rN zWu~f$Wzas`Ywc=z9;H-c>#GUgeBS5%_3r0$&+p!I&-b2l|G9_hV2>6RmJ+U3dVATv8N0HCHvk3V&Vw->l!i?-k?Z|s@3Gk78tZsifdQ>)`? z*&AYf|`&e6;xmaP4WYNcQblEvhwnF&xLqG-XrQlCl-`#_Lftp7|Z0BTJK0TWguOve_Q)2TlBo zBD-4eMOx1-%V#7l*gBPk{Q+;E{)QG*W!)Pb%%N@+j?WsKPa(X0!Z{VSbLfj}OEHfi zGYq$bPNJ7^bdk$c&)tD{k*6<#tWgtVv;eM2+)m&0jf$2ucZ81*6ejS#PhC22qa<25 zT6x9iF(H_cW$7u_+|(1hq?z)A`&e5hm2~(^yKuo|8zHII8=at*dp`7=zLmxta4%*k zXRJgiuC$^TSpUbKR1tq>dk|q(^OKHKj&0@6Og_vZYn=c zjwJ1{n@ggj$7T5Rek?-_y$Nr6o+0RFDfo*xoxS&c=t_}-nA9o%v`?x8PswCow3%!PEn0l5%>x%7}Usr zU)Q=*ZeoAX<@BQEXsdV%SHV3*BM`DlhOqmVYWf5mNKKu410 zy!;{ILt>3<|F<-S-AT3l*i(&ztruv%L+s)XulBS&=?`F5f8@0CL#5WJGA>a%)$M20 zujKFfB&WyX%&CX5?FKEWGK%xGe?OXIh*{@}Vl>DqDm)`s zQ_78<=}N3jT;p67+HYL`aabqpbHLEY*Ylfo2+m+gxig+Yt-oc3UOEsgrqc%)_HoZr z({uWqn0ap=)Et^S$t9GNvYagYtao4NldNRU1#05u`{yEc}< zN4X;ze1)|oN*3v7GWJ7OfBk5$u65_>0$Y9b(uYD~H8S$=U*`syozl?&^?sxZI%t3> z1eqiF-?x1?)~BF1eo0^X&K6=KU;H266kZJPkjy5+aE)?e&!aRmyQ(R=t`1nSxd0b) zPs+%u88^g4(U55~G}XxVD;ZAh}xp!sE&y7SXG+%#v)#)laO)WyS(!V?|TN zWhY1c&a$P|&*%nhVV!;G-BeGt(2lkxj=jQaoOX%LKgZK;o{Hh*h+b5dAqRYLPRI=@ zdwF~;J@z{lTZPr(;7wPhzs)DgX?*cN%Z{+*JgyhT>N;EwRwo?9jYy*3ddwoiq=2rV zsRl=FFGck9JsXcPZ_Q2Bce3m*ovUAyaIp_qRr6CEQ)o@bt}DKrTxb|W?{Y@26^T^m za_ZCK7Z&YzzsN-uM}boo^-2P_3%VRuF;aKV@~L=!N7}<~FHiYxKN;If*112LxcKwt zJMT-s+me2DvW>3bt^gdX7uPs<(C?wPy=!w1@8|;}e-BH$a_wNJk?VCCHGL ze|qhjL`2eTke13rWNCMFlc|v&w%8=y^zt@ZIK<=S1`V9P(_x^1>OHT)u5fbaYGnQE z^wm|hwYw3YlCH3OR3nhsPv-e4NDT=Lq%kGr^(zhQC(4DLZ3f9%;ASJcCZ(-DyTbuv zf~ns#g2kL_-eP-~_u(*O`&5>&3Mk#>QyRCT!540u8#BAVa=5&EKDH2<)2)?f$NP2F zDA(L6I5OU!f(Hfg3;+&=>Vsf#5cG-*430wTppMphD4th}!~6?4ibS1IJOBm-LE#`c z)CI1Cg6pDS$P+-oz&qi803s=X5J>+I00Y5bE>L|G9EyU$P5_bTSS$}f9+iY4;P7D) zc)(2xB^(8T9OoiX@i-EZND7MplgL4k(36k|2nOwdfxy8qh{4Ooqb_p%rjum<=S_jX z)OGNH3r889Nr4oaKN$}&3n1OVU$iFr2jQLY{sHu#dhv$5AkmYcNHPu&u#cpYLj5a$ zL~im95dS^!b#=o0RWgl60~2thFi;2~EC|di91>0@1>Z^XY{{$yGPTv3k literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-right-2.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-right-2.png new file mode 100644 index 0000000000000000000000000000000000000000..d4016e2c72a2461b0722fc3aa38b48408a69c629 GIT binary patch literal 1478 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmUzPnffIy#(^vlDyqr zfHV;NblzPLq&N#aB8!2v2N=7Z%(epwmK8Xr18J~%3>7|0moYFf@AGtV4DmR=_tNR? zm{f`5AKzD7razlA^Q6kd^L zo#?XOg-a9eC*RÅZ){re2Z8$Y+_3r~;@KCofaH$mZBs%z#hxKKQIE$feOM|qix zZYJd}7kl)!y4~8ZFXjC&i)Y90FaDb*ChGNV&R$st-JlEK6Si5`y?Nnrd*9x_=a)zS zoy_tsIO6rj@7fhbkK!2`M4Q?IQg)dYKM4qvuc`WQ(fYl|T!oU7g9;bf?e>eJ6>gqr@Z&u<$E|mPo9^(F~*@&(naUVL-WK>cS{+fzd3K+lkxcF=cTJO z<4%YE(&9BLeP|qfp<>?Y69GZXY_8VYo@buw5WQqq%hgc1DsDCR>kMh-E`eKFZp3m^S^D;N9L@3xa?3ODb}jX|+aD{Uq(E-Pht@Jig^ zx8;}pSB~WGlxX#tp1o{yOy;+%AD$kqJli`dA$*NZ z=t;kR9N6~dUDLfMXQn(>u{p6sC;S=D<^63(gRaio=rqr%YvPttf#Fe2;c~5=U-4l4sVd6MXo*NOu!lQ#Wg2mIC$SHKb7Ln1Y=(tqyYSzOk8*3V-#0$BqCUIKG#v22zDq&rjw$_mOYmjLMOWGkN z{{>$xHz+>Vea-cMX7o0hSr;?ZwU@|kbP}ETNqWzh_a>)4oILiZo;ik1Vdu)$6CSI+ zax7{3_@(Xf|I>dNcY3vT+@9EJ3Cz~2C9V-A$wjG&C8@e8K!U-@z`#=1&`8(7BE-CMA49&pOTqY z3DsbzYiJl^U}KKAFj>dBv#=86_nJR{Hv2yE4mClk;Bl$^@oS6Y^zleos;^dnHh1R)V08sr0Wa(Q{V uUS@KBo^Ez#Ub-I8;razd`B|ySCB^!AVD0*4wp&0YHG`+CpUXO@geCxCqjY!x literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-right-double-2.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/arrow-right-double-2.png new file mode 100644 index 0000000000000000000000000000000000000000..1fe9feb284002c2c194f74aeb4c1985e12e729d1 GIT binary patch literal 1899 zcmai#c{~*A8po$&--_fU6glM6WmelU(qyumOj)L}H^vy-%wWb?rZ|dg&30xaj@(RK zYa=vbBvM8=LY5@iKG9~2GPdas&iUNW{p-G;_kDlw@_gUtdH#8y)Qe8G3bJal002P2 z-p5?_0)V6F-?Wh~z1NJ157@Hn#ibr~jB-Z~O5`9Kicg+YIxI z{QQk_eTT>JMyhAiu`eU++E6~@bRovEMqk}JM;=a{L$)|x^!apQ|`hzd12n1 zVar8ls+{20(M!y)bBbGJNAq>Uo72rUrW&~ci%pC#jE%)Lb^}|xwKb~E;s+ngnrbD=AXBO})|Vzn9uh~F4^1rg91ETcVewU`kIcWs|wKyc4E}0!WwhSI~KE6h8}n(&fO9m_wVR zqdVtV1te-!mr9XW)SU__r?x=@+=Fz>r8 zLXTaik_Y8Z)iax*RgfpQaf9#s)9|aUCC6cmuVZROn+GxS)>o9mzZrQw3x&7z2h8BX zd7cMV`;I>`5R5iXXXV+vA>QC-76Pw%TD+KhS zIJQ!zc(JNDt9Qm-QtsQf=Gr@CYW0#k@^r|Zf!McPcLz8~K? zpUR|-K^J3?3--#1oK`-x|IBuX3NMzKro}D6{eiz;7q424QE$wr@SCv?qSdiWsyVat} zvaxAx30&8nNPNGcvO2K6U4a2!mGsQx^tKBd|5X~Qho3<77bBRTKB|Agy35|qcd8i- zb}i(WTttYu+8b3?NXmMDJpVaaQZu9ZpI&;i*%&u9IjUjYaJ+-9E{t^{n3Kil(xT`Qx8rEOLOYHRvj{XAXX41K+x#E>3g7;t%Ac%Gqh~Y z(}@^0w7_Vt@79gr*%Zq&=#pQA--ko$x;p44xnKQ3Vr3H$R{F!=ajFRGZu1Uh%?@f| zdxS*ZgyX46661s!?*d+ktnsM8-5!_z ztC7ZW@8?VfySktk&9oFm{K;})jfSK44mI~)r|-Jg@77fuJC9T*Qm^PYmd6%Kv0P8- zt4?4CRr&Gs7ZtjllvG}a6?#{%y~6q(<2R8De17h7(9q+6z$Q%R%~73}-?Zi$ zm5a-69aSjg_6k^=lRFNN@4{DbbCdb+*RtHJFcO)={oNV^cQeO1uz7pKZY|l%Y2VrZ z$ti&S;IRlew(HS_5MGyd%{uDogmOTkaBsddr{xRV%2xmk_3j0Qp5qJW?DUwbJ z#Z{w?S40;^|Gn^ab4L9ga#U0lC14DwyM35+Oa99K-n1H8I!Q?$0Ot)y*EvEo` MYbUGw$Sc?X1Hp?})c^nh literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/document-export-4.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/document-export-4.png new file mode 100644 index 0000000000000000000000000000000000000000..bc76888585fa83ffc6e6acc390fefdbb83ab4316 GIT binary patch literal 1604 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjY)RhkEu-kbUIVD{!X-4)8V`{&-A`TqC4>dog(tgL=H+Q{u!UaBI{ z$k4#y!ou)%=iXwISproP ztvDo-Z`{9szs|t+uZ)9sAKa_+Q9y*z}F5A`2fMUxEZ%2S;rD7z$jqP5$a3HP7Z6gTnOFjvS2{CS5*ikLBg$`1<<$w+l7Ac(lOxl7f=Bg{6nZ zbB%fP7Oh{;Z)i{FPXUc2_J zxq*Set$X+W1*jgsUMXoIc+li*T33s}S(CzZic4Mc^Yxn!Dli-{=qWOq>GOKmzkpng z_V6y5iz`=g?Z1DYMa0R$i!=6mVNp@jzkhWM4oibpst9%7J$v?S`_ZJ2%ek3u?{1V$ zRF>e`_QuxQ+SzXY@dpnSm>L;YUCpYjt*y15>ScSWwANC9BZ5co>$_PNwTpK!Bt(jB zx~X&Mp@Gvv0j;U0Dvf6T(Vps6TU}AHLxhKI?aGy!SF#_xFrVJId+{b6?x#hP7c)fW z6u)`%CL}yOe16#KtE{eptAvGx%T{mb_K{PFdl2LmF^%<{<)1qHvhwncTfeHCnVAI@ z7k@rIYguSoTAJRjOz-wD9-X_z<`hrq5#sw?c_m9MWGk=q{{P#4pN@L;+UHs5vW0&Z z-Td2sQ9;n@fQj#(Yw?Ts#yYDMF}E&uscJ5T_ zj~cr@cW#|JXIJ;<>YX=lHm^(A@JzMuVVC}8-=s(}ucbmFt_S7yN8V+UVQZD&*$t}X`#NKU;Sd_5WL9Ca7y)9jxvMF zQk{kWj>j=8$Q+ARWAI3yB+77|;YZGUv-!Cx^6MB^Fj=rCSj}7gZ4X<+fA*QNm*!Rm zG4lgU4%HIZh?3-@)Wnih-4r0fU}RumscUGcYiJr`Xl`X-WMyowYXD>zwDV8thiL$* zw1VgYYA`g=H89dOG6*p;wlXraGBkx~V6%>CMA49&pOTqY3Dp3!!7#+Y5~RVz5Td~^ zMxhp{Asyr-pUmXcyy8@bjFOT9D}8;iU72O6$@#gt`FX{9`9=0i`}Mu~s?$I%44$rjF6*2UngH#hkgNaz literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/news-subscribe-2.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/news-subscribe-2.png new file mode 100644 index 0000000000000000000000000000000000000000..d5259a76be445872183b39b00889f995f0b6d9d0 GIT binary patch literal 1805 zcma)+c~nzZ9>*Vx7zB~HV4!hOkZ6UFJAX#>2c z%8$v34s=*Q@j!}K-3w<5O7QgU3ylC7VZ+-H}eH_nh+eibm3=*$jJ-wwP^HB zoaVm5n+MA?{1E%CA{GvyHaqCrOz@o&1enbuA2Xby5snHM$DYo}pjWyk!TWCQD+x~J zSoZaW4K#=AjDl^$J9mzLc)hgNZaB8=F*o+$+3>Id6l`SFu=Vib*@d-tzvf+Fxl&Gx zZpJW1wr6-t_<7pry+3>;$G*GM7;(txRaY>e&?AvZ?RF^b^gz0O@hNqjHqSV3dle1Z zFD=T`rJi||Dn-*C4G)LtA?=V?j}KTm%G!JWz@)C7UcZx2p{-VWRVsIwkTl@*i(R`= zew2c1KkyIT==*YUsHng7BBa^TUATK*Y_GPZh&L4A&T_HHY8DXXFlGb z7!N)zl45pLBQiTPE^a57IA=BM=o~CmmD&N2U-`N0YA`#`+qHZ9E&$A9xT>rM*q(Ek zzPBhI&!k(Uo1RZQb>v5)jCd99JvJvtTI*X)0V(=6nMGgLqJ>DQE5oc-N8{_zh@w+46RihUdh%0SZE{ zyvY7COGUNcJ>Sy$bZwn++FWl@U-Bwbi-1vU#CbGJf(&=B%c?f<^?M5{t|RC@{I1oz zCum$Klv36m_IQ8)(!-<^+2+p3&Qv!<`}L?IQ_0K3yk(VvR;I=3x+!C%GN1Mf~LUBTJL&Kv;>*0zGHju;?a*>DZOok&^NAnIj^TzXh5kel$3uX z-dsu3{1&T(PZl3wU85Pw-=~~>80~j8L>8~EgB(DNt3|OS`vjXAU278&Wq5oY+8mTI0DBcwjE_gh`*RbIL1dMtV20UVZWa^&n zvrDU6{+7EW0IrDaCui45sjY5^bAbhcZ=jD05f6X=9Vt#t5|vMi0+Ie6Ve0=d`L$~; z?k!6=7F8`ocd3Pk+{-GLRLcxU#<9zK+eV$LS@F15u4VhUSx$)e%j zvi@t=%B_Mj)zUlK#yLODRGJQOlD0~odDaY~bOe4# z$Ca{W#V;L0lWDK3L22lrg6FxuDaa@@>e<@BC8ssnLZ(de<65_3`u==s7rL{DKKo`Y zLaRCLj-~jySw)Lk9JqEc^ugH=pya(s^sVWWk`}Xh@rYPl!(cFeo0`J7_~Fqy^6>ez z(mfTGL-yyazKE+a{-MKg$E!fwkE8N8nYbez_?pAq=wA{>PfbqsRy8&^v&J87t^J}{ z(VzU``+RVdnjX0+XFNIVL7L?X0(?8@G@4=$d` zWvv%AtY?PJEifZh&?>ndDzcsRip~G_Snj+sJ$4_y0!`R1L2POg6vc+HG4KEo@pv07 zfq*4g1`w>scp@3J!s6i;DH} zFk@nA+`j<010eu!qXa;3qeGvDwKojVm65z+sZat70={Ci6UjK-r(Uti5S7VbFcVnj z%p^K4e#64TMRT#NC>&^x$L(b%?friUFE?K=9MPPBJ9+*v^>fCJc>i?@co0jl2_WFf zmPE3JH5N~TPXQBh*AoWxl^KsSX>3kZ5(IEK9P`+OI2JW35mH90nmcR&n|5rNzD}Y- zz~582bSPo3I~7j<2GukTPapWtbOgoU>+@EXi!ndBv9U_`r@g+l;y-~Az}>~ixydn- F_a_&a9Pj`D literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/run-build-2.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/run-build-2.png new file mode 100644 index 0000000000000000000000000000000000000000..5fea6c540bc908826f0d875e9b490146fcddf973 GIT binary patch literal 2228 zcma);cU05K7RP^5Lz66m1PDbQ7(hb`)eu@j6In_C1qCS~uu%hnP{InK3kn-7VL>Sg zPlF^LnkWj0NHeR5EFA(+vJ2^F z0Qhx30HkIDz#2G|`WFDip#k9UU;w}s0e~vKq{#~pPDq6wadrSLSXPP6DWH*|yZAG} z&?DP*7r~&Ixjh(9w~qkrcOd}Njb8zxw8IYeJ}#o)e7(+iKT)wZ`8-+A)kLu8i}b=~ zf-`t&vif@ZF8c#JFPzf1S?h?GJ5ul|ZE*Q|f$pb)NZZQHwey48nVtvK&9bCk2*p(N z?Z+-x<%B6AsR}n_Mg^MbSN!TUtrlAn7kj>lU%pHdoPGF~{kH7vVo9G`Ix4L>VNpsv z%}&g&9x6u2Da;~FfA+rVuF~qZX+Cj3TAbt>(DJJaK!5v8EufXPq0w7reE@cDpL_?@ zgKHDm693`&;#hseF5q6&)H#llqP9&^wnw5B3=Z$CI&p8^#Kh#|*x1&PNj|K#N6hpShwmWS3ct)P&k7CwLWir>NEDxndO%3#OhN}tWfWUy!SI;NKd z$={Zf5ry3wtkBTcbE`=Y>F@gaAND{AV$S(m8?jrP8&fp`K_AY_D#g!0*@k-Rl5gy*p-cvV|+=H+3{PIk~02v2iu8 zf~r0TzgF?iDq&%e&v{isgruFiD@y=s79{oMIV63h^QKMJas#IO_&%pkpY9FAUbPil zkq1V@cex6@gMy|e`}=P@`}(><(l|;L)PH&!@JNDGsF*0u%c!RO6lW>`ody+appj{` z9yJXO4K+>84Vn!%NE&GJ=hf6ieO&!K*ZMrXUZZ+GoxyhHr^htPb9=L|T~n_V%KCau zkBBb`%?t{14UP>zYLx80U0Yl0Sz28DZRp*=P1SoR`+ABke)`R+Z(x9E9Q1JgZbilZ zR@yinE?q%wU;IjRJ%9I;lIxHopv3Z!xV!1Z^tF}0E}PvCR}eA2yIU)zA+;gDd-(>v>T2D*kUA{2PXe!{2t100>t-%BSI-9> zKmLMGe=eYn^J0%gp&vI?pr0AJi8=|?-R_PV90{LM-d<_*rvT)^NfeCys-z?@8QETC?R;<97YCn8dny`{MQA$(^5u+c1hlz)V(t4k;>O9 z2qP`yIU~cvVRe3{3LSnS3JMAbZr!>S^sKiR;p_Wy&+hCDfG0FM*qwe%E_9QsUcr=U zgIG$3^`(_gW)aW2mp|4{70rzOZByV%c`Op8U~Fw|i+aLE1-YuIa79hr7b`*zYq~v4 z6Fljg7~m;P+*=KQjm=&W6|s~Evt4jVfu}#bG-w05c=x>+zXri#=!eF~FDo9a4p`-L z`isiS&h-xsNdhiODr%5D7eo0@cD z1Eu-O;AZ7p^)Hp@}{d{xq?cC>8d=x%q{t3y+ zUgqTmi>`@Gj#3_GWrO8$;>7sZ8=MrWrF*oKcWP@dmX(#U9vxB@3ey?J{qOD=W?p=Q zB+is{#05h31^fSAQe13uYe)zH-M2dj2M062Ur6Gd^O5~ma;>ecxnjK1(o$_VcXu|Q zB(TlunEpp#V4$`Zo>^OPab)WXR4iZH_9XMmUwuJGj~zoO!Y!AoI+J0G1l`}ZT5V}F9^_s@cr2WuQdg_1=P>|Ue#X(b@LNx*Rl?q_poP|53EV^7wMLfP zx1D~hqhBmJh7uA>L56`1fJLJZAu$*v#>@v}fkR_)CKgCE_<_z=6bN9@ zNHi9SMf+e)a9DF3#&QRUcyA5ft2+_C1GK0xYIx#*05Af^2YqN8FvaWu?g2WYPUPdsKQ!*h`=2bJEsL5b8kOa_I$hgE~m0|4>g=^$T{+mBTwBqSI|efLIEx4pkFW6?s`4I;o{M|X#6 ILU8)O0m?46@c;k- literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler.qrc b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler.qrc new file mode 100644 index 000000000..8d31abddc --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler.qrc @@ -0,0 +1,11 @@ + + + images/arrow-left-2.png + images/arrow-left-double-2.png + images/arrow-right-2.png + images/arrow-right-double-2.png + images/document-export-4.png + images/news-subscribe-2.png + images/run-build-2.png + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index dcf17ba34..b233ce922 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -25,6 +25,7 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : m_undoStack = new QUndoStack(this); // Populate the "all" primitives box. + QStringList list; std::vector paths; NLMISC::CPath::getFileList("primitive", paths); @@ -32,9 +33,31 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : while( itr != paths.end() ) { const char *path2 = (*itr).c_str(); - ui->allPrimitivesList->insertItem(0,path2); + list << path2; ++itr; } + + m_regexpFilter = new QRegExp(); + m_regexpFilter->setPatternSyntax(QRegExp::FixedString); + m_regexpFilter->setCaseSensitivity(Qt::CaseInsensitive); + + m_allPrimitivesModel = new QStringListModel(list, this); + m_filteredProxyModel = new QSortFilterProxyModel(this); + m_filteredProxyModel->setSourceModel(m_allPrimitivesModel); + m_filteredProxyModel->setDynamicSortFilter(true); + m_filteredProxyModel->setFilterRegExp(*m_regexpFilter); + ui->allPrimitivesList->setModel(m_filteredProxyModel); + m_selectedPrimitivesModel = new QStringListModel(this); + ui->selectedPrimitivesList->setModel(m_selectedPrimitivesModel); + + connect(ui->filterEdit, SIGNAL(textEdited(const QString&)), this, SLOT(handleFilterChanged(const QString&))); + +} + +void MissionCompilerMainWindow::handleFilterChanged(const QString &text) +{ + m_regexpFilter->setPattern(text); + m_filteredProxyModel->setFilterRegExp(*m_regexpFilter); } void MissionCompilerMainWindow::loadConfig() { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h index 9e94ea9fb..388b5c57d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include namespace Ui { class MissionCompilerMainWindow; @@ -24,6 +26,8 @@ public: void saveConfig(); QUndoStack *getUndoStack() { return m_undoStack; } +public Q_SLOTS: + void handleFilterChanged(const QString &text); private: Ui::MissionCompilerMainWindow *ui; @@ -31,6 +35,9 @@ private: QMenu *_toolModeMenu; QUndoStack *m_undoStack; QStringListModel *m_allPrimitivesModel; + QStringListModel *m_selectedPrimitivesModel; + QSortFilterProxyModel *m_filteredProxyModel; + QRegExp *m_regexpFilter; }; #endif // MISSION_COMPILER_MAIN_WINDOW_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui index 30756f91c..865cb20d2 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui @@ -15,7 +15,7 @@ - + 0 @@ -26,17 +26,14 @@ 0 0 776 - 398 + 426 Mission Compiler Options - - - - + @@ -53,15 +50,29 @@ + + Add Selected + - >> + + + + + :/buttons/images/arrow-right-2.png:/buttons/images/arrow-right-2.png + + Add All + - ALL >> + + + + + :/buttons/images/arrow-right-double-2.png:/buttons/images/arrow-right-double-2.png @@ -80,15 +91,29 @@ + + Remove All + - << ALL + + + + + :/buttons/images/arrow-left-double-2.png:/buttons/images/arrow-left-double-2.png + + Remove Selected + - << + + + + + :/buttons/images/arrow-left-2.png:/buttons/images/arrow-left-2.png @@ -107,8 +132,87 @@ + + + + true + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + true + + + QAbstractItemView::ExtendedSelection + + + + + + + true + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::DragDrop + + + Qt::MoveAction + + + true + + + QAbstractItemView::ExtendedSelection + + + - + + + All Primitives + + + + + + + Selected Primitives + + + + + + + + + Filter + + + + + + + type filter here + + + + @@ -128,7 +232,7 @@ 0 0 776 - 398 + 426 @@ -146,36 +250,6 @@ - - - - Actions - - - - - - Validate - - - - - - - Compile - - - - - - - Compile and Publish - - - - - - @@ -189,7 +263,59 @@ + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + + :/buttons/images/run-build-2.png:/buttons/images/run-build-2.png + + + Compile + + + Compile + + + + + + :/buttons/images/news-subscribe-2.png:/buttons/images/news-subscribe-2.png + + + Validate + + + Validate + + + + + + :/buttons/images/document-export-4.png:/buttons/images/document-export-4.png + + + Publish + + + Compile and Publish + + - + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp index cfea63959..005140391 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp @@ -6,7 +6,8 @@ #include "../../extension_system/iplugin_spec.h" // NeL includes -#include "nel/misc/debug.h" +#include +#include // Qt includes #include @@ -58,12 +59,12 @@ void MissionCompilerPlugin::extensionsInitialized() //zoneMenu->addAction(exampleAction2); // Initialize Ligo. - settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - QString ligoConfigFile = settings->value(Core::Constants::DATA_PATH_SECTION).toString(); - settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + //QString ligoConfigFile = settings->value(Core::Constants::DATA_PATH_SECTION).toString(); + //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); NLLIGO::Register(); - LigoConfig.readPrimitiveClass(ligoConfigFile.toAscii().data(), false); + LigoConfig.readPrimitiveClass(NLMISC::CPath::lookup("world_editor_classes.xml").c_str(), false); NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; } From 9e3a19e654376a70ce103dc2687dbeec95c81bb4 Mon Sep 17 00:00:00 2001 From: sfb Date: Thu, 2 Jun 2011 13:47:31 -0500 Subject: [PATCH 014/215] Changed: Implemented mission validation. --HG-- branch : branch-mission-compiler-qt --- .../mission_compiler_main_window.cpp | 114 +++++++++++++++ .../mission_compiler_main_window.h | 14 ++ .../mission_compiler_plugin.cpp | 4 - .../mission_compiler_plugin.h | 4 - .../mission_compiler/validation_file.cpp | 133 ++++++++++++++++++ .../mission_compiler/validation_file.h | 51 +++++++ 6 files changed, 312 insertions(+), 8 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/validation_file.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/validation_file.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index b233ce922..0cbb1e708 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -1,17 +1,24 @@ #include "mission_compiler_main_window.h" #include "ui_mission_compiler_main_window.h" +#include "validation_file.h" #include #include #include #include #include +#include #include "../core/icore.h" #include "../core/imenu_manager.h" #include "../core/core_constants.h" +#include + #include +#include +#include +#include MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : QMainWindow(parent), @@ -19,6 +26,9 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : { ui->setupUi(this); + m_compileLog = ""; + updateCompileLog(); + // Load the settings. loadConfig(); @@ -51,7 +61,11 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : ui->selectedPrimitivesList->setModel(m_selectedPrimitivesModel); connect(ui->filterEdit, SIGNAL(textEdited(const QString&)), this, SLOT(handleFilterChanged(const QString&))); + connect(ui->actionValidate, SIGNAL(triggered()), this, SLOT(handleValidation())); + NLLIGO::Register(); + m_ligoConfig.readPrimitiveClass(NLMISC::CPath::lookup("world_editor_classes.xml").c_str(), false); + NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &m_ligoConfig; } void MissionCompilerMainWindow::handleFilterChanged(const QString &text) @@ -60,6 +74,106 @@ void MissionCompilerMainWindow::handleFilterChanged(const QString &text) m_filteredProxyModel->setFilterRegExp(*m_regexpFilter); } +void MissionCompilerMainWindow::handleValidation() +{ + // First switch toolbox pages to show the compilation output. + ui->toolBox->setCurrentIndex(2); + + m_compileLog.append("Begin mission validation.\n"); + updateCompileLog(); + + // Load existing validation + CValidationFile validation; + validation.loadMissionValidationFile("mission_validation.cfg"); + + // Go through each file. + QStringList list = m_selectedPrimitivesModel->stringList(); + QStringListIterator itr(list); + while(itr.hasNext()) + { + QString filename = itr.next(); + //QString filePath = NLMISC::CPath::lookup(filename.toAscii().data(), false).c_str(); + m_compileLog.append("Parsing '"+filename+"'...\n"); + updateCompileLog(); + + TMissionContainer missions; + NLLIGO::CPrimitives primDoc; + NLLIGO::CPrimitiveContext::instance().CurrentPrimitive = &primDoc; + NLLIGO::loadXmlPrimitiveFile(primDoc, NLMISC::CPath::lookup(filename.toAscii().data(), false), m_ligoConfig); + parsePrimForMissions(primDoc.RootNode, missions); + + // Parse missions to check modification + std::map::iterator itMission, itMissionEnd = missions.end(); + for (itMission=missions.begin(); itMission!=itMissionEnd; ++itMission) + { + CValidationFile::TMissionStateContainer::iterator itMissionValidation = validation._MissionStates.find(itMission->first); + if (itMissionValidation!=validation._MissionStates.end()) + { + // Mission already registered, check hash key + if (itMissionValidation->second.hashKey!=itMission->second.hashKey) + { + itMissionValidation->second.hashKey = itMission->second.hashKey; + itMissionValidation->second.state = validation.defaultState(); + } + } + else + { + // New mission + validation.insertMission(itMission->first, itMission->second.hashKey); + } + m_compileLog.append("Mission: '"+QString(itMission->first.c_str())+"->"+QString(itMission->second.hashKey.c_str())+"\n"); + updateCompileLog(); + } + } + validation.saveMissionValidationFile("mission_validation.cfg"); + + m_compileLog.append("Validation finished"); + updateCompileLog(); +} + +bool MissionCompilerMainWindow::parsePrimForMissions(NLLIGO::IPrimitive const *prim, TMissionContainer &missions) +{ + std::string value; + // if the node is a mission parse it + if (prim->getPropertyByName("class",value) && !stricmp(value.c_str(),"mission") ) + { + std::string name; + prim->getPropertyByName("name",name); + + m_compileLog.append(" ** Parsing mission '"+QString(name.c_str())+"'\n"); + updateCompileLog(); + + // parse the mission and put it in our manager + CMission mission(value, ""); + if (!mission.parsePrim(prim) ) + { + m_compileLog.append(" ** Previous errors in mission '"+QString(name.c_str())+"'"); + updateCompileLog(); + return false; + } + missions.insert(make_pair(name, mission)); + return true; + } + else + { + //this is not a mission node, so lookup recursively in the children + bool ok = true; + for (uint i=0;igetNumChildren();++i) + { + const NLLIGO::IPrimitive *child; + if ( !prim->getChild(child,i) || !parsePrimForMissions(child, missions) ) + ok = false; + } + return ok; + } +} + +void MissionCompilerMainWindow::updateCompileLog() +{ + ui->compileOutputText->setPlainText(m_compileLog); + QCoreApplication::processEvents(); +} + void MissionCompilerMainWindow::loadConfig() { QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("MissionCompiler"); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h index 388b5c57d..de8328dee 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h @@ -10,10 +10,15 @@ #include #include +#include +#include + namespace Ui { class MissionCompilerMainWindow; } +struct CMission; + class MissionCompilerMainWindow : public QMainWindow { Q_OBJECT @@ -26,18 +31,27 @@ public: void saveConfig(); QUndoStack *getUndoStack() { return m_undoStack; } + typedef std::map TMissionContainer; + public Q_SLOTS: void handleFilterChanged(const QString &text); + void handleValidation(); private: Ui::MissionCompilerMainWindow *ui; + void updateCompileLog(); + bool parsePrimForMissions(NLLIGO::IPrimitive const *prim, TMissionContainer &missions); + QMenu *_toolModeMenu; QUndoStack *m_undoStack; QStringListModel *m_allPrimitivesModel; QStringListModel *m_selectedPrimitivesModel; QSortFilterProxyModel *m_filteredProxyModel; QRegExp *m_regexpFilter; + QString m_compileLog; + + NLLIGO::CLigoConfig m_ligoConfig; }; #endif // MISSION_COMPILER_MAIN_WINDOW_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp index 005140391..7bd06de91 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp @@ -62,10 +62,6 @@ void MissionCompilerPlugin::extensionsInitialized() //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); //QString ligoConfigFile = settings->value(Core::Constants::DATA_PATH_SECTION).toString(); //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - - NLLIGO::Register(); - LigoConfig.readPrimitiveClass(NLMISC::CPath::lookup("world_editor_classes.xml").c_str(), false); - NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; } void MissionCompilerPlugin::setNelContext(NLMISC::INelContext *nelContext) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h index a9d34bd13..773c984ea 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h @@ -9,8 +9,6 @@ // NeL includes #include #include -#include -#include // Qt includes #include @@ -53,8 +51,6 @@ public: QObject *objectByName(const QString &name) const; ExtensionSystem::IPluginSpec *pluginByName(const QString &name) const; - NLLIGO::CLigoConfig LigoConfig; - protected: NLMISC::CLibraryContext *_LibContext; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/validation_file.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/validation_file.cpp new file mode 100644 index 000000000..3e166da6b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/validation_file.cpp @@ -0,0 +1,133 @@ +#include "validation_file.h" + +#include +#include + +void CValidationFile::loadMissionValidationFile(std::string filename) +{ + // load the configuration file + NLMISC::CConfigFile cf; + std::string pathName = NLMISC::CPath::lookup(filename, false); + + if (pathName.empty()) + { + nlwarning("Can't find index file '%s' in search path, no mission will be valid", filename.c_str()); + return; + } + cf.load(pathName); + + // get the variable + NLMISC::CConfigFile::CVar* var = cf.getVarPtr("AuthorizedStates"); + if (var) + { + for (uint i=0; isize(); ++i) + _AuthorizedStates.push_back(var->asString(i)); + } + int missionStatesFields = 3; + var = cf.getVarPtr("MissionStatesFields"); + if (var) + missionStatesFields = var->asInt(); + else + nlwarning("Mission validation file does not contain MissionStatesFields variable. Parsing may fail and corrupt data."); + + var = cf.getVarPtr("MissionStates"); + if (var) + { + for (uint i=0; isize()/missionStatesFields; ++i) + { + std::string mission = var->asString(i*missionStatesFields); + std::string stateName = var->asString(i*missionStatesFields+1); + std::string hashKey = var->asString(i*missionStatesFields+2); + _MissionStates.insert(std::make_pair(mission, CMissionState(mission, stateName, hashKey))); + } + } +} + +void CValidationFile::saveMissionValidationFile(std::string filename) +{ + // load the configuration file + std::string pathName = NLMISC::CPath::lookup(filename, false); + + if (pathName.empty()) + { + nlwarning("Can't find index file '%s' in search path, no mission will be valid", filename.c_str()); + return; + } + FILE* file = fopen(pathName.c_str(), "w"); + nlassert(file!=NULL); + + // AuthorizedStates + fprintf(file, "%s", + "// AuthorizedStates contains the list of authorized states. EGS mission\n" + "// manager can accept any number of states. Default state is the first one.\n" + "AuthorizedStates = {\n"); + std::deque::iterator itAuth, itAuthEnd = _AuthorizedStates.end(); + for (itAuth=_AuthorizedStates.begin(); itAuth!=itAuthEnd; ++itAuth) + fprintf(file, "\t\"%s\",\n", itAuth->c_str()); + fprintf(file, "%s", "};\n\n"); + + // MissionStatesFields + fprintf(file, "%s", + "// MissionStatesFields contains the number of fields in MissionStates, for\n" + "// future compatibility purpose.\n" + "MissionStatesFields = "); + fprintf(file, "%d", 3); // 3 fields: name, state, hash key + fprintf(file, "%s", ";\n\n"); + + // MissionStates + fprintf(file, "%s", + "// MissionStates contains a list of mission with for each the state of the\n" + "// mission and its hash key. The tool will add new missions with the default\n" + "// state. It will flag missions with a modified hash key with default state to\n" + "// prevent untested modified missions to be published.\n" + "// :NOTE: You can add a field to this structure without the need to modify EGS\n" + "// code. Simply update MissionStatesFields.\n" + "MissionStates = {\n"); + TMissionStateContainer::iterator itMission, itMissionEnd = _MissionStates.end(); + for (itMission=_MissionStates.begin(); itMission!=itMissionEnd; ++itMission) + fprintf(file, "\t%-42s %-12s \"%s\",\n", ("\""+itMission->second.name+"\",").c_str(), ("\""+itMission->second.state+"\",").c_str(), itMission->second.hashKey.c_str()); + fprintf(file, "};\n\n"); + + fclose(file); +} + +// :NOTE: This function exists in mission_template.cpp. If you change it here modify the other file. +std::string buildHashKey(std::string const& content) +{ + uint32 sum = 0; + size_t size = content.length()/4; + for (size_t i=0; i>1 | 0x80000000; + else + sum = sum>>1; + } + return NLMISC::toString("0x%08X", sum); +} + +bool CMission::parsePrim(NLLIGO::IPrimitive const* prim) +{ + // init default values + std::vector* params; + // get the mission script + if (!prim->getPropertyByName("script", params) || !params) + { + nlwarning("ERROR : cant find mission script!!!!!!"); + return false; + } + + // parse them + std::string content; + std::vector::iterator itParam, itParamEnd = params->end(); + for (itParam=params->begin(); itParam!=itParamEnd; ++itParam) + { + content += *itParam + "\n"; + } + hashKey = buildHashKey(content); + return true; +} \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/validation_file.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/validation_file.h new file mode 100644 index 000000000..73d661a26 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/validation_file.h @@ -0,0 +1,51 @@ +#ifndef VALIDATION_FILE_H +#define VALIDATION_FILE_H + +#include +#include +#include + +#include + +struct CMissionState +{ + std::string name; + std::string state; + std::string hashKey; + CMissionState(std::string _name, std::string _state, std::string _hashKey) + : name(_name), state(_state), hashKey(_hashKey) { } +}; + +struct CMission +{ + std::string name; + std::string hashKey; + CMission(std::string _name, std::string _hashKey) + : name(_name), hashKey(_hashKey) { } + bool parsePrim(NLLIGO::IPrimitive const* prim); +}; + +class CValidationFile +{ +public: + typedef std::map TMissionStateContainer; + std::deque _AuthorizedStates; + TMissionStateContainer _MissionStates; +public: + // CValidationFile() { } + void loadMissionValidationFile(std::string filename); + void saveMissionValidationFile(std::string filename); + void insertMission(std::string const& mission, std::string const& hashKey) + { + _MissionStates.insert(std::make_pair(mission, CMissionState(mission, defaultState(), hashKey))); + } + std::string defaultState() + { + if (!_AuthorizedStates.empty()) + return _AuthorizedStates.front(); + else + return ""; + } +}; + +#endif // VALIDATION_FILE_H \ No newline at end of file From f7ebb89d94cda8082b013ca5f7d31ece13207598 Mon Sep 17 00:00:00 2001 From: sfb Date: Thu, 2 Jun 2011 15:15:12 -0500 Subject: [PATCH 015/215] Changed: Implemented basic mission compilation without publishing. --HG-- branch : branch-mission-compiler-qt --- .../src/plugins/CMakeLists.txt | 6 +- .../plugins/mission_compiler/CMakeLists.txt | 8 +- .../mission_compiler_main_window.cpp | 77 ++++++++++++++++++- .../mission_compiler_main_window.h | 3 + 4 files changed, 91 insertions(+), 3 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt index c5d49418d..66cbdb188 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt @@ -7,4 +7,8 @@ ADD_SUBDIRECTORY(disp_sheet_id) ADD_SUBDIRECTORY(object_viewer) ADD_SUBDIRECTORY(zone_painter) ADD_SUBDIRECTORY(georges_editor) -ADD_SUBDIRECTORY(mission_compiler) \ No newline at end of file + +# Ryzom Specific Plugins +IF(WITH_RYZOM AND WITH_RYZOM_TOOLS) + ADD_SUBDIRECTORY(mission_compiler) +ENDIF(WITH_RYZOM AND WITH_RYZOM_TOOLS) \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt index af39fc18a..4d11e808e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt @@ -28,9 +28,15 @@ SOURCE_GROUP(QtGeneratedMocSrc FILES ${OVQT_PLUG_MISSION_COMPILER_MOC_SRC} ${OVQ SOURCE_GROUP("Mission Compiler Plugin" FILES ${SRC}) SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) +# Mission Compiler Library +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/ryzom/tools/leveldesign/mission_compiler_lib) + +# Game Share Library +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/common/src) + ADD_LIBRARY(ovqt_plugin_mission_compiler MODULE ${SRC} ${OVQT_PLUG_MISSION_COMPILER_MOC_SRC} ${OVQT_PLUG_MISSION_COMPILER_RC_SRCS} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_MISSION_COMPILER_UI_HDRS}) -TARGET_LINK_LIBRARIES(ovqt_plugin_mission_compiler ovqt_plugin_core nelmisc nelligo ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY}) +TARGET_LINK_LIBRARIES(ovqt_plugin_mission_compiler ovqt_plugin_core nelmisc nelligo ryzom_mission_compiler_lib ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY}) NL_DEFAULT_PROPS(ovqt_plugin_mission_compiler "NeL, Tools, 3D: Object Viewer Qt Plugin: Mission Compiler") NL_ADD_RUNTIME_FLAGS(ovqt_plugin_mission_compiler) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index 0cbb1e708..e760d9f94 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -1,6 +1,7 @@ #include "mission_compiler_main_window.h" #include "ui_mission_compiler_main_window.h" #include "validation_file.h" +#include "mission_compiler.h" #include #include @@ -62,6 +63,8 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : connect(ui->filterEdit, SIGNAL(textEdited(const QString&)), this, SLOT(handleFilterChanged(const QString&))); connect(ui->actionValidate, SIGNAL(triggered()), this, SLOT(handleValidation())); + connect(ui->actionCompile, SIGNAL(triggered()), this, SLOT(handleCompile())); + connect(ui->actionPublish, SIGNAL(triggered()), this, SLOT(handlePublish())); NLLIGO::Register(); m_ligoConfig.readPrimitiveClass(NLMISC::CPath::lookup("world_editor_classes.xml").c_str(), false); @@ -74,6 +77,79 @@ void MissionCompilerMainWindow::handleFilterChanged(const QString &text) m_filteredProxyModel->setFilterRegExp(*m_regexpFilter); } +void MissionCompilerMainWindow::handleCompile() +{ + compileMission(); +} + +void MissionCompilerMainWindow::handlePublish() +{ + compileMission(true); +} + +void MissionCompilerMainWindow::compileMission(bool publish) +{ + uint nbMission = 0; + + // First switch toolbox pages to show the compilation output. + ui->toolBox->setCurrentIndex(2); + + m_compileLog.append("Begin mission compilation.\n"); + updateCompileLog(); + + // Go through each file. + QStringList list = m_selectedPrimitivesModel->stringList(); + QStringListIterator itr(list); + while(itr.hasNext()) + { + QString filename = itr.next(); + m_compileLog.append("Compiling '"+filename+"'...\n"); + updateCompileLog(); + + NLLIGO::CPrimitives primDoc; + NLLIGO::CPrimitiveContext::instance().CurrentPrimitive = &primDoc; + NLLIGO::loadXmlPrimitiveFile(primDoc, NLMISC::CPath::lookup(filename.toAscii().data(), false), m_ligoConfig); + NLLIGO::CPrimitiveContext::instance().CurrentPrimitive = NULL; + + try + { + CMissionCompiler mc; + mc.compileMissions(primDoc.RootNode, filename.toStdString()); + m_compileLog.append("Found "+QString::number(mc.getMissionsCount())+" valid missions\n"); + updateCompileLog(); + + mc.installCompiledMission(m_ligoConfig, filename.toStdString()); + nbMission += mc.getMissionsCount(); + + // publish files to selected servers + //if (publish) + //for (uint i=0 ; i Date: Tue, 7 Jun 2011 14:42:00 +0200 Subject: [PATCH 016/215] Changed: #1304: Updating CMissionGuild for guild missions. Implementation of the functions: - updateUsersJournalEntry - clearUsersJournalEntry --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.h | 5 ++ .../mission_manager/mission_guild.cpp | 69 ++++++++++++++++++- .../mission_manager/mission_manager.cpp | 4 ++ .../mission_manager/missions_commands.cpp | 14 +++- 4 files changed, 88 insertions(+), 4 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index e740540ca..3f3b04eb6 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -179,6 +179,11 @@ public: void removeMission(CMissionGuild * mission, TMissionResult result); void addSuccessfulMission(CMissionTemplate * templ); bool processMissionEvent( CMissionEvent & event, TAIAlias alias = CAIAliasTranslator::Invalid); + ///\return the mission + inline std::vector & getMissions() + { + // To Do + } //@} /// inventory management diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.cpp index d3bb2c106..63b74e997 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.cpp @@ -25,18 +25,83 @@ #include "player_manager/player.h" #include "player_manager/character.h" +using namespace std; +using namespace NLMISC; + NL_INSTANCE_COUNTER_IMPL(CMissionGuild); //---------------------------------------------------------------------------- void CMissionGuild::updateUsersJournalEntry() { - /// todo guild mission + CGuild * guild = CGuildManager::getInstance()->getGuildFromId( _GuildId ); + if (!guild) + { + nlwarning( "cant find guild ID : %d", _GuildId ); + return; + } + + for ( std::map::iterator it = guild->getMembersBegin(); + it != guild->getMembersEnd();++it ) + { + CCharacter * user = PlayerManager.getChar( it->first ); + if ( !user ) + { + nlwarning( "cant find user %s", it->first.toString().c_str() ); + continue; + } + updateUserJournalEntry(*user,"GROUP:"); + } } //---------------------------------------------------------------------------- void CMissionGuild::clearUsersJournalEntry() { - /// todo guild mission + CGuild * guild = CGuildManager::getInstance()->getGuildFromId( _GuildId ); + if (!guild) + { + nlwarning( "cant find guild ID : %d", _GuildId ); + return; + } + + for ( std::map::iterator it = guild->getMembersBegin(); + it != guild->getMembersEnd();++it ) + { + CCharacter * user = PlayerManager.getChar( it->first ); + if ( !user ) + { + nlwarning( "cant find user %s", it->first.toString().c_str() ); + continue; + } + + CBankAccessor_PLR::TGROUP::TMISSIONS::TArray &missionItem = CBankAccessor_PLR::getGROUP().getMISSIONS().getArray(_ClientIndex); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:TYPE",_ClientIndex), 0); + missionItem.setTYPE(user->_PropertyDatabase, 0); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:ICON",_ClientIndex), 0); + missionItem.setICON(user->_PropertyDatabase, CSheetId::Unknown); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:TITLE",_ClientIndex), 0); + missionItem.setTITLE(user->_PropertyDatabase, 0); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:DETAIL_TEXT",_ClientIndex), 0); + missionItem.setDETAIL_TEXT(user->_PropertyDatabase, 0); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:END_DATE",_ClientIndex), 0 ); + missionItem.setEND_DATE(user->_PropertyDatabase, 0); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:BEGIN_DATE",_ClientIndex), 0 ); + missionItem.setBEGIN_DATE(user->_PropertyDatabase, 0); + for (uint i = 0; i < NB_JOURNAL_COORDS; i++) + { + CBankAccessor_PLR::TGROUP::TMISSIONS::TArray::TTARGET &targetItem = missionItem.getTARGET(i); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:TARGET%u:TITLE",_ClientIndex,i), 0); + targetItem.setTITLE(user->_PropertyDatabase, 0); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:TARGET%u:X",_ClientIndex,i), 0); + targetItem.setX(user->_PropertyDatabase, 0); + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:TARGET%u:Y",_ClientIndex,i), 0); + targetItem.setY(user->_PropertyDatabase, 0); + } + for (uint i = 0; i < NB_STEP_PER_MISSION; i++) + { + // user->_PropertyDatabase.setProp( NLMISC::toString( "GROUP:MISSIONS:%u:GOALS:%u:TEXT",_ClientIndex,i), 0); + missionItem.getGOALS().getArray(i).setTEXT(user->_PropertyDatabase, 0); + } + } } diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp index b0bd41ffb..55a08ef4f 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp @@ -846,6 +846,10 @@ void CMissionManager::instanciateMission(CCharacter* user,TAIAlias alias, TAIAl inst = module->pickMission( templ->Alias ); if (!inst) return; + + /// /!\ Do the same thing that the team missions but with the loop: for ( uint i = MaxGroupMissionCount; i < MaxGroupMissionCount + MaxGuildMissionCount; i++ ) + /// Instead of for ( uint i = 0; i < MaxGroupMissionCount; i++ ), so that we use available space for guild missions + /// todo guild mission : see solo /* todo guild mission : implement that in module diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/missions_commands.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/missions_commands.cpp index 78229d3a8..a5f0f863f 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/missions_commands.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/missions_commands.cpp @@ -30,7 +30,9 @@ #include "team_manager/team_manager.h" #include "mission_manager/mission_team.h" #include "mission_manager/mission_step_ai.h" +#include "mission_manager/mission_guild.h" #include "guild_manager/guild_manager.h" +#include "guild_manager/guild.h" #include "admin.h" #include "creature_manager/creature_manager.h" @@ -81,10 +83,18 @@ NLMISC_COMMAND(forceJournalUpdate,"force mission journal update","getMissions()[i]->updateUsersJournalEntry(); } } - for (uint i = 0; i < MaxGuildMissionCount; i++) + CGuild * guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); + if (guild) + { + for ( uint i = 0; i < guild->getMissions().size(); i++ ) + { + guild->getMissions()[i]->updateUsersJournalEntry(); + } + } + /*for (uint i = 0; i < MaxGuildMissionCount; i++) { /// todo guild mission - } + }*/ return true; } // missionProgress // From 21db83a0ed8280d1a4ce343867cad7dde7da6a73 Mon Sep 17 00:00:00 2001 From: sfb Date: Tue, 7 Jun 2011 12:14:10 -0500 Subject: [PATCH 017/215] Changed: Added 'data directory' box and re-arranged filters. --HG-- branch : branch-mission-compiler-qt --- .../mission_compiler_main_window.cpp | 82 +++++++++++++++--- .../mission_compiler_main_window.h | 5 ++ .../mission_compiler_main_window.ui | 86 ++++++++++++++----- 3 files changed, 137 insertions(+), 36 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index e760d9f94..f7da41ff2 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include #include "../core/icore.h" #include "../core/imenu_manager.h" @@ -27,6 +29,7 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : { ui->setupUi(this); + m_lastDir = "."; m_compileLog = ""; updateCompileLog(); @@ -35,24 +38,11 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : m_undoStack = new QUndoStack(this); - // Populate the "all" primitives box. - QStringList list; - std::vector paths; - NLMISC::CPath::getFileList("primitive", paths); - - std::vector::iterator itr = paths.begin(); - while( itr != paths.end() ) - { - const char *path2 = (*itr).c_str(); - list << path2; - ++itr; - } - m_regexpFilter = new QRegExp(); m_regexpFilter->setPatternSyntax(QRegExp::FixedString); m_regexpFilter->setCaseSensitivity(Qt::CaseInsensitive); - m_allPrimitivesModel = new QStringListModel(list, this); + m_allPrimitivesModel = new QStringListModel(this); m_filteredProxyModel = new QSortFilterProxyModel(this); m_filteredProxyModel->setSourceModel(m_allPrimitivesModel); m_filteredProxyModel->setDynamicSortFilter(true); @@ -65,12 +55,63 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : connect(ui->actionValidate, SIGNAL(triggered()), this, SLOT(handleValidation())); connect(ui->actionCompile, SIGNAL(triggered()), this, SLOT(handleCompile())); connect(ui->actionPublish, SIGNAL(triggered()), this, SLOT(handlePublish())); + connect(ui->allPrimitivesList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(handleAllDoubleClick(const QModelIndex &))); + connect(ui->dataDirButton, SIGNAL(clicked()), this, SLOT(handleDataDirButton())); + connect(ui->dataDirEdit, SIGNAL(textChanged(const QString &)), this, SLOT(handleDataDirChanged(const QString &))); + + // Set the default data dir to the primitives path. + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + m_lastDir = settings->value(Core::Constants::PRIMITIVES_PATH).toString(); + ui->dataDirEdit->setText(m_lastDir); + populateAllPrimitives(m_lastDir); + settings->endGroup(); NLLIGO::Register(); m_ligoConfig.readPrimitiveClass(NLMISC::CPath::lookup("world_editor_classes.xml").c_str(), false); NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &m_ligoConfig; } +void MissionCompilerMainWindow::populateAllPrimitives(const QString &dataDir) +{ + // First we need to clear out the models entirely. + QStringList emptyList; + m_selectedPrimitivesModel->setStringList(emptyList); + m_allPrimitivesModel->setStringList(emptyList); + + + // Populate the "all" primitives box. + QStringList list; + + // Filter for only primitive files. + QStringList filters; + filters << "*.primitive"; + + QDirIterator it(dataDir, filters, QDir::Files, QDirIterator::Subdirectories|QDirIterator::FollowSymlinks); + while(it.hasNext()) + { + it.next(); + list << it.fileName(); + } + + m_allPrimitivesModel->setStringList(list); +} +void MissionCompilerMainWindow::handleDataDirChanged(const QString &text) +{ + populateAllPrimitives(text); +} + +void MissionCompilerMainWindow::handleDataDirButton() +{ + QString newPath = QFileDialog::getExistingDirectory(this, "", m_lastDir); + if(!newPath.isEmpty()) + { + ui->dataDirEdit->setText(newPath); + m_lastDir = newPath; + populateAllPrimitives(newPath); + } +} + void MissionCompilerMainWindow::handleFilterChanged(const QString &text) { m_regexpFilter->setPattern(text); @@ -87,6 +128,19 @@ void MissionCompilerMainWindow::handlePublish() compileMission(true); } +void MissionCompilerMainWindow::handleAllDoubleClick(const QModelIndex &index) +{ + const QAbstractItemModel *model = index.model(); + QString item = model->data(index).toString(); + nlinfo("all primitives was double clicked: %s", item.toAscii().data()); + + m_filteredProxyModel->removeRows(index.row(),1); + + QStringList list = m_selectedPrimitivesModel->stringList(); + list << item; + m_selectedPrimitivesModel->setStringList(list); +} + void MissionCompilerMainWindow::compileMission(bool publish) { uint nbMission = 0; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h index 7c4feb78a..2248a7cc5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h @@ -38,11 +38,15 @@ public Q_SLOTS: void handleValidation(); void handleCompile(); void handlePublish(); + void handleAllDoubleClick(const QModelIndex &index); + void handleDataDirButton(); + void handleDataDirChanged(const QString &text); private: Ui::MissionCompilerMainWindow *ui; void updateCompileLog(); + void populateAllPrimitives(const QString &dataDir = QString()); bool parsePrimForMissions(NLLIGO::IPrimitive const *prim, TMissionContainer &missions); void compileMission(bool publish=false); @@ -53,6 +57,7 @@ private: QSortFilterProxyModel *m_filteredProxyModel; QRegExp *m_regexpFilter; QString m_compileLog; + QString m_lastDir; NLLIGO::CLigoConfig m_ligoConfig; }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui index 865cb20d2..a995f0dab 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui @@ -33,7 +33,7 @@ Mission Compiler Options - + @@ -132,7 +132,7 @@ - + true @@ -157,7 +157,7 @@ - + true @@ -182,41 +182,83 @@ - + All Primitives - + Selected Primitives - - - - - - Filter - - - - - - - type filter here - - - - + + + + Filter Criteria + + + + + + + + Data Directory + + + + + + + + + + ... + + + + + + + + + + + Filter + + + + + + + type filter here + + + + + + + horizontalLayoutWidget + dataDirLabel + horizontalLayoutWidget + + + + + 0 + 0 + 776 + 426 + + Publish Options From 884017a65d4abc03b742bf2be35cae31254096fc Mon Sep 17 00:00:00 2001 From: sfb Date: Tue, 7 Jun 2011 14:30:34 -0500 Subject: [PATCH 018/215] Changed: Mission Compiler add/remove selected/all and reset filter functions. --HG-- branch : branch-mission-compiler-qt --- .../mission_compiler_main_window.cpp | 80 ++++++++++++++++--- .../mission_compiler_main_window.h | 7 ++ .../mission_compiler_main_window.ui | 10 +++ 3 files changed, 87 insertions(+), 10 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index f7da41ff2..95d7ca50f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -50,14 +50,25 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : ui->allPrimitivesList->setModel(m_filteredProxyModel); m_selectedPrimitivesModel = new QStringListModel(this); ui->selectedPrimitivesList->setModel(m_selectedPrimitivesModel); - - connect(ui->filterEdit, SIGNAL(textEdited(const QString&)), this, SLOT(handleFilterChanged(const QString&))); + + // Connections for toolbar buttons. connect(ui->actionValidate, SIGNAL(triggered()), this, SLOT(handleValidation())); connect(ui->actionCompile, SIGNAL(triggered()), this, SLOT(handleCompile())); connect(ui->actionPublish, SIGNAL(triggered()), this, SLOT(handlePublish())); + + // Connections for selected item moves. connect(ui->allPrimitivesList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(handleAllDoubleClick(const QModelIndex &))); + connect(ui->selectedPrimitivesList, SIGNAL(doubleClicked(const QModelIndex &)), this, SLOT(handleSelDoubleClick(const QModelIndex &))); + connect(ui->addSelectedButton, SIGNAL(clicked()), this, SLOT(handleMoveSelectedRight())); + connect(ui->removeSelectedButton, SIGNAL(clicked()), this, SLOT(handleMoveSelectedLeft())); + connect(ui->addAllButton, SIGNAL(clicked()), this, SLOT(handleMoveAllRight())); + connect(ui->removeAllButton, SIGNAL(clicked()), this, SLOT(handleMoveAllLeft())); + + // Connections for the filter group box. connect(ui->dataDirButton, SIGNAL(clicked()), this, SLOT(handleDataDirButton())); connect(ui->dataDirEdit, SIGNAL(textChanged(const QString &)), this, SLOT(handleDataDirChanged(const QString &))); + connect(ui->filterEdit, SIGNAL(textEdited(const QString&)), this, SLOT(handleFilterChanged(const QString&))); + connect(ui->resetFiltersButton, SIGNAL(clicked()), this, SLOT(handleResetFiltersButton())); // Set the default data dir to the primitives path. QSettings *settings = Core::ICore::instance()->settings(); @@ -96,6 +107,14 @@ void MissionCompilerMainWindow::populateAllPrimitives(const QString &dataDir) m_allPrimitivesModel->setStringList(list); } + +void MissionCompilerMainWindow::handleResetFiltersButton() +{ + handleDataDirChanged(m_lastDir); + ui->filterEdit->setText(""); + handleFilterChanged(""); +} + void MissionCompilerMainWindow::handleDataDirChanged(const QString &text) { populateAllPrimitives(text); @@ -128,17 +147,58 @@ void MissionCompilerMainWindow::handlePublish() compileMission(true); } +void MissionCompilerMainWindow::handleMoveSelectedRight() +{ + QModelIndexList indexes = ui->allPrimitivesList->selectionModel()->selectedIndexes(); + while(!indexes.isEmpty()) + { + const QModelIndex index = indexes.takeFirst(); + moveSelectedItem(index, m_allPrimitivesModel, m_selectedPrimitivesModel); + indexes = ui->allPrimitivesList->selectionModel()->selectedIndexes(); + } +} + +void MissionCompilerMainWindow::handleMoveAllRight() +{ + ui->allPrimitivesList->selectAll(); + handleMoveSelectedRight(); +} + +void MissionCompilerMainWindow::handleMoveSelectedLeft() +{ + QModelIndexList indexes = ui->selectedPrimitivesList->selectionModel()->selectedIndexes(); + while(!indexes.isEmpty()) + { + const QModelIndex index = indexes.takeFirst(); + moveSelectedItem(index, m_selectedPrimitivesModel, m_allPrimitivesModel); + indexes = ui->selectedPrimitivesList->selectionModel()->selectedIndexes(); + } +} + +void MissionCompilerMainWindow::handleMoveAllLeft() +{ + ui->selectedPrimitivesList->selectAll(); + handleMoveSelectedLeft(); +} + +void MissionCompilerMainWindow::moveSelectedItem(const QModelIndex &index, QStringListModel *from, QStringListModel *to) +{ + QString item = from->data(index, Qt::DisplayRole).toString(); + + from->removeRows(index.row(),1); + QStringList list = to->stringList(); + list << item; + to->setStringList(list); +} + void MissionCompilerMainWindow::handleAllDoubleClick(const QModelIndex &index) { - const QAbstractItemModel *model = index.model(); - QString item = model->data(index).toString(); - nlinfo("all primitives was double clicked: %s", item.toAscii().data()); + moveSelectedItem(index, m_allPrimitivesModel, m_selectedPrimitivesModel); +} - m_filteredProxyModel->removeRows(index.row(),1); - - QStringList list = m_selectedPrimitivesModel->stringList(); - list << item; - m_selectedPrimitivesModel->setStringList(list); +void MissionCompilerMainWindow::handleSelDoubleClick(const QModelIndex &index) +{ + moveSelectedItem(index, m_selectedPrimitivesModel, m_allPrimitivesModel); } void MissionCompilerMainWindow::compileMission(bool publish) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h index 2248a7cc5..7ea2e1f81 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h @@ -39,8 +39,14 @@ public Q_SLOTS: void handleCompile(); void handlePublish(); void handleAllDoubleClick(const QModelIndex &index); + void handleSelDoubleClick(const QModelIndex &index); + void handleMoveSelectedRight(); + void handleMoveSelectedLeft(); + void handleMoveAllRight(); + void handleMoveAllLeft(); void handleDataDirButton(); void handleDataDirChanged(const QString &text); + void handleResetFiltersButton(); private: Ui::MissionCompilerMainWindow *ui; @@ -49,6 +55,7 @@ private: void populateAllPrimitives(const QString &dataDir = QString()); bool parsePrimForMissions(NLLIGO::IPrimitive const *prim, TMissionContainer &missions); void compileMission(bool publish=false); + void moveSelectedItem(const QModelIndex &index, QStringListModel *from, QStringListModel *to); QMenu *_toolModeMenu; QUndoStack *m_undoStack; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui index a995f0dab..d68b1a76d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui @@ -239,6 +239,16 @@ + + + + Blanks out the filter and reloads all files from the data directory. + + + Reset + + + From ba23190a9d8177d57138f831da45166d24c41be6 Mon Sep 17 00:00:00 2001 From: sfb Date: Thu, 9 Jun 2011 14:26:02 -0500 Subject: [PATCH 019/215] Changed: Started to add dialogs for server and publishing settings. --HG-- branch : branch-mission-compiler-qt --- .../plugins/mission_compiler/CMakeLists.txt | 2 +- .../images/ic_nel_add_item.png | Bin 0 -> 3270 bytes .../images/ic_nel_delete_item.png | Bin 0 -> 1496 bytes .../images/ic_nel_down_item.png | Bin 0 -> 3812 bytes .../images/ic_nel_generic_settings.png | Bin 0 -> 5633 bytes .../images/ic_nel_reset_all.png | Bin 0 -> 5938 bytes .../images/ic_nel_up_item.png | Bin 0 -> 3304 bytes .../mission_compiler/mission_compiler.qrc | 6 + .../mission_compiler_settings_page.cpp | 212 ++++++++++++++ .../mission_compiler_settings_page.h | 74 +++++ .../mission_compiler_settings_page.ui | 265 ++++++++++++++++++ .../mission_compiler/server_entry_dialog.ui | 108 +++++++ 12 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_add_item.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_delete_item.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_down_item.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_generic_settings.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_reset_all.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_up_item.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.ui diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt index 4d11e808e..cc6d8489f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt @@ -12,7 +12,7 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin. SET(OVQT_PLUG_MISSION_COMPILER_HDR mission_compiler_plugin.h mission_compiler_main_window.h) -SET(OVQT_PLUG_MISSION_COMPILER_UIS mission_compiler_main_window.ui) +SET(OVQT_PLUG_MISSION_COMPILER_UIS mission_compiler_main_window.ui server_entry_dialog.ui mission_compiler_settings_page.ui) SET(OVQT_PLUG_MISSION_COMPILER_RCS mission_compiler.qrc) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_add_item.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_add_item.png new file mode 100644 index 0000000000000000000000000000000000000000..bde338f7851d2675b29272c7dcf9471196a8f6cc GIT binary patch literal 3270 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 Eg5p97)c^nh literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_delete_item.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_delete_item.png new file mode 100644 index 0000000000000000000000000000000000000000..a5a1787d593591f855be9a7eb8da271cc8d8f214 GIT binary patch literal 1496 zcmaKs{XY{30LC|C-c2!W(h#B2QhA@kOt{brn;~tK$;-mn#DM?-zdb>=i zu*@RTd8@_pvUxpuxyoG1>Bsxw?uX}jKHujL_-0@|T~w8{l>h*Ms+%k3&``sHb<81=-~(e+|N^9C&_KHVw5X^3IM3J{S1(sAHJ}8!egC%W4+FY#L`JIX8~B! zIZ7C+< z6bcMhk7Jz$z zt<~^Npzeyeat^;b(tkMW#bRkwb5S`|-P(cZ{!1geIKC7Tzpi^Q9XhN7r*| zh)Tmw?h}4)r(e%EPx*~jbiDn>W9k_F^gyx#1J1b!&uZuD!J$Duh_I|$-B|3)pjcABT`DZr@@$gnJ?g*7niqC zPg~-q=_uM61|+bWZC}wlE4;C-L;LVrU6J(R@D0Z4=p`kt`4==LnW(d8wXiBZND)3NugXyk}j`B>QSp`rx>7I47#1U z_C?Zqn_!w_iq*Q=7#33$KOO+R;CW!bcdn#k;~tqqg>*o8s|#;{%M1@^2B=`1X*QOW z`~~EL<8YMd;Wk~hP%ln9+T{3weTCGPC>#3IdbbgtfSL(^;<-$(&+()%CWY(ZY}rUs zYZ^=Sws7E+dDpj6e_>>!qz;jFg;SMzWx6GvaM=_CJv#h+^qF(IggsR{8BYMQlBT|c zLR(J;=NJUkn>YRLcdDtY?zXt@zIEUuTe=1hEndEU6|7%)i#=`5cs5S}P9Mh=PcJfo z{ZbSwueq|yZyG~GMGk&1;XI&33fgfyu~6eIQgk!Fkv4<^W8F=|9U5oXINb+#fCen_ z{sB?u3Xfi@SxqMAC%ZI%1BJIf0uMiCx!yXz62rndMv z$=6toP8$LinV$s7C|ZjMS5+@W1rn4NxN=o7bmp|tGx-1vXy7V6(LK7Owl1b<1|o_O z>bY9rcsZ;+(mT(U8Vqy|@K*SC7Xeg!@WG?ntw{ZC`G3Vh1-90Bf1vJ=9f)jI$P4Q6YBQ@Z8;^^H#>9pe+1$q6DXU$-5VbBeJ(3kUiuh943T}3C$!G=4Zw&rX zIZx;etQ%$b10W;H`0fD|-g&n~Eq? zJZ zC}pc4u@0qD`7mQX=zMD+8o3Hi#&31p7{mxJ*6-KkigeYIgt}@Xn1a05YyE6U8VHU~ z8PL=CTn*0W$(#-|89Sz`9P!(hY?pOfm^ZQVXfjUeBhD+B#yUg0vkok?_%`$Xq0`=> g{}tB1bjDO$2a)_&SEAML{IoE@&Dj&vfF`E?18{b*hyVZp literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_down_item.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_down_item.png new file mode 100644 index 0000000000000000000000000000000000000000..fc86c4f6db072ec99ee76b81b8ea21d607226dd7 GIT binary patch literal 3812 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 UsoZjLV*mgE07*qoM6N<$g2^9G?f?J) literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_up_item.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/images/ic_nel_up_item.png new file mode 100644 index 0000000000000000000000000000000000000000..ee471023e095a1ae11a0f614eb953b727b0b169d GIT binary patch literal 3304 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 mw#_~9)Gt2qq5{#N!2bXMO`x+Fh$+(m0000 + images/ic_nel_reset_all.png + images/ic_nel_add_item.png + images/ic_nel_delete_item.png + images/ic_nel_down_item.png + images/ic_nel_generic_settings.png + images/ic_nel_up_item.png images/arrow-left-2.png images/arrow-left-double-2.png images/arrow-right-2.png diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp new file mode 100644 index 000000000..2d92bd45e --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp @@ -0,0 +1,212 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Project includes +#include "mission_compiler_settings_page.h" +#include "../core/core_constants.h" +#include "../core/icore.h" + +// NeL includes +#include + +// Qt includes +#include +#include +#include + +namespace Plugin +{ + +QString lastDir = "."; + +MissionCompilerSettingsPage::MissionCompilerSettingsPage(QObject *parent) + : IOptionsPage(parent), + m_page(0) +{ +} + +MissionCompilerSettingsPage::~MissionCompilerSettingsPage() +{ +} + +QString MissionCompilerSettingsPage::id() const +{ + return QLatin1String("mission_compiler_settings"); +} + +QString MissionCompilerSettingsPage::trName() const +{ + return tr("Mission Compiler Settings"); +} + +QString MissionCompilerSettingsPage::category() const +{ + return QLatin1String("MissionCompilerSettings"); +} + +QString MissionCompilerSettingsPage::trCategory() const +{ + return tr("MissionCompilerSettings"); +} + +QIcon MissionCompilerSettingsPage::categoryIcon() const +{ + return QIcon(); +} + +QWidget *MissionCompilerSettingsPage::createPage(QWidget *parent) +{ + m_page = new QWidget(parent); + m_ui.setupUi(m_page); + + readSettings(); + checkEnabledButton(); + connect(m_ui.addToolButton, SIGNAL(clicked()), this, SLOT(addPath())); + connect(m_ui.removeToolButton, SIGNAL(clicked()), this, SLOT(delPath())); + connect(m_ui.upToolButton, SIGNAL(clicked()), this, SLOT(upPath())); + connect(m_ui.downToolButton, SIGNAL(clicked()), this, SLOT(downPath())); + connect(m_ui.resetToolButton, SIGNAL(clicked()), m_ui.serversTreeWidget, SLOT(clear())); + return m_page; +} + +void MissionCompilerSettingsPage::apply() +{ + writeSettings(); + applySearchPaths(); +} + +void MissionCompilerSettingsPage::finish() +{ + delete m_page; + m_page = 0; +} + +void MissionCompilerSettingsPage::applySearchPaths() +{ + QStringList paths, remapExt; + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + if (m_recurse) + paths = settings->value(Core::Constants::RECURSIVE_SEARCH_PATHS).toStringList(); + else + paths = settings->value(Core::Constants::SEARCH_PATHS).toStringList(); + + remapExt = settings->value(Core::Constants::REMAP_EXTENSIONS).toStringList(); + settings->endGroup(); + + for (int i = 1; i < remapExt.size(); i += 2) + NLMISC::CPath::remapExtension(remapExt.at(i - 1).toStdString(), remapExt.at(i).toStdString(), true); + + Q_FOREACH(QString path, paths) + { + NLMISC::CPath::addSearchPath(path.toStdString(), m_recurse, false); + } +} + +void MissionCompilerSettingsPage::addPath() +{ + QString newPath = QFileDialog::getExistingDirectory(m_page, "", lastDir); + if (!newPath.isEmpty()) + { + QTreeWidgetItem *newItem = new QTreeWidgetItem; + newItem->setText(newPath); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + m_ui.serversTreeWidget->addItem(newItem); + lastDir = newPath; + } + + checkEnabledButton(); +} + +void MissionCompilerSettingsPage::delPath() +{ + QTreeWidgetItem *removeItem = m_ui.serversTreeWidget->takeItem(m_ui.serversTreeWidget->currentRow()); + if (!removeItem) + delete removeItem; + + checkEnabledButton(); +} + +void MissionCompilerSettingsPage::upPath() +{ + int currentRow = m_ui.serversTreeWidget->currentRow(); + if (!(currentRow == 0)) + { + QListWidgetItem *item = m_ui.serversListWidget->takeItem(currentRow); + m_ui.serversListWidget->insertItem(--currentRow, item); + m_ui.serversListWidget->setCurrentRow(currentRow); + } +} + +void MissionCompilerSettingsPage::downPath() +{ + int currentRow = m_ui.serversListWidget->currentRow(); + if (!(currentRow == m_ui.serversListWidget->count()-1)) + { + QListWidgetItem *item = m_ui.serversListWidget->takeItem(currentRow); + m_ui.serversTreeWidget->insertItem(++currentRow, item); + m_ui.serversTreeWidget->setCurrentRow(currentRow); + } +} + +void MissionCompilerSettingsPage::readSettings() +{ + QStringList paths; + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + if (m_recurse) + paths = settings->value(Core::Constants::RECURSIVE_SEARCH_PATHS).toStringList(); + else + paths = settings->value(Core::Constants::SEARCH_PATHS).toStringList(); + settings->endGroup(); + Q_FOREACH(QString path, paths) + { + QListWidgetItem *newItem = new QListWidgetItem; + newItem->setText(path); + newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + m_ui.serversTreeWidget->addItem(newItem); + } +} + +void MissionCompilerSettingsPage::writeSettings() +{ + QStringList paths; + for (int i = 0; i < m_ui.serversTreeWidget->count(); ++i) + paths << m_ui.serversTreeWidget->item(i)->text(); + + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + if (m_recurse) + settings->setValue(Core::Constants::RECURSIVE_SEARCH_PATHS, paths); + else + settings->setValue(Core::Constants::SEARCH_PATHS, paths); + settings->endGroup(); + settings->sync(); +} + +void MissionCompilerSettingsPage::checkEnabledButton() +{ + bool bEnabled = true; + if (m_ui.serversTreeWidget->count() == 0) + bEnabled = false; + + m_ui.removeToolButton->setEnabled(bEnabled); + m_ui.upToolButton->setEnabled(bEnabled); + m_ui.downToolButton->setEnabled(bEnabled); +} + +} /* namespace Plugin */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h new file mode 100644 index 000000000..43fc4ec5c --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h @@ -0,0 +1,74 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#ifndef MISSION_COMPILER_SETTINGS_PAGE_H +#define MISSION_COMPILER_SETTINGS_PAGE_H + +#include + +#include "../core/ioptions_page.h" + +#include "ui_mission_compiler_settings_page.h" + +class QWidget; + +namespace Plugin +{ +/** +@class MissionCompilerSettingsPage +*/ +class MissionCompilerSettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + MissionCompilerSettingsPage(QObject *parent = 0); + ~MissionCompilerSettingsPage(); + + QString id() const; + QString trName() const; + QString category() const; + QString trCategory() const; + QIcon categoryIcon() const; + QWidget *createPage(QWidget *parent); + + void apply(); + void finish(); + + // Set of the search paths(not recursive) and the remap extensions (loading from settings file) + void applySearchPaths(); + +private Q_SLOTS: + void addPath(); + void delPath(); + void upPath(); + void downPath(); + +private: + void readSettings(); + void writeSettings(); + void checkEnabledButton(); + + bool m_recurse; + QWidget *m_page; + Ui::MissionCompilerSettingsPage m_ui; +}; + +} // namespace Plugin + +#endif // MISSION_COMPILER_SETTINGS_PAGE_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui new file mode 100644 index 000000000..c5640750b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui @@ -0,0 +1,265 @@ + + + MissionCompilerSettingsPage + + + + 0 + 0 + 496 + 544 + + + + Form + + + + 6 + + + 3 + + + + + Publication Servers + + + + + + + + + 0 + 0 + + + + Delete + + + + + + + :/buttons/images/ic_nel_delete_item.png:/buttons/images/ic_nel_delete_item.png + + + + 20 + 20 + + + + true + + + + + + + + 0 + 0 + + + + Add + + + + + + + :/buttons/images/ic_nel_add_item.png:/buttons/images/ic_nel_add_item.png + + + + 20 + 20 + + + + true + + + + + + + + 0 + 0 + + + + Up + + + + + + + :/buttons/images/ic_nel_up_item.png:/buttons/images/ic_nel_up_item.png + + + + 20 + 20 + + + + true + + + + + + + + 0 + 0 + + + + Down + + + + + + + :/buttons/images/ic_nel_down_item.png:/buttons/images/ic_nel_down_item.png + + + + 20 + 20 + + + + true + + + + + + + Reset + + + + + + + :/buttons/images/ic_nel_reset_all.png:/buttons/images/ic_nel_reset_all.png + + + + 20 + 20 + + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + true + + + 100 + + + true + + + + Server Name + + + + + Server Text Path + + + + + Server Primitive Path + + + + + + + + + + + General Settings + + + + + + Local Text Path + + + + + + + Local path for compiled mission texts. + + + + + + + ... + + + + + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.ui new file mode 100644 index 000000000..5f7c6d827 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.ui @@ -0,0 +1,108 @@ + + + ServerEntryDialog + + + + 0 + 0 + 400 + 300 + + + + Dialog + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + Server Name + + + + + + + Server Text Path + + + + + + + Server Primitive Path + + + + + + + + + + + + + + + + ... + + + + + + + ... + + + + + + + + + buttonBox + accepted() + ServerEntryDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ServerEntryDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + From c41a312a86894e3b29caa61a27cf55d6df1a93e5 Mon Sep 17 00:00:00 2001 From: sfb Date: Sat, 11 Jun 2011 08:48:16 -0500 Subject: [PATCH 020/215] Changed: Fixed the 'QMetaObject' linking error and commented out most of the old code to be replaced. --HG-- branch : branch-mission-compiler-qt --- .../plugins/mission_compiler/CMakeLists.txt | 3 +- .../mission_compiler_settings_page.cpp | 127 +++++++++--------- .../mission_compiler_settings_page.ui | 2 +- 3 files changed, 69 insertions(+), 63 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt index cc6d8489f..4919bceff 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt @@ -10,7 +10,8 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin. ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_spec.h) SET(OVQT_PLUG_MISSION_COMPILER_HDR mission_compiler_plugin.h - mission_compiler_main_window.h) + mission_compiler_main_window.h + mission_compiler_settings_page.h) SET(OVQT_PLUG_MISSION_COMPILER_UIS mission_compiler_main_window.ui server_entry_dialog.ui mission_compiler_settings_page.ui) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp index 2d92bd45e..f500933a7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp @@ -20,6 +20,8 @@ #include "../core/core_constants.h" #include "../core/icore.h" +#include "ui_server_entry_dialog.h" + // NeL includes #include @@ -27,6 +29,7 @@ #include #include #include +#include namespace Plugin { @@ -79,7 +82,7 @@ QWidget *MissionCompilerSettingsPage::createPage(QWidget *parent) connect(m_ui.removeToolButton, SIGNAL(clicked()), this, SLOT(delPath())); connect(m_ui.upToolButton, SIGNAL(clicked()), this, SLOT(upPath())); connect(m_ui.downToolButton, SIGNAL(clicked()), this, SLOT(downPath())); - connect(m_ui.resetToolButton, SIGNAL(clicked()), m_ui.serversTreeWidget, SLOT(clear())); + //connect(m_ui.resetToolButton, SIGNAL(clicked()), m_ui.serversTreeWidget, SLOT(clear())); return m_page; } @@ -119,94 +122,96 @@ void MissionCompilerSettingsPage::applySearchPaths() void MissionCompilerSettingsPage::addPath() { - QString newPath = QFileDialog::getExistingDirectory(m_page, "", lastDir); - if (!newPath.isEmpty()) - { - QTreeWidgetItem *newItem = new QTreeWidgetItem; - newItem->setText(newPath); - newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - m_ui.serversTreeWidget->addItem(newItem); - lastDir = newPath; - } + Ui::ServerEntryDialog serverEntryDialog; - checkEnabledButton(); + //QString newPath = QFileDialog::getExistingDirectory(m_page, "", lastDir); + //if (!newPath.isEmpty()) + //{ + // QTreeWidgetItem *newItem = new QTreeWidgetItem; + // newItem->setText(newPath); + // newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + // m_ui.serversTreeWidget->addItem(newItem); + // lastDir = newPath; + //} + + //checkEnabledButton(); } void MissionCompilerSettingsPage::delPath() { - QTreeWidgetItem *removeItem = m_ui.serversTreeWidget->takeItem(m_ui.serversTreeWidget->currentRow()); - if (!removeItem) - delete removeItem; + //QTreeWidgetItem *removeItem = m_ui.serversTreeWidget->takeItem(m_ui.serversTreeWidget->currentRow()); + //if (!removeItem) + // delete removeItem; - checkEnabledButton(); + //checkEnabledButton(); } void MissionCompilerSettingsPage::upPath() { - int currentRow = m_ui.serversTreeWidget->currentRow(); - if (!(currentRow == 0)) - { - QListWidgetItem *item = m_ui.serversListWidget->takeItem(currentRow); - m_ui.serversListWidget->insertItem(--currentRow, item); - m_ui.serversListWidget->setCurrentRow(currentRow); - } + //int currentRow = m_ui.serversTreeWidget->currentRow(); + //if (!(currentRow == 0)) + //{ + // QListWidgetItem *item = m_ui.serversListWidget->takeItem(currentRow); + // m_ui.serversListWidget->insertItem(--currentRow, item); + // m_ui.serversListWidget->setCurrentRow(currentRow); + //} } void MissionCompilerSettingsPage::downPath() { - int currentRow = m_ui.serversListWidget->currentRow(); - if (!(currentRow == m_ui.serversListWidget->count()-1)) - { - QListWidgetItem *item = m_ui.serversListWidget->takeItem(currentRow); - m_ui.serversTreeWidget->insertItem(++currentRow, item); - m_ui.serversTreeWidget->setCurrentRow(currentRow); - } + //int currentRow = m_ui.serversListWidget->currentRow(); + //if (!(currentRow == m_ui.serversListWidget->count()-1)) + //{ + // QListWidgetItem *item = m_ui.serversListWidget->takeItem(currentRow); + // m_ui.serversTreeWidget->insertItem(++currentRow, item); + // m_ui.serversTreeWidget->setCurrentRow(currentRow); + //} } void MissionCompilerSettingsPage::readSettings() { - QStringList paths; - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - if (m_recurse) - paths = settings->value(Core::Constants::RECURSIVE_SEARCH_PATHS).toStringList(); - else - paths = settings->value(Core::Constants::SEARCH_PATHS).toStringList(); - settings->endGroup(); - Q_FOREACH(QString path, paths) - { - QListWidgetItem *newItem = new QListWidgetItem; - newItem->setText(path); - newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - m_ui.serversTreeWidget->addItem(newItem); - } + //QStringList paths; + //QSettings *settings = Core::ICore::instance()->settings(); + //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + //if (m_recurse) + // paths = settings->value(Core::Constants::RECURSIVE_SEARCH_PATHS).toStringList(); + //else + // paths = settings->value(Core::Constants::SEARCH_PATHS).toStringList(); + //settings->endGroup(); + //Q_FOREACH(QString path, paths) + //{ + // QListWidgetItem *newItem = new QListWidgetItem; + // newItem->setText(path); + // newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); + // m_ui.serversTreeWidget->addItem(newItem); + //} } void MissionCompilerSettingsPage::writeSettings() { - QStringList paths; - for (int i = 0; i < m_ui.serversTreeWidget->count(); ++i) - paths << m_ui.serversTreeWidget->item(i)->text(); + //QStringList paths; + //for (int i = 0; i < m_ui.serversTreeWidget->count(); ++i) + // paths << m_ui.serversTreeWidget->item(i)->text(); - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - if (m_recurse) - settings->setValue(Core::Constants::RECURSIVE_SEARCH_PATHS, paths); - else - settings->setValue(Core::Constants::SEARCH_PATHS, paths); - settings->endGroup(); - settings->sync(); + //QSettings *settings = Core::ICore::instance()->settings(); + //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + //if (m_recurse) + // settings->setValue(Core::Constants::RECURSIVE_SEARCH_PATHS, paths); + //else + // settings->setValue(Core::Constants::SEARCH_PATHS, paths); + //settings->endGroup(); + //settings->sync(); } void MissionCompilerSettingsPage::checkEnabledButton() { - bool bEnabled = true; - if (m_ui.serversTreeWidget->count() == 0) - bEnabled = false; + //bool bEnabled = true; + //if (m_ui.serversTreeWidget->count() == 0) + // bEnabled = false; - m_ui.removeToolButton->setEnabled(bEnabled); - m_ui.upToolButton->setEnabled(bEnabled); - m_ui.downToolButton->setEnabled(bEnabled); + //m_ui.removeToolButton->setEnabled(bEnabled); + //m_ui.upToolButton->setEnabled(bEnabled); + //m_ui.downToolButton->setEnabled(bEnabled); } } /* namespace Plugin */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui index c5640750b..905cc5185 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui @@ -183,7 +183,7 @@ - + Qt::Horizontal From 624cb708131cbbd0297fcc7092548bd1d49b7da1 Mon Sep 17 00:00:00 2001 From: cemycc Date: Tue, 14 Jun 2011 18:01:21 +0300 Subject: [PATCH 021/215] Changed: #1307 Update the plugin to work with the latest plugin system --HG-- branch : gsoc2011-translationovqt --- .../src/plugins/translation_manager/extract_bot_names.cpp | 1 + .../src/plugins/translation_manager/simple_viewer.cpp | 2 ++ .../src/plugins/translation_manager/simple_viewer.h | 4 +++- .../translation_manager/translation_manager_plugin.cpp | 8 ++++---- .../translation_manager/translation_manager_plugin.h | 8 ++++++++ .../translation_manager_settings_page.cpp | 5 +++++ .../translation_manager_settings_page.h | 3 ++- .../translation_manager_settings_page.ui | 2 +- 8 files changed, 26 insertions(+), 7 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp index 75a97d3bb..3a24127e3 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp @@ -36,6 +36,7 @@ vector Filters; static CLigoConfig LigoConfig; static bool RemoveOlds = false; + struct TCreatureInfo { CSheetId SheetId; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp index e128710c4..1f6df9117 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp @@ -37,6 +37,8 @@ CSimpleViewer::CSimpleViewer(QWidget *parent) gridLayout->setContentsMargins(0, 0, 0, 0); NLQT::QNLWidget *_nelWidget = new NLQT::QNLWidget(this); gridLayout->addWidget(_nelWidget, 0, 0, 1, 1); + + m_undoStack = new QUndoStack(this); } bool CCoreListener::closeMainWindow() const diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h index bbff7e9e0..14b782c22 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h @@ -25,7 +25,7 @@ // Qt includes #include - +#include class QWidget; namespace Plugin @@ -37,6 +37,8 @@ class CSimpleViewer : public QWidget public: CSimpleViewer(QWidget *parent = 0); virtual ~CSimpleViewer() {} + + QUndoStack *m_undoStack; }; class CCoreListener : public Core::ICoreListener diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index 233780ca5..f82024c79 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -57,10 +57,10 @@ void TranslationManagerPlugin::extensionsInitialized() helpMenu->addSeparator(); helpMenu->insertAction(aboutQtAction, aboutTManPlugin); QMenu *transMenu = menuManager->menuBar()->addMenu("Translation Manager"); - /* Words extraction*/ - QAction *botnamesAct = new QAction("Extract bot_names", this); - connect(botnamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); - transMenu->addAction(botnamesAct); + // Words extraction + QAction *botnamesAct = new QAction("Extract bot_names", this); + connect(botnamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); + transMenu->addAction(botnamesAct); } void TranslationManagerPlugin::extractBotNames() diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h index 07bf0d434..0dc6e6510 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -92,6 +92,14 @@ public: { return m_simpleViewer; } + virtual QUndoStack *undoStack() + { + return m_simpleViewer->m_undoStack; + } + virtual void open() + { + + } CSimpleViewer *m_simpleViewer; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp index b3a731ae5..d99cc8642 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp @@ -59,6 +59,11 @@ QString CTranslationManagerSettingsPage::trCategory() const return tr("General"); } +QIcon CTranslationManagerSettingsPage::categoryIcon() const +{ + return QIcon(); +} + QWidget *CTranslationManagerSettingsPage::createPage(QWidget *parent) { _currentPage = new QWidget(parent); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h index 4fec51fb6..7a082f199 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h @@ -43,8 +43,9 @@ public: virtual QString trName() const; virtual QString category() const; virtual QString trCategory() const; + virtual QIcon categoryIcon() const; virtual QWidget *createPage(QWidget *parent); - + virtual void apply(); virtual void finish() {} private Q_SLOTS: diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui index c58533185..6da7b0d8b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui @@ -7,7 +7,7 @@ 0 0 490 - 495 + 482 From ef009ebc90911b5bb65e35ea23e7497fede2f31d Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 20 Jun 2011 15:15:07 +0200 Subject: [PATCH 022/215] Changed: #1304: Instanciation of guild missions. --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.h | 1 + .../guild_manager/guild_member_module.h | 6 ++ .../guild_manager/guild_officer_module.h | 8 +++ .../mission_manager/mission_guild.h | 5 ++ .../mission_manager/mission_manager.cpp | 60 +++++++++++++++---- 5 files changed, 69 insertions(+), 11 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index 3f3b04eb6..49b02e93c 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -184,6 +184,7 @@ public: { // To Do } + void addMission(CMissionGuild* guildMission) {} //@} /// inventory management diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild_member_module.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild_member_module.h index 4447e356f..46dee0f96 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild_member_module.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild_member_module.h @@ -99,6 +99,12 @@ public: /// user wanna pick a mission CMissionGuild * pickMission( TAIAlias alias ); + // Function to check if the member can pick a mission. By default only Officer and above can pick a guild mission + virtual bool canPickMission(TAIAlias alias) + { + return false; + } + /// set the version of last sent info of items in guild inventory void setLastSentInfoVersion(uint32 slot, uint8 infoVersion) { diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild_officer_module.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild_officer_module.h index 7d6f49096..e31885300 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild_officer_module.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild_officer_module.h @@ -34,6 +34,14 @@ public: :CGuildMemberModule(proxy,guildMember){} virtual bool canAffectGrade(EGSPD::CGuildGrade::TGuildGrade grade)const; virtual bool canInvite()const; + + // Function to check if the member can pick a mission. + // By default only Officers and above can pick a guild mission. + // So we don't need to implement this function for the other grades + virtual bool canPickMission(TAIAlias alias) + { + return true; + } }; ; diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.h b/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.h index e0c65f2c2..76cd0cb6a 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.h +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.h @@ -32,6 +32,7 @@ public: CMissionGuild() : _Chained(false) { } + inline void setGuild( uint16 guildId ); /// override void updateUsersJournalEntry(); /// override @@ -58,6 +59,10 @@ private: bool _Chained; }; +void CMissionGuild::setGuild( uint16 guildId ) +{ + _GuildId = guildId; +} #endif // RY_MISSION_GUILD_H diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp index 55a08ef4f..057412068 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp @@ -836,29 +836,68 @@ void CMissionManager::instanciateMission(CCharacter* user,TAIAlias alias, TAIAl } else if ( templ->Type == MISSION_DESC::Guild ) { - /// todo guild mission + /// Check to see if we can pick the mission CGuildMemberModule * module; if ( !user->getModuleParent().getModule( module ) ) { MISDBG("%s user not in a guild", sDebugPrefix.c_str()); return; } - inst = module->pickMission( templ->Alias ); - if (!inst) + /* /// This is already checked in the prerequisites + if (!module->pickMission( templ->Alias )) + { + /// Todo : error message for the member return; + }*/ + + CGuild * guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); + if (!guild) + { + nlwarning( "cant find guild ID : %d", _GuildId ); + return; + } + if ( !templ->Tags.NoList && guild->getMissions().size() >= MaxGuildMissionCount) + { + CCharacter::sendDynamicSystemMessage(user->getId(), "MISSION_MAX_GUILD_REACHED" ); + return; + } + + CMissionGuild * guildMission = EGS_PD_CAST( EGSPD::CMissionGuildPD::create( templ->Alias ) ); + if ( !guildMission ) + { + MISDBG("%s could not create guild mission", sDebugPrefix.c_str()); + return; + } + guildMission->onCreation( giver ); + guildMission->setGuild(user->getGuildId()); + + // Find a suitable client index (for non-invisible missions) + if ( templ->Tags.NoList == false ) + { + uint8 idx = 0; + for ( uint i = MaxGroupMissionCount; i < MaxGroupMissionCount + MaxGuildMissionCount; i++ ) + { + if ( ! CBankAccessor_PLR::getGROUP().getMISSIONS().getArray(i).getTITLE(user->_PropertyDatabase)) + { + idx = i; + break; + } + } + guildMission->setClientIndex( idx ); + } + + // Add mission + guild->addMission( guildMission ); + inst = guildMission; /// /!\ Do the same thing that the team missions but with the loop: for ( uint i = MaxGroupMissionCount; i < MaxGroupMissionCount + MaxGuildMissionCount; i++ ) /// Instead of for ( uint i = 0; i < MaxGroupMissionCount; i++ ), so that we use available space for guild missions - /// todo guild mission : see solo - /* - todo guild mission : implement that in module - teamMission->initBasics( giver ); - soloMission->setTeam( user->getTeamId() ); + /*//teamMission->initBasics( giver ); + //soloMission->setTeam( user->getTeamId() ); CGuild * guild = user->getGuild(); if ( guild ) { - mission->getguild if ( guild->getMissions().size() >= MaxGuildMissionCount) { CCharacter::sendDynamicSystemMessage(user->getId(), "MISSION_MAX_GUILD_REACHED" ); @@ -884,8 +923,7 @@ void CMissionManager::instanciateMission(CCharacter* user,TAIAlias alias, TAIAl else { - } - */ + }*/ } else { From a7f4bfa718411811d3c861908e3f12831fde5d74 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 20 Jun 2011 16:08:12 +0200 Subject: [PATCH 023/215] Changed: #1304: Add guild missions prerequisites. --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.h | 2 + .../mission_manager/mission_manager.cpp | 3 +- .../mission_manager/mission_template.cpp | 102 ++++++++++++++---- 3 files changed, 86 insertions(+), 21 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index 49b02e93c..ecedbf9ee 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -182,9 +182,11 @@ public: ///\return the mission inline std::vector & getMissions() { + return std::vector(); // To Do } void addMission(CMissionGuild* guildMission) {} + bool isMissionSuccessfull(TAIAlias alias) { return false; } //@} /// inventory management diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp index 057412068..ca5d4bba0 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_manager.cpp @@ -44,6 +44,7 @@ #include "egs_utils.h" #include "egs_pd.h" #include "guild_manager/guild_member_module.h" +#include "guild_manager/guild_manager.h" #include "building_manager/building_manager.h" #include "building_manager/room_instance.h" #include "zone_manager.h" @@ -853,7 +854,7 @@ void CMissionManager::instanciateMission(CCharacter* user,TAIAlias alias, TAIAl CGuild * guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); if (!guild) { - nlwarning( "cant find guild ID : %d", _GuildId ); + nlwarning( "cant find guild ID : %d", user->getGuildId() ); return; } if ( !templ->Tags.NoList && guild->getMissions().size() >= MaxGuildMissionCount) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_template.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_template.cpp index dfd516b45..af0686f1e 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_template.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_template.cpp @@ -1307,21 +1307,57 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos else if ( Type == MISSION_DESC::Guild ) { /// todo guild mission - /* - CGuild * guild = user->getGuild(); + + CGuild * guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); if ( guild == NULL ) { MISDBG("No guild"); return MISSION_DESC::PreReqFail; + + /*if (logOnFail) + MISDBG("%s No guild", sDebugPrefix.c_str()); + + if (!fillPrereqInfos) + return MISSION_DESC::PreReqFail; + + prereqDesc.Description = STRING_MANAGER::sendStringToClient(user->getEntityRowId(), "MISSION_PREREQ_TEAM", TVectorParamCheck()); + prereqDesc.IsMandatory = true; + prereqDesc.Validated = false; + prereqInfos.Prerequisits.push_back(prereqDesc); + + addedPrereqTexts.insert("MISSION_PREREQ_TEAM"); + + returnValue = MISSION_DESC::PreReqFail; + logOnFail = false;*/ } // check if the mission is already picked for ( uint j = 0 ; j < guild->getMissions().size(); j++ ) { - if ( guild->getMissions()[j]->getTemplate()->Alias == alias ) + if ( guild->getMissions()[j]->getTemplateId() == alias || + guild->getMissions()[j]->getMainMissionTemplateId() == alias) { MISDBG("The guild already own this mission"); return MISSION_DESC::PreReqFail; + + /*if (logOnFail) + MISDBG("%s The guild already own this mission", sDebugPrefix.c_str()); + + if (!fillPrereqInfos) + return MISSION_DESC::PreReqFail; + + if (addedPrereqTexts.find("MISSION_PREREQ_ALREADY_DONE") == addedPrereqTexts.end()) + { + prereqDesc.Description = STRING_MANAGER::sendStringToClient(user->getEntityRowId(), "MISSION_PREREQ_ALREADY_DONE", TVectorParamCheck()); + prereqDesc.IsMandatory = true; + prereqDesc.Validated = false; + prereqInfos.Prerequisits.push_back(prereqDesc); + addedPrereqTexts.insert("MISSION_PREREQ_ALREADY_DONE"); + } + + returnValue = MISSION_DESC::PreReqFail; + logOnFail = false;*/ } + } // check non replayable missions if( !Tags.Replayable ) @@ -1329,11 +1365,29 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos if (guild->isMissionSuccessfull(alias)) // if ( std::find(guild->getSuccessfulMissions().begin(),guild->getSuccessfulMissions().end(), alias) != guild->getSuccessfulMissions().end() ) { - MISDBG("solo non replayable"); + MISDBG("mission non replayable"); return MISSION_DESC::PreReqFail; + + /*if (logOnFail) + MISDBG("%s Guild mission already done and not replayable", sDebugPrefix.c_str()); + + if (!fillPrereqInfos) + return MISSION_DESC::PreReqFailAlreadyDone; + + if (addedPrereqTexts.find("MISSION_PREREQ_ALREADY_DONE") == addedPrereqTexts.end()) + { + prereqDesc.Description = STRING_MANAGER::sendStringToClient(user->getEntityRowId(), "MISSION_PREREQ_ALREADY_DONE", TVectorParamCheck()); + prereqDesc.IsMandatory = true; + prereqDesc.Validated = false; + prereqInfos.Prerequisits.push_back(prereqDesc); + addedPrereqTexts.insert("MISSION_PREREQ_ALREADY_DONE"); + } + + returnValue = MISSION_DESC::PreReqFailAlreadyDone; + logOnFail = false;*/ } } - */ + } else { @@ -1412,7 +1466,7 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos if ( templ->Type == MISSION_DESC::Guild ) { /// todo guild mission - /* + CGuild* guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); if ( ! guild ) { MISDBG("Require needed mission at line %u (guild mission but player has no guild)", Prerequisits.NeededMissions[i].Line); @@ -1420,7 +1474,7 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos } if (guild->isMissionSuccessfull(templ->Alias)) break; - */ + } else if ( templ->Type == MISSION_DESC::Solo ) { @@ -1473,15 +1527,14 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos else if ( templ->Type == MISSION_DESC::Guild ) { /// todo guild mission - /* + CGuild* guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); if ( !guild ) break; if (!guild->isMissionSuccessfull(templ->Alias)) - - if (!guild || !guild->isMissionSuccessfull(templ->Alias)) - break; - */ + /*if (!guild || !guild->isMissionSuccessfull(templ->Alias)) + break;*/ + } else if ( templ->Type == MISSION_DESC::Solo ) { @@ -1537,8 +1590,8 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos { /// todo guild mission - /* - + + CGuild* guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); if ( !guild ) { MISDBG("Require running mission at line %u (guild mission but player has no guild)", Prerequisits.RunningMissions[i].Line ); @@ -1548,12 +1601,12 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos for ( ; k < guild->getMissions().size(); k++ ) { - if ( guild->getMissions()[k]->getTemplate()->Alias == templ->Alias ) + if ( guild->getMissions()[k]->getTemplateId() == templ->Alias ) break; } if (k != guild->getMissions().size()) break; - */ + } else if ( templ->Type == MISSION_DESC::Solo ) { @@ -1651,20 +1704,20 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos { /// todo guild mission - /* - + + CGuild* guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); if(!guild) break; uint k=0; for ( ; k < guild->getMissions().size(); k++ ) { - if ( guild->getMissions()[k]->getTemplate()->Alias == templ->Alias ) + if ( guild->getMissions()[k]->getTemplateId() == templ->Alias ) break; } if (k == guild->getMissions().size()) break; - */ + } else if ( templ->Type == MISSION_DESC::Solo ) { @@ -2041,6 +2094,15 @@ uint32 CMissionTemplate::testPrerequisits( CCharacter * user, CPrerequisitInfos if (!fillPrereqInfos) return MISSION_DESC::PreReqFail; + /*if (addedPrereqTexts.find("GUILD_BUILDING_BAD_GRADE") == addedPrereqTexts.end()) + { + prereqDesc.Description = STRING_MANAGER::sendStringToClient(user->getEntityRowId(), "GUILD_BUILDING_BAD_GRADE", TVectorParamCheck()); + prereqDesc.IsMandatory = true; + prereqDesc.Validated = false; + prereqInfos.Prerequisits.push_back(prereqDesc); + addedPrereqTexts.insert("GUILD_BUILDING_BAD_GRADE"); + }*/ + returnValue = MISSION_DESC::PreReqFail; logOnFail = false; } From 32c07d0b62d332e259aa9e5c05820c6275ce1b5d Mon Sep 17 00:00:00 2001 From: cemycc Date: Tue, 21 Jun 2011 01:03:54 +0300 Subject: [PATCH 024/215] Changed: #1307 Added extract_bot_names --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/CMakeLists.txt | 6 +- .../translation_manager/extract_bot_names.cpp | 111 +++++----- .../translation_manager/qnel_widget.cpp | 197 ------------------ .../plugins/translation_manager/qnel_widget.h | 130 ------------ .../translation_manager/simple_viewer.cpp | 56 ----- .../translation_manager_main_window.cpp | 169 +++++++++++++++ ...er.h => translation_manager_main_window.h} | 40 +++- .../translation_manager_plugin.cpp | 50 +---- .../translation_manager_plugin.h | 18 +- .../translation_manager_settings_page.cpp | 42 ++-- .../translation_manager_settings_page.ui | 12 +- 11 files changed, 302 insertions(+), 529 deletions(-) delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.cpp delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.h delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp rename code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/{simple_viewer.h => translation_manager_main_window.h} (60%) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt index 827db7487..ec3980f05 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -10,11 +10,11 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin. ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_spec.h) SET(OVQT_PLUG_TRANSLATION_MANAGER_HDR translation_manager_plugin.h - qnel_widget.h - simple_viewer.h + translation_manager_main_window.h translation_manager_settings_page.h) -SET(OVQT_PLUG_TRANSLATION_MANAGER_UIS translation_manager_settings_page.ui) +SET(OVQT_PLUG_TRANSLATION_MANAGER_UIS translation_manager_settings_page.ui + translation_manager_main_window.ui) SET(QT_USE_QTGUI TRUE) SET(QT_USE_QTOPENGL TRUE) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp index 3a24127e3..a5e5690ad 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp @@ -145,6 +145,15 @@ set GenericNames; map SimpleNames; set Functions; +set getGenericNames() +{ + return GenericNames; +} + +map getSimpleNames() +{ + return SimpleNames; +} string removeAndStoreFunction(const std::string &fullName) { @@ -221,40 +230,6 @@ void addSimpleName(const std::string &name, const std::string &sheetName) int extractBotNamesAll(map > config_paths, string ligo_class_file, string trans_path, string work_path) { - //------------------------------------------------------------------- - // read the parameters - /*for (int i=2; i::iterator it = config_paths["paths"].begin(); it != config_paths["paths"].end(); ++it) { CPath::addSearchPath(*it, true, false); @@ -269,7 +244,6 @@ int extractBotNamesAll(map > config_paths, string ligo_class Filters.push_back(*it); } - //------------------------------------------------------------------- // init the sheets CSheetId::init(false); @@ -451,26 +425,62 @@ int extractBotNamesAll(map > config_paths, string ligo_class } else { - TEntryInfo ei; addSimpleName(removeAndStoreFunction(name), sheetStr); } } } - } - + } + //------------------------------------------------------------------- // step 2 : load the reference file nlinfo("Looking for missing translation:"); + + string work_path_file = work_path + "/bot_names.txt"; + string trans_path_file = trans_path + "/bot_names.txt"; + string title_path_file = work_path + "/title_words_wk.txt"; + + TWorksheet botNames; + if (!CFile::fileExists(work_path_file) || !loadExcelSheet(work_path_file, botNames)) + { + botNames.resize(botNames.size() + 1); + botNames.insertColumn(botNames.ColCount); + botNames.setData(0,botNames.ColCount - 1,ucstring("bot name")); + botNames.insertColumn(botNames.ColCount); + botNames.setData(0,botNames.ColCount - 1,ucstring("translated name")); + botNames.insertColumn(botNames.ColCount); + botNames.setData(0,botNames.ColCount - 1,ucstring("sheet_name")); + } - TWorksheet botNames; - loadExcelSheet(work_path, botNames, true); - TWorksheet transBotNames; - loadExcelSheet(trans_path, transBotNames, true); + TWorksheet transBotNames; + if (!CFile::fileExists(trans_path_file) || !loadExcelSheet(trans_path_file, transBotNames)) + { + transBotNames.resize(transBotNames.size() + 1); + transBotNames.insertColumn(transBotNames.ColCount); + transBotNames.setData(0,transBotNames.ColCount - 1,ucstring("*HASH_VALUE")); + transBotNames.insertColumn(transBotNames.ColCount); + transBotNames.setData(0,transBotNames.ColCount - 1,ucstring("bot name")); + transBotNames.insertColumn(transBotNames.ColCount); + transBotNames.setData(0,transBotNames.ColCount - 1,ucstring("translated name")); + transBotNames.insertColumn(transBotNames.ColCount); + transBotNames.setData(0,transBotNames.ColCount - 1,ucstring("sheet_name")); + } - TWorksheet fcts; - loadExcelSheet(work_path, fcts, true); + TWorksheet fcts; + if (!CFile::fileExists(title_path_file) || !loadExcelSheet(title_path_file, fcts)) + { + fcts.resize(fcts.size() + 1); + fcts.insertColumn(fcts.ColCount); + fcts.setData(0,fcts.ColCount - 1,ucstring("title_id")); + fcts.insertColumn(fcts.ColCount); + fcts.setData(0,fcts.ColCount - 1,ucstring("name")); + fcts.insertColumn(fcts.ColCount); + fcts.setData(0,fcts.ColCount - 1,ucstring("women_name")); + } + loadExcelSheet(work_path_file, botNames, true); + loadExcelSheet(trans_path_file, transBotNames, true); + loadExcelSheet(title_path_file, fcts, true); // add missing element @@ -482,9 +492,9 @@ int extractBotNamesAll(map > config_paths, string ligo_class nlverify(botNames.findId(botIdCol)); uint transIdCol; nlverify(transBotNames.findId(transIdCol)); - uint fctsIdCol; + uint fctsIdCol; nlverify(fcts.findId(fctsIdCol)); - + // special treatment to add the sheet_name col { uint sheetCol; @@ -743,14 +753,13 @@ int extractBotNamesAll(map > config_paths, string ligo_class nlinfo("Adding %u new generic name", nbAddGenericName); nlinfo("Adding %u new function name", nbAddFunction); - // saving the modified files - + // saving the modified files ucstring s = prepareExcelSheet(botNames); - CI18N::writeTextFile(work_path, s, false); + CI18N::writeTextFile(work_path_file, s, false); s = prepareExcelSheet(transBotNames); - CI18N::writeTextFile(trans_path, s, false); + CI18N::writeTextFile(trans_path_file, s, false); s = prepareExcelSheet(fcts); - CI18N::writeTextFile(work_path, s, false); + CI18N::writeTextFile(title_path_file, s, false); return 0; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.cpp deleted file mode 100644 index 9a67abb80..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.cpp +++ /dev/null @@ -1,197 +0,0 @@ -// Object Viewer Qt - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "qnel_widget.h" - -// STL includes - -// Qt includes -#include -#include - -// NeL includes -#include -#include -#include -#include - -namespace NLQT -{ - -QNLWidget::QNLWidget(QWidget *parent) - : QNeLWidget(parent), - m_driver(NULL), - m_initialized(false), - m_interval(25) -{ - setMouseTracking(true); - setFocusPolicy(Qt::StrongFocus); - - init(); -#ifdef Q_OS_LINUX - makeCurrent(); -#endif - m_mainTimer = new QTimer(this); - connect(m_mainTimer, SIGNAL(timeout()), this, SLOT(updateRender())); -} - -QNLWidget::~QNLWidget() -{ - release(); -} - -void QNLWidget::init() -{ - // create the driver - m_driver = NL3D::UDriver::createDriver(NULL, false, NULL); - nlassert(m_driver); - - // initialize the nel 3d viewport - m_driver->setDisplay((nlWindow)winId(), NL3D::UDriver::CMode(width(), height(), 32)); - - // set the cache size for the font manager(in bytes) - m_driver->setFontManagerMaxMemory(2097152); - - m_initialized = true; -} - -void QNLWidget::release() -{ - m_mainTimer->stop(); - delete m_mainTimer; - if (m_initialized) - { - m_driver->release(); - delete m_driver; - m_driver = NULL; - } -} - -void QNLWidget::setInterval(int msec) -{ - m_interval = msec; - m_mainTimer->setInterval(msec); -} - -void QNLWidget::setBackgroundColor(NLMISC::CRGBA backgroundColor) -{ - m_backgroundColor = backgroundColor; -} - -void QNLWidget::updateRender() -{ - if (isVisible()) - { - if (m_initialized) - m_driver->EventServer.pump(); - Q_EMIT updateData(); - - // Calc FPS - static sint64 lastTime = NLMISC::CTime::getPerformanceTime (); - sint64 newTime = NLMISC::CTime::getPerformanceTime (); - m_fps = float(1.0 / NLMISC::CTime::ticksToSecond (newTime-lastTime)); - lastTime = newTime; - - if (m_initialized && !m_driver->isLost()) - { - //_driver->activate(); - m_driver->clearBuffers(m_backgroundColor); - Q_EMIT updatePreRender(); - - Q_EMIT updatePostRender(); - // swap 3d buffers - m_driver->swapBuffers(); - } - } -} - -void QNLWidget::showEvent(QShowEvent *showEvent) -{ - QWidget::showEvent(showEvent); - m_driver->activate(); - m_mainTimer->start(m_interval); -} - -void QNLWidget::hideEvent(QHideEvent *hideEvent) -{ - m_mainTimer->stop(); - QWidget::hideEvent(hideEvent); -} - -#if defined(NL_OS_WINDOWS) - -typedef bool (*winProc)(NL3D::IDriver *driver, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); - -bool QNLWidget::winEvent(MSG *message, long *result) -{ - if (m_driver && m_driver->isActive()) - { - NL3D::IDriver *driver = dynamic_cast(m_driver)->getDriver(); - if (driver) - { - winProc proc = (winProc)driver->getWindowProc(); - return proc(driver, message->hwnd, message->message, message->wParam, message->lParam); - } - } - - return false; -} - -#elif defined(NL_OS_MAC) - -typedef bool (*cocoaProc)(NL3D::IDriver *, const void *e); - -bool QNLWidget::macEvent(EventHandlerCallRef caller, EventRef event) -{ - if(caller) - nlerror("You are using QtCarbon! Only QtCocoa supported, please upgrade Qt"); - - if (m_driver && m_driver->isActive()) - { - NL3D::IDriver *driver = dynamic_cast(m_driver)->getDriver(); - if (driver) - { - cocoaProc proc = (cocoaProc)driver->getWindowProc(); - return proc(driver, event); - } - } - - return false; -} - -#elif defined(NL_OS_UNIX) - -typedef bool (*x11Proc)(NL3D::IDriver *drv, XEvent *e); - -bool QNLWidget::x11Event(XEvent *event) -{ - if (m_driver && m_driver->isActive()) - { - NL3D::IDriver *driver = dynamic_cast(m_driver)->getDriver(); - if (driver) - { - x11Proc proc = (x11Proc)driver->getWindowProc(); - return proc(driver, event); - } - } - - return false; -} -#endif - -} /* namespace NLQT */ - diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.h deleted file mode 100644 index a54e6bb8a..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/qnel_widget.h +++ /dev/null @@ -1,130 +0,0 @@ -// Object Viewer Qt - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef QNEL_WIDGET_H -#define QNEL_WIDGET_H - -// NeL includes -#include -#include -#include - -// Qt includes -#include -#include - -class QAction; - -/* TODO every platform should use QWidget */ -#if defined(NL_OS_WINDOWS) -typedef QWidget QNeLWidget; -#elif defined(NL_OS_MAC) -typedef QWidget QNeLWidget; -#elif defined(NL_OS_UNIX) -typedef QGLWidget QNeLWidget; -#endif // NL_OS_UNIX - -namespace NL3D -{ -class UDriver; -class UScene; -} - -namespace NLQT -{ - -/** -@class QNLWidget -@brief Responsible for interaction between Qt and NeL. -@details Automatically begins to update the render if the widget is visible -or suspends the updating of render if the widget is hidden. -*/ -class QNLWidget : public QNeLWidget -{ - Q_OBJECT - -public: - QNLWidget(QWidget *parent); - virtual ~QNLWidget(); - - /// Set the update interval renderer - void setInterval(int msec); - - /// Set the background color. - void setBackgroundColor(NLMISC::CRGBA backgroundColor); - - float fps() const - { - return m_fps; - } - - inline NLMISC::CRGBA backgroundColor() const - { - return m_backgroundColor; - } - - NL3D::UDriver *driver() const - { - return m_driver; - } - - virtual QPaintEngine* paintEngine() const - { - return NULL; - } -Q_SIGNALS: - void updateData(); - void updatePreRender(); - void updatePostRender(); - -private Q_SLOTS: - void updateRender(); - -protected: - virtual void showEvent(QShowEvent *showEvent); - virtual void hideEvent(QHideEvent *hideEvent); - -#if defined(NL_OS_WINDOWS) - virtual bool winEvent(MSG *message, long *result); -#elif defined(NL_OS_MAC) - virtual bool macEvent(EventHandlerCallRef caller, EventRef event); -#elif defined(NL_OS_UNIX) - virtual bool x11Event(XEvent *event); -#endif - -private: - void init(); - void release(); - - QNLWidget(const QNLWidget &); - QNLWidget &operator=(const QNLWidget &); - - NL3D::UDriver *m_driver; - NLMISC::CRGBA m_backgroundColor; - - QTimer *m_mainTimer; - - bool m_initialized; - int m_interval; - float m_fps; - -}; /* class QNLWidget */ - -} /* namespace NLQT */ - - -#endif // QNEL_WIDGET_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp deleted file mode 100644 index 1f6df9117..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.cpp +++ /dev/null @@ -1,56 +0,0 @@ -// Object Viewer Qt - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "simple_viewer.h" - -// Qt includes -#include -#include -#include - -// NeL includes - -// Project includes - -namespace Plugin -{ - -CSimpleViewer::CSimpleViewer(QWidget *parent) - : QWidget(parent) -{ - QGridLayout *gridLayout = new QGridLayout(this); - gridLayout->setObjectName(QString::fromUtf8("gridLayoutSimpleViewer")); - gridLayout->setContentsMargins(0, 0, 0, 0); - NLQT::QNLWidget *_nelWidget = new NLQT::QNLWidget(this); - gridLayout->addWidget(_nelWidget, 0, 0, 1, 1); - - m_undoStack = new QUndoStack(this); -} - -bool CCoreListener::closeMainWindow() const -{ - int ret = QMessageBox::question(0, tr("Example close event hook"), - tr("Do you want to close window?"), - QMessageBox::Yes | QMessageBox::No); - - if (ret == QMessageBox::Yes) - return true; - else - return false; -} - -} /* namespace Plugin */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp new file mode 100644 index 000000000..159e80e04 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -0,0 +1,169 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "translation_manager_main_window.h" +// Project system includes +#include "../core/icore.h" +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +struct TEntryInfo +{ + string SheetName; +}; + +set getGenericNames(); +map getSimpleNames(); +int extractBotNamesAll(map > config_paths, string ligo_class_file, string trans_path, string work_path); + +namespace Plugin +{ + +CMainWindow::CMainWindow(QWidget *parent) + : QMainWindow(parent) +{ + _ui.setupUi(this); + + _toolMenu = new QMenu(tr("Primitives"), _ui.toolBar); + _ui.toolBar->addAction(_toolMenu->menuAction()); + + QAction *extractBotNames = _toolMenu->addAction(tr("Extract bot names")); + extractBotNames->setStatusTip(tr("Extract bot names from primitives")); + connect(extractBotNames, SIGNAL(triggered()), this, SLOT(extractBotNames())); + + + + readSettings(); + m_undoStack = new QUndoStack(this); +} + +void CMainWindow::readSettings() +{ + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("translationmanager"); + + list paths = convertQStringList(settings->value("paths").toStringList()); /* paths */ + config_paths["paths"] = paths; + list pathsR = convertQStringList(settings->value("pathsR").toStringList()); /* pathsR */ + config_paths["pathsR"] = pathsR; + list georges = convertQStringList(settings->value("georges").toStringList()); /* georges */ + config_paths["georges"] = georges; + list filters = convertQStringList(settings->value("filters").toStringList()); /* filters */ + config_paths["filters"] = filters; + + languages = convertQStringList(settings->value("trlanguages").toStringList()); /* languages */ + ligo_path = settings->value("ligo").toString().toStdString(); + translation_path = settings->value("translation").toString().toStdString(); + work_path = settings->value("work").toString().toStdString(); + + settings->endGroup(); +} + +void CMainWindow::extractBotNames() +{ + if(verifySettings() == true) + { + // int extract_bot_names = extractBotNamesAll(config_paths, ligo_path, translation_path, work_path); + + QGridLayout* mainLayout = new QGridLayout(); + + + + //contentsWindow->setAllowedAreas(Qt::LeftDockWidgetArea); + + + QListWidget *listWidget = new QListWidget(this); + + mainLayout->addWidget(QListWidget); + + + + QTableWidget *tableWidget = new QTableWidget(this); + + tableWidget->setRowCount(10); + tableWidget->setColumnCount(5); + + mainLayout->addWidget(QTableWidget); + setCentralWidget(tableWidget); + } +} + + +bool CMainWindow::verifySettings() +{ + bool count_errors = false; + + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("translationmanager"); + + if(settings->value("paths").toList().count() == 0 + || settings->value("pathsR").toList().count() == 0 + || settings->value("georges").toList().count() == 0 + || settings->value("filters").toList().count() == 0) + { + QErrorMessage error_settings; + error_settings.showMessage("Please write all the paths on the settings dialog."); + error_settings.exec(); + count_errors = true; + } + + if((settings->value("ligo").toString().isEmpty() + || settings->value("translation").toString().isEmpty() + || settings->value("work").toString().isEmpty() + || settings->value("trlanguages").toList().count() == 0) + && count_errors == false) + { + QErrorMessage error_settings; + error_settings.showMessage("Please write the paths for ligo, translation and work files and the languages on the settings dialog." + settings->value("trlanguages").toString()); + error_settings.exec(); + count_errors = true; + } + + settings->endGroup(); + + return !count_errors; + +} + +list CMainWindow::convertQStringList(QStringList listq) +{ + std::list stdlist; + + Q_FOREACH(QString text, listq) + { + stdlist.push_back(text.toStdString()); + } + + return stdlist; +} + +bool CCoreListener::closeMainWindow() const +{ + return true; +} + +} /* namespace Plugin */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h similarity index 60% rename from code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h rename to code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 14b782c22..4e90c6088 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/simple_viewer.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -16,29 +16,53 @@ // along with this program. If not, see . -#ifndef SIMPLE_VIEWER_H -#define SIMPLE_VIEWER_H +#ifndef MAIN_WINDOW_H +#define MAIN_WINDOW_H // Project includes -#include "qnel_widget.h" #include "../core/icore_listener.h" // Qt includes #include #include +#include +#include +#include +#include + +#include "ui_translation_manager_main_window.h" +#include + class QWidget; +using namespace std; + namespace Plugin { -class CSimpleViewer : public QWidget +class CMainWindow : public QMainWindow { Q_OBJECT public: - CSimpleViewer(QWidget *parent = 0); - virtual ~CSimpleViewer() {} - - QUndoStack *m_undoStack; + CMainWindow(QWidget *parent = 0); + virtual ~CMainWindow() {} + QUndoStack *m_undoStack; +private: + Ui::CMainWindow _ui; + QMenu *_toolMenu; + map > config_paths; + list languages; + string ligo_path; + string translation_path; + string work_path; +private Q_SLOTS: + void extractBotNames(); +private: + void compareBotNames(); + bool verifySettings(); + void readSettings(); + list convertQStringList(QStringList listq); + }; class CCoreListener : public Core::ICoreListener diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index f82024c79..91e7686d5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -1,7 +1,7 @@ // Project includes #include "translation_manager_plugin.h" #include "translation_manager_settings_page.h" -#include "simple_viewer.h" +#include "translation_manager_main_window.h" // Project system includes #include "../core/icore.h" #include "../core/core_constants.h" @@ -14,13 +14,12 @@ // Qt includes #include #include +#include #include #include #include #include -int extractBotNamesAll(map > config_paths, string ligo_class_file, string trans_path, string work_path); - namespace Plugin { TranslationManagerPlugin::~TranslationManagerPlugin() @@ -41,6 +40,7 @@ bool TranslationManagerPlugin::initialize(ExtensionSystem::IPluginManager *plugi addAutoReleasedObject(new CTranslationManagerSettingsPage(this)); addAutoReleasedObject(new CTranslationManagerContext(this)); addAutoReleasedObject(new CCoreListener(this)); + return true; } @@ -56,39 +56,6 @@ void TranslationManagerPlugin::extensionsInitialized() QAction *aboutQtAction = menuManager->action(Core::Constants::ABOUT_QT); helpMenu->addSeparator(); helpMenu->insertAction(aboutQtAction, aboutTManPlugin); - QMenu *transMenu = menuManager->menuBar()->addMenu("Translation Manager"); - // Words extraction - QAction *botnamesAct = new QAction("Extract bot_names", this); - connect(botnamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); - transMenu->addAction(botnamesAct); -} - -void TranslationManagerPlugin::extractBotNames() -{ - // prepare the config paths - list paths,pathsR, georges, filters, languages; - string ligo, translation, work; - map > config_paths; - - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup("translationmanager"); - - paths = ConvertQStringList(settings->value("paths").toStringList()); /* paths */ - config_paths["paths"] = paths; - pathsR = ConvertQStringList(settings->value("pathsR").toStringList()); /* pathsR */ - config_paths["pathsR"] = pathsR; - georges = ConvertQStringList(settings->value("georges").toStringList()); /* georges */ - config_paths["georges"] = georges; - filters = ConvertQStringList(settings->value("filters").toStringList()); /* filters */ - config_paths["filters"] = filters; - languages = ConvertQStringList(settings->value("languages").toStringList()); /* languages */ - ligo = settings->value("ligo").toString().toStdString(); - translation = settings->value("translation").toString().toStdString(); - work = settings->value("work").toString().toStdString(); - settings->endGroup(); - - extractBotNamesAll(config_paths, ligo, translation, work); - } void TranslationManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) @@ -101,17 +68,6 @@ void TranslationManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) _LibContext = new NLMISC::CLibraryContext(*nelContext); } -list TranslationManagerPlugin::ConvertQStringList(QStringList listq) -{ - std::list stdlist; - Q_FOREACH(QString text, listq) - { - stdlist.push_back(text.toStdString()); - } - - return stdlist; -} - QString TranslationManagerPlugin::name() const { return "Translation Manager"; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h index 0dc6e6510..42515cb2f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -4,7 +4,7 @@ // Project includes #include "../../extension_system/iplugin.h" #include "../core/icontext.h" -#include "simple_viewer.h" +#include "translation_manager_main_window.h" // NeL includes #include "nel/misc/app_context.h" @@ -28,12 +28,13 @@ class IPluginSpec; namespace Plugin { + class CTranslationManagerContext; + class TranslationManagerPlugin : public QObject, public ExtensionSystem::IPlugin { Q_OBJECT Q_INTERFACES(ExtensionSystem::IPlugin) public: - virtual ~TranslationManagerPlugin(); bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); @@ -58,11 +59,6 @@ protected: private: ExtensionSystem::IPluginManager *_plugMan; QList _autoReleaseObjects; - list ConvertQStringList(QStringList list); - - -private Q_SLOTS: - void extractBotNames(); }; class CTranslationManagerContext: public Core::IContext @@ -71,7 +67,7 @@ class CTranslationManagerContext: public Core::IContext public: CTranslationManagerContext(QObject *parent = 0): IContext(parent) { - m_simpleViewer = new CSimpleViewer(); + m_MainWindow = new CMainWindow(); } virtual ~CTranslationManagerContext() {} @@ -90,18 +86,18 @@ public: } virtual QWidget *widget() { - return m_simpleViewer; + return m_MainWindow; } virtual QUndoStack *undoStack() { - return m_simpleViewer->m_undoStack; + return m_MainWindow->m_undoStack; } virtual void open() { } - CSimpleViewer *m_simpleViewer; + CMainWindow *m_MainWindow; }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp index d99cc8642..078a9c18c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp @@ -68,7 +68,7 @@ QWidget *CTranslationManagerSettingsPage::createPage(QWidget *parent) { _currentPage = new QWidget(parent); _ui.setupUi(_currentPage); - readSettings(); + readSettings(); connect(_ui.paths_add, SIGNAL(clicked()), this, SLOT(pathAdd())); connect(_ui.paths_del, SIGNAL(clicked()), this, SLOT(pathDel())); connect(_ui.pathsR_add, SIGNAL(clicked()), this, SLOT(pathRAdd())); @@ -79,8 +79,8 @@ QWidget *CTranslationManagerSettingsPage::createPage(QWidget *parent) connect(_ui.filter_del, SIGNAL(clicked()), this, SLOT(filterDel())); connect(_ui.lang_add, SIGNAL(clicked()), this, SLOT(languageAdd())); connect(_ui.lang_del, SIGNAL(clicked()), this, SLOT(languageDel())); - connect(_ui.translation_add, SIGNAL(clicked()), this, SLOT(translationAdd())); - connect(_ui.work_add, SIGNAL(clicked()), this, SLOT(workAdd())); + connect(_ui.translation_add, SIGNAL(clicked()), this, SLOT(translationAdd())); + connect(_ui.work_add, SIGNAL(clicked()), this, SLOT(workAdd())); return _currentPage; } @@ -218,13 +218,13 @@ void CTranslationManagerSettingsPage::readSettings() pathsR = settings->value("pathsR").toStringList(); /* pathsR */ georges = settings->value("georges").toStringList(); /* georges */ filters = settings->value("filters").toStringList(); /* filters */ - languages = settings->value("languages").toStringList(); /* languages */ + languages = settings->value("trlanguages").toStringList(); /* languages */ ligo = settings->value("ligo").toString(); translation = settings->value("translation").toString(); work = settings->value("work").toString(); settings->endGroup(); - /* paths */ + // paths Q_FOREACH(QString path, paths) { QListWidgetItem *newItem = new QListWidgetItem; @@ -232,7 +232,7 @@ void CTranslationManagerSettingsPage::readSettings() newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.paths_list->addItem(newItem); } - /* pathsR */ + // pathsR Q_FOREACH(QString pathR, pathsR) { QListWidgetItem *newItem = new QListWidgetItem; @@ -240,7 +240,7 @@ void CTranslationManagerSettingsPage::readSettings() newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.pathsR_list->addItem(newItem); } - /* georges */ + // georges Q_FOREACH(QString george, georges) { QListWidgetItem *newItem = new QListWidgetItem; @@ -248,7 +248,7 @@ void CTranslationManagerSettingsPage::readSettings() newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.georges_list->addItem(newItem); } - /* filter */ + // filter Q_FOREACH(QString filter, filters) { QListWidgetItem *newItem = new QListWidgetItem; @@ -256,7 +256,7 @@ void CTranslationManagerSettingsPage::readSettings() newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.filter_list->addItem(newItem); } - /* languages */ + // languages Q_FOREACH(QString lang, languages) { QListWidgetItem *newItem = new QListWidgetItem; @@ -264,11 +264,11 @@ void CTranslationManagerSettingsPage::readSettings() newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.lang_list->addItem(newItem); } - /* ligo */ + // ligo _ui.ligo_edit->setText(ligo); - /* translation */ + // translation _ui.translation_edit->setText(translation); - /* work */ + // work _ui.work_edit->setText(work); } @@ -277,25 +277,26 @@ void CTranslationManagerSettingsPage::writeSettings() { QStringList paths, pathsR, georges, filters, languages; QString ligo, translation, work; - /* paths */ + // paths for (int i = 0; i < _ui.paths_list->count(); ++i) paths << _ui.paths_list->item(i)->text(); - /* pathsR */ + // pathsR for (int i = 0; i < _ui.pathsR_list->count(); ++i) pathsR << _ui.pathsR_list->item(i)->text(); - /* georges */ + // georges for (int i = 0; i < _ui.georges_list->count(); ++i) georges << _ui.georges_list->item(i)->text(); - /* filters */ + // filters for (int i = 0; i < _ui.filter_list->count(); ++i) filters << _ui.filter_list->item(i)->text(); - /* languages */ + // languages for (int i = 0; i < _ui.lang_list->count(); ++i) languages << _ui.lang_list->item(i)->text(); - /* ligo path */ + // ligo path ligo = _ui.ligo_edit->text(); - /* translations path*/ + // translations path translation = _ui.translation_edit->text(); + // work path work = _ui.work_edit->text(); QSettings *settings = Core::ICore::instance()->settings(); @@ -304,11 +305,12 @@ void CTranslationManagerSettingsPage::writeSettings() settings->setValue("pathsR", pathsR); settings->setValue("georges", georges); settings->setValue("filters", filters); - settings->setValue("languages", languages); + settings->setValue("trlanguages", languages); settings->setValue("ligo", ligo); settings->setValue("translation", translation); settings->setValue("work", work); settings->endGroup(); + settings->sync(); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui index 6da7b0d8b..4c932b4f5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui @@ -7,7 +7,7 @@ 0 0 490 - 482 + 496 @@ -214,9 +214,9 @@ 9 - 230 - 450 - 201 + 190 + 454 + 161 @@ -280,7 +280,7 @@ 9 10 211 - 221 + 181 @@ -333,7 +333,7 @@ 240 10 221 - 221 + 181 From ed92fd2a786d1d20b349c710bae2cd6a366eb837 Mon Sep 17 00:00:00 2001 From: cemycc Date: Sat, 25 Jun 2011 16:42:45 +0300 Subject: [PATCH 025/215] Changed: #1307 Added editor for worksheet files. --HG-- branch : gsoc2011-translationovqt --- .../translation_manager_main_window.cpp | 122 +++++++++++++----- .../translation_manager_main_window.h | 34 ++++- .../translation_manager_main_window.ui | 57 ++++++++ 3 files changed, 183 insertions(+), 30 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 159e80e04..5dedd7c70 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -18,6 +18,9 @@ #include "translation_manager_main_window.h" // Project system includes #include "../core/icore.h" +#include "../core/core_constants.h" +#include "../core/imenu_manager.h" +#include "../../extension_system/iplugin_spec.h" // Qt includes #include #include @@ -27,10 +30,14 @@ #include #include #include +#include #include #include #include #include +#include +#include + struct TEntryInfo { string SheetName; @@ -48,19 +55,97 @@ CMainWindow::CMainWindow(QWidget *parent) { _ui.setupUi(this); - _toolMenu = new QMenu(tr("Primitives"), _ui.toolBar); - _ui.toolBar->addAction(_toolMenu->menuAction()); - - QAction *extractBotNames = _toolMenu->addAction(tr("Extract bot names")); - extractBotNames->setStatusTip(tr("Extract bot names from primitives")); - connect(extractBotNames, SIGNAL(triggered()), this, SLOT(extractBotNames())); - - readSettings(); + createToolbar(); m_undoStack = new QUndoStack(this); } +void CMainWindow::createToolbar() +{ + // Tools menu + Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); + QMenu *translationManagerMenu = new QMenu("Translation Manager"); + QAction *extractBotNamesAct = translationManagerMenu->addAction("Extract bot names"); + extractBotNamesAct->setStatusTip(tr("Extract bot names from primitives")); + QMenu *toolMenu = menuManager->menu(Core::Constants::M_TOOLS); + toolMenu->addMenu(translationManagerMenu); + + + // File menu + //QAction *action = menuManager->action(Core::Constants::NEW); + //_ui.toolBar->addAction(action); + openAct = menuManager->action(Core::Constants::OPEN); + _ui.toolBar->addAction(openAct); + connect(openAct, SIGNAL(triggered()), this, SLOT(open())); + + saveAct = menuManager->action(Core::Constants::SAVE); + _ui.toolBar->addAction(saveAct); + connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); + //action = menuManager->action(Core::Constants::SAVE_AS); + //_ui.toolBar->addAction(action); + +} + +void CMainWindow::open() +{ + QString file_name = QFileDialog::getOpenFileName(this); + if (!file_name.isEmpty()) + { + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(file_name.toStdString(), wk_file, true) == true) + { + QTableWidget *wk_table = new QTableWidget(); + wk_table->setToolTip(file_name); + wk_table->setWindowFilePath(file_name); + wk_table->setColumnCount(wk_file.ColCount); + wk_table->setRowCount(wk_file.size() - 1); + // read columns name + for(unsigned int i = 0; i < wk_file.ColCount; i++) + { + QTableWidgetItem *col = new QTableWidgetItem(); + ucstring col_name = wk_file.getData(0, i); + col->setText(tr(col_name.toString().c_str())); + + wk_table->setHorizontalHeaderItem(i, col); + } + // read rows + for(unsigned int i = 1; i < wk_file.size(); i++) + { + for(unsigned int j = 0; j < wk_file.ColCount; j++) + { + QTableWidgetItem *row = new QTableWidgetItem(); + ucstring row_value = wk_file.getData(i, j); + row->setText(tr(row_value.toString().c_str())); + + wk_table->setItem(i - 1, j, row); + } + } + QMdiSubWindow *sub_window = new QMdiSubWindow(_ui.mdiArea); + sub_window->setWidget(wk_table); + wk_table->resizeColumnsToContents(); + wk_table->resizeRowsToContents(); + wk_table->showMaximized(); + sub_window->activateWindow(); + //_ui.mdiArea->addSubWindow(sub_window); + // set editor signals + connect(wk_table, SIGNAL(cellChanged(int,int) ), this, SLOT(sheetEditorChanged(int,int))); + } + } + +} + +void CMainWindow::sheetEditorChanged(int, int) +{ + saveAct->setEnabled(true); +} + +void CMainWindow::save() +{ + QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); + +} + void CMainWindow::readSettings() { QSettings *settings = Core::ICore::instance()->settings(); @@ -87,28 +172,7 @@ void CMainWindow::extractBotNames() { if(verifySettings() == true) { - // int extract_bot_names = extractBotNamesAll(config_paths, ligo_path, translation_path, work_path); - - QGridLayout* mainLayout = new QGridLayout(); - - - - //contentsWindow->setAllowedAreas(Qt::LeftDockWidgetArea); - - - QListWidget *listWidget = new QListWidget(this); - mainLayout->addWidget(QListWidget); - - - - QTableWidget *tableWidget = new QTableWidget(this); - - tableWidget->setRowCount(10); - tableWidget->setColumnCount(5); - - mainLayout->addWidget(QTableWidget); - setCentralWidget(tableWidget); } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 4e90c6088..863145bc5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -22,6 +22,12 @@ // Project includes #include "../core/icore_listener.h" +// Nel includes +#include "nel/misc/types_nl.h" +#include "nel/misc/sheet_id.h" +#include "nel/misc/path.h" +#include "nel/misc/diff_tool.h" + // Qt includes #include #include @@ -29,6 +35,8 @@ #include #include #include +#include + #include "ui_translation_manager_main_window.h" #include @@ -49,7 +57,10 @@ public: QUndoStack *m_undoStack; private: Ui::CMainWindow _ui; - QMenu *_toolMenu; + // actions + QAction *openAct; + QAction *saveAct; + // config map > config_paths; list languages; string ligo_path; @@ -57,12 +68,18 @@ private: string work_path; private Q_SLOTS: void extractBotNames(); + void open(); + void save(); + void sheetEditorChanged(int, int); private: void compareBotNames(); bool verifySettings(); void readSettings(); + void createMenus(); + void createToolbar(); list convertQStringList(QStringList listq); + }; class CCoreListener : public Core::ICoreListener @@ -75,6 +92,21 @@ public: virtual bool closeMainWindow() const; }; +class CMdiSubWindow : public QMdiSubWindow +{ + private: + int window_type; + public: + int getWType() + { + return window_type; + } + void setWType(int nType) + { + window_type = nType; + } +}; + } // namespace Plugin #endif // SIMPLE_VIEWER_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui new file mode 100644 index 000000000..395574415 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui @@ -0,0 +1,57 @@ + + + CMainWindow + + + + 0 + 0 + 883 + 576 + + + + MainWindow + + + + + + + + 0 + 0 + + + + Qt::Horizontal + + + + true + + + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + From fe125bbaebe1792c9e44f323a7354c6c18541432 Mon Sep 17 00:00:00 2001 From: cemycc Date: Sun, 26 Jun 2011 05:48:28 +0300 Subject: [PATCH 026/215] Changed: #1307 Added option for SaveAs and a windows list --HG-- branch : gsoc2011-translationovqt --- .../translation_manager_main_window.cpp | 175 ++++++++++++++++-- .../translation_manager_main_window.h | 17 ++ 2 files changed, 180 insertions(+), 12 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 5dedd7c70..1866b3975 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -37,6 +37,10 @@ #include #include #include +#include +#include +#include + struct TEntryInfo { @@ -54,7 +58,8 @@ CMainWindow::CMainWindow(QWidget *parent) : QMainWindow(parent) { _ui.setupUi(this); - + _ui.mdiArea->closeAllSubWindows(); + connect(_ui.mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),this, SLOT(activeSubWindowChanged())); readSettings(); createToolbar(); @@ -72,19 +77,55 @@ void CMainWindow::createToolbar() toolMenu->addMenu(translationManagerMenu); - // File menu - //QAction *action = menuManager->action(Core::Constants::NEW); - //_ui.toolBar->addAction(action); - openAct = menuManager->action(Core::Constants::OPEN); + // File menu + openAct = new QAction(QIcon(Core::Constants::ICON_OPEN), "&Open...", this); _ui.toolBar->addAction(openAct); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); - saveAct = menuManager->action(Core::Constants::SAVE); + saveAct = new QAction(QIcon(Core::Constants::ICON_SAVE), "&Save...", this); _ui.toolBar->addAction(saveAct); connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); - //action = menuManager->action(Core::Constants::SAVE_AS); - //_ui.toolBar->addAction(action); - + + saveAsAct = new QAction(QIcon(Core::Constants::ICON_SAVE_AS), "&Save as...", this); + _ui.toolBar->addAction(saveAsAct); + connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); + + // Windows menu + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + windowMenu = new QMenu(tr("&Windows..."), _ui.toolBar); + windowMenu->setIcon(QIcon(Core::Constants::ICON_PILL)); + _ui.toolBar->addAction(windowMenu->menuAction()); + connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowsList())); + + +} + +void CMainWindow::activeSubWindowChanged() +{ + updateWindowsList(); +} + +void CMainWindow::updateWindowsList() +{ + int i = 0; + windowMenu->clear(); + QList windows = _ui.mdiArea->subWindowList(); + for (QList::iterator it = windows.begin(); it != windows.end(); ++it) { + QString window_file = QFileInfo((*it)->widget()->windowFilePath()).fileName(); + QString action_text; + if (i < 9) { + action_text = tr("&%1 %2").arg(i + 1).arg(window_file); + } else { + action_text = tr("%1 %2").arg(i + 1).arg(window_file); + } + QAction *action = windowMenu->addAction(action_text); + action->setCheckable(true); + action->setChecked((*it) == _ui.mdiArea->activeSubWindow()); + connect(action, SIGNAL(triggered()), windowMapper, SLOT(map())); + windowMapper->setMapping(action, windows.at(i)); + i++; + } } void CMainWindow::open() @@ -127,23 +168,126 @@ void CMainWindow::open() wk_table->resizeRowsToContents(); wk_table->showMaximized(); sub_window->activateWindow(); - //_ui.mdiArea->addSubWindow(sub_window); // set editor signals connect(wk_table, SIGNAL(cellChanged(int,int) ), this, SLOT(sheetEditorChanged(int,int))); + // windows menu + updateWindowsList(); + } else { + QErrorMessage error_settings; + error_settings.showMessage("This file is not a worksheet file."); + error_settings.exec(); } } } -void CMainWindow::sheetEditorChanged(int, int) +void CMainWindow::sheetEditorChanged(int row, int column) { saveAct->setEnabled(true); + QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); + if(modifiedCells.find(current_window) != modifiedCells.end()) // founded + { + list cells = modifiedCells[current_window]; + bool overwriteResult = false; + for(list::iterator it = cells.begin(); it != cells.end(); ++it) + { + if((*it).row == row && (*it).col == column ) + overwriteResult = true; + } + if(overwriteResult == false) + { + CCelPos v; + v.row = row; + v.col = column; + cells.push_back(v); + } + } else { // not found + list cells; + CCelPos v; + v.row = row; + v.col = column; + cells.push_back(v); + modifiedCells[current_window] = cells; + } } void CMainWindow::save() { QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); + + if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + { + QWidget *subwindow_widget = current_window->widget(); + QTableWidget *table_editor = qobject_cast(subwindow_widget); + QString file_path = table_editor->windowFilePath(); + + if(modifiedCells.find(current_window) != modifiedCells.end()) + { + STRING_MANAGER::TWorksheet wk_file; + loadExcelSheet(file_path.toStdString(), wk_file, true); + list cells = modifiedCells[current_window]; + for(list::iterator it = cells.begin(); it != cells.end(); ++it) + { + QTableWidgetItem* edited_item = table_editor->item((*it).row, (*it).col); + wk_file.setData((*it).row + 1, (*it).col, ucstring(edited_item->text().toStdString())); + cells.erase(it); + } + ucstring s = prepareExcelSheet(wk_file); + NLMISC::CI18N::writeTextFile(file_path.toStdString(), s, false); + if(cells.size() == 0) + modifiedCells.erase(current_window); + } + } +} +void CMainWindow::saveAs() +{ + QString file_name; + if (_ui.mdiArea->isActiveWindow()) + { + file_name = QFileDialog::getSaveFileName(this); + } + + if (!file_name.isEmpty()) + { + QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); + + if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + { + QWidget *subwindow_widget = current_window->widget(); + QTableWidget *table_editor = qobject_cast(subwindow_widget); + QString orig_file_path = table_editor->windowFilePath(); + STRING_MANAGER::TWorksheet new_file, wk_file; + loadExcelSheet(orig_file_path.toStdString(), wk_file, true); + // set columns + new_file.resize(new_file.size() + 1); + for(unsigned int i = 0; i < wk_file.ColCount; i++) + { + ucstring col_name = wk_file.getData(0, i); + new_file.insertColumn(new_file.ColCount); + new_file.setData(0, new_file.ColCount - 1, col_name); + } + // read all the rows from table + uint rowIdx; + for(int i = 0; i < table_editor->rowCount(); i++) + { + rowIdx = new_file.size(); + new_file.resize(new_file.size() + 1); + for(int j = 0; j < table_editor->columnCount(); j++) + { + QTableWidgetItem* item = table_editor->item(i, j); + new_file.setData(rowIdx, j, ucstring(item->text().toStdString())); + } + } + ucstring s = prepareExcelSheet(new_file); + NLMISC::CI18N::writeTextFile(file_name.toStdString(), s, false); + } + + } + + QErrorMessage error_settings; + error_settings.showMessage( file_name); + error_settings.exec(); } void CMainWindow::readSettings() @@ -212,7 +356,14 @@ bool CMainWindow::verifySettings() return !count_errors; } - + + void CMainWindow::setActiveSubWindow(QWidget *window) + { + if (!window) + return; + _ui.mdiArea->setActiveSubWindow(qobject_cast(window)); + } + list CMainWindow::convertQStringList(QStringList listq) { std::list stdlist; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 863145bc5..9d7cb8862 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -36,6 +36,8 @@ #include #include #include +#include + #include "ui_translation_manager_main_window.h" @@ -48,6 +50,12 @@ using namespace std; namespace Plugin { +struct CCelPos +{ + int col; + int row; +}; + class CMainWindow : public QMainWindow { Q_OBJECT @@ -57,9 +65,14 @@ public: QUndoStack *m_undoStack; private: Ui::CMainWindow _ui; + + map > modifiedCells; // actions QAction *openAct; QAction *saveAct; + QAction *saveAsAct; + QMenu *windowMenu; + QSignalMapper *windowMapper; // config map > config_paths; list languages; @@ -70,13 +83,17 @@ private Q_SLOTS: void extractBotNames(); void open(); void save(); + void saveAs(); void sheetEditorChanged(int, int); + void setActiveSubWindow(QWidget *window); + void activeSubWindowChanged(); private: void compareBotNames(); bool verifySettings(); void readSettings(); void createMenus(); void createToolbar(); + void updateWindowsList(); list convertQStringList(QStringList listq); From f19ebefbe69b326d25864223bf2f6b47258c25b1 Mon Sep 17 00:00:00 2001 From: cemycc Date: Mon, 27 Jun 2011 03:02:21 +0300 Subject: [PATCH 027/215] Changed: #1307 Added option to extract the botnames from primitives. --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/extract_bot_names.cpp | 28 ++- .../translation_manager_main_window.cpp | 192 +++++++++++++----- .../translation_manager_main_window.h | 2 + 3 files changed, 161 insertions(+), 61 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp index a5e5690ad..66d7d1c69 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp @@ -155,6 +155,16 @@ map getSimpleNames() return SimpleNames; } +void cleanSimpleNames() +{ + SimpleNames.clear(); +} + +void cleanGenericNames() +{ + GenericNames.clear(); +} + string removeAndStoreFunction(const std::string &fullName) { string::size_type pos = fullName.find("$"); @@ -228,7 +238,7 @@ void addSimpleName(const std::string &name, const std::string &sheetName) } } -int extractBotNamesAll(map > config_paths, string ligo_class_file, string trans_path, string work_path) +void setPathsForPrimitives(map > config_paths, string ligo_class_file) { for (std::list::iterator it = config_paths["paths"].begin(); it != config_paths["paths"].end(); ++it) { @@ -265,8 +275,11 @@ int extractBotNamesAll(map > config_paths, string ligo_class LigoConfig.readPrimitiveClass(ligoPath.c_str(), false); NLLIGO::Register(); - CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; + CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; +} +void extractBotNamesFromPrimitives() +{ //------------------------------------------------------------------- // ok, ready for the real work, // first, read the primitives files and parse the primitives @@ -429,8 +442,13 @@ int extractBotNamesAll(map > config_paths, string ligo_class } } } - } - + } +} + +int extractBotNamesAll(map > config_paths, string ligo_class_file, string trans_path, string work_path) +{ + + /* //------------------------------------------------------------------- // step 2 : load the reference file @@ -760,7 +778,7 @@ int extractBotNamesAll(map > config_paths, string ligo_class CI18N::writeTextFile(trans_path_file, s, false); s = prepareExcelSheet(fcts); CI18N::writeTextFile(title_path_file, s, false); - +*/ return 0; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 1866b3975..946ed78fe 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -48,8 +48,13 @@ struct TEntryInfo }; set getGenericNames(); +void cleanGenericNames(); map getSimpleNames(); -int extractBotNamesAll(map > config_paths, string ligo_class_file, string trans_path, string work_path); +void cleanSimpleNames(); +void setPathsForPrimitives(map > config_paths, string ligo_class_file); +void extractBotNamesFromPrimitives(); +string cleanupName(const std::string &name); +ucstring cleanupUcName(const ucstring &name); namespace Plugin { @@ -61,6 +66,9 @@ CMainWindow::CMainWindow(QWidget *parent) _ui.mdiArea->closeAllSubWindows(); connect(_ui.mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),this, SLOT(activeSubWindowChanged())); + // set extraction scripts counters + execution_count["extract_bot_names"] = 0; + readSettings(); createToolbar(); m_undoStack = new QUndoStack(this); @@ -68,28 +76,25 @@ CMainWindow::CMainWindow(QWidget *parent) void CMainWindow::createToolbar() { - // Tools menu - Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); - QMenu *translationManagerMenu = new QMenu("Translation Manager"); - QAction *extractBotNamesAct = translationManagerMenu->addAction("Extract bot names"); - extractBotNamesAct->setStatusTip(tr("Extract bot names from primitives")); - QMenu *toolMenu = menuManager->menu(Core::Constants::M_TOOLS); - toolMenu->addMenu(translationManagerMenu); - - // File menu openAct = new QAction(QIcon(Core::Constants::ICON_OPEN), "&Open...", this); _ui.toolBar->addAction(openAct); - connect(openAct, SIGNAL(triggered()), this, SLOT(open())); - + connect(openAct, SIGNAL(triggered()), this, SLOT(open())); saveAct = new QAction(QIcon(Core::Constants::ICON_SAVE), "&Save...", this); _ui.toolBar->addAction(saveAct); - connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); - + connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); saveAsAct = new QAction(QIcon(Core::Constants::ICON_SAVE_AS), "&Save as...", this); _ui.toolBar->addAction(saveAsAct); connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); + // Tools menu + QMenu *wordsExtractionMenu = new QMenu("&Words extraction..."); + wordsExtractionMenu->setIcon(QIcon(Core::Constants::ICON_SETTINGS)); + _ui.toolBar->addAction(wordsExtractionMenu->menuAction()); + QAction *extractBotNamesAct = wordsExtractionMenu->addAction("&Extract bot names..."); + extractBotNamesAct->setStatusTip(tr("Extract bot names from primitives.")); + connect(extractBotNamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); + // Windows menu windowMapper = new QSignalMapper(this); connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); @@ -97,8 +102,6 @@ void CMainWindow::createToolbar() windowMenu->setIcon(QIcon(Core::Constants::ICON_PILL)); _ui.toolBar->addAction(windowMenu->menuAction()); connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowsList())); - - } void CMainWindow::activeSubWindowChanged() @@ -136,32 +139,60 @@ void CMainWindow::open() STRING_MANAGER::TWorksheet wk_file; if(loadExcelSheet(file_name.toStdString(), wk_file, true) == true) { + bool hasHashValue = false; QTableWidget *wk_table = new QTableWidget(); wk_table->setToolTip(file_name); wk_table->setWindowFilePath(file_name); - wk_table->setColumnCount(wk_file.ColCount); + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + wk_table->setColumnCount(wk_file.ColCount - 1); + hasHashValue = true; + } else { + wk_table->setColumnCount(wk_file.ColCount); + } wk_table->setRowCount(wk_file.size() - 1); // read columns name + for(unsigned int i = 0; i < wk_file.ColCount; i++) { - QTableWidgetItem *col = new QTableWidgetItem(); - ucstring col_name = wk_file.getData(0, i); - col->setText(tr(col_name.toString().c_str())); - - wk_table->setHorizontalHeaderItem(i, col); + if(hasHashValue && i == 0) + { + // we don't show the column with hash value + } else { + QTableWidgetItem *col = new QTableWidgetItem(); + ucstring col_name = wk_file.getData(0, i); + col->setText(tr(col_name.toString().c_str())); + if(hasHashValue) + { + wk_table->setHorizontalHeaderItem(i - 1, col); + } else { + wk_table->setHorizontalHeaderItem(i, col); + } + } } // read rows + for(unsigned int i = 1; i < wk_file.size(); i++) { for(unsigned int j = 0; j < wk_file.ColCount; j++) { - QTableWidgetItem *row = new QTableWidgetItem(); - ucstring row_value = wk_file.getData(i, j); - row->setText(tr(row_value.toString().c_str())); - - wk_table->setItem(i - 1, j, row); - } - } + if(hasHashValue && j == 0) + { + // we don't show the column with hash value + } else { + QTableWidgetItem *row = new QTableWidgetItem(); + ucstring row_value = wk_file.getData(i, j); + row->setText(tr(row_value.toString().c_str())); + if(hasHashValue) + { + wk_table->setItem(i - 1, j - 1, row); + } else { + wk_table->setItem(i - 1, j, row); + } + } + } + } + QMdiSubWindow *sub_window = new QMdiSubWindow(_ui.mdiArea); sub_window->setWidget(wk_table); wk_table->resizeColumnsToContents(); @@ -170,7 +201,6 @@ void CMainWindow::open() sub_window->activateWindow(); // set editor signals connect(wk_table, SIGNAL(cellChanged(int,int) ), this, SLOT(sheetEditorChanged(int,int))); - // windows menu updateWindowsList(); } else { QErrorMessage error_settings; @@ -284,10 +314,81 @@ void CMainWindow::saveAs() } } - - QErrorMessage error_settings; - error_settings.showMessage( file_name); - error_settings.exec(); +} + +void CMainWindow::extractBotNames() +{ + if(verifySettings() == true) + { + QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); + if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + { + if(execution_count["extract_bot_names"] == 0) + setPathsForPrimitives(config_paths, ligo_path); + extractBotNamesFromPrimitives(); + execution_count["extract_bot_names"] = execution_count["extract_bot_names"] + 1; + + QWidget *subwindow_widget = current_window->widget(); + QTableWidget *table_editor = qobject_cast(subwindow_widget); + // get SimpleNames + { + map SimpleNames = getSimpleNames(); + map::iterator it(SimpleNames.begin()), last(SimpleNames.end()); + + for (; it != last; ++it) + { + QList search_results = table_editor->findItems(tr(it->first.c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + QTableWidgetItem *bot_name_row = new QTableWidgetItem(); + bot_name_row->setText(tr(it->first.c_str())); + bot_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 0, bot_name_row); + QTableWidgetItem *translation_name_row = new QTableWidgetItem(); + translation_name_row->setBackgroundColor(QColor("#F75D59")); + translation_name_row->setText(tr(it->first.c_str())); + table_editor ->setItem(currentRow , 1, translation_name_row); + QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); + sheet_name_row->setText(tr(it->second.SheetName.c_str())); + sheet_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 2, sheet_name_row); + } + } + cleanSimpleNames(); + } + // get GenericNames + { + set GenericNames = getGenericNames(); + set::iterator it(GenericNames.begin()), last(GenericNames.end()); + for (; it != last; ++it) + { + string gnName = "gn_" + cleanupName(*it); + QList search_results = table_editor->findItems(tr((*it).c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + QTableWidgetItem *bot_name_row = new QTableWidgetItem(); + bot_name_row->setText(tr((*it).c_str())); + bot_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 0, bot_name_row); + QTableWidgetItem *translation_name_row = new QTableWidgetItem(); + translation_name_row->setBackgroundColor(QColor("#F75D59")); + translation_name_row->setText(tr(gnName.c_str())); + table_editor ->setItem(currentRow , 1, translation_name_row); + QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); + sheet_name_row->setText(" "); + sheet_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 2, sheet_name_row); + } + } + cleanGenericNames(); + } + + } + } } void CMainWindow::readSettings() @@ -312,15 +413,6 @@ void CMainWindow::readSettings() settings->endGroup(); } -void CMainWindow::extractBotNames() -{ - if(verifySettings() == true) - { - - } -} - - bool CMainWindow::verifySettings() { bool count_errors = false; @@ -338,19 +430,7 @@ bool CMainWindow::verifySettings() error_settings.exec(); count_errors = true; } - - if((settings->value("ligo").toString().isEmpty() - || settings->value("translation").toString().isEmpty() - || settings->value("work").toString().isEmpty() - || settings->value("trlanguages").toList().count() == 0) - && count_errors == false) - { - QErrorMessage error_settings; - error_settings.showMessage("Please write the paths for ligo, translation and work files and the languages on the settings dialog." + settings->value("trlanguages").toString()); - error_settings.exec(); - count_errors = true; - } - + settings->endGroup(); return !count_errors; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 9d7cb8862..4df21fa0b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -79,6 +79,8 @@ private: string ligo_path; string translation_path; string work_path; + // counts + map execution_count; private Q_SLOTS: void extractBotNames(); void open(); From 19cfe260328633662835109e60ddffec06282254 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Wed, 29 Jun 2011 14:44:30 +0200 Subject: [PATCH 028/215] Changed: #1304: Implementation of basic CGuild functions for guild missions --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.cpp | 44 ++++++++++++++++--- .../guild_manager/guild.h | 27 +++++++++--- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp index 56a2606dd..ccf55d54b 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp @@ -34,6 +34,7 @@ #include "outpost_manager/outpost_manager.h" #include "primitives_parser.h" #include "modules/shard_unifier_client.h" +#include "mission_manager/mission_manager.h" /// todo guild remove entity id translator #include "nel/misc/eid_translator.h" @@ -686,23 +687,54 @@ void CGuild::unregisterGuild() // //} - //---------------------------------------------------------------------------- -void CGuild::removeMission(CMissionGuild * mission, TMissionResult result) +void CGuild::removeMission( uint idx, TMissionResult result) { - /// todo guild mission + if ( idx >= _Missions.size() ) + return; + + /// if the mission was finished, the result is success + if ( _Missions[idx]->getFinished() ) + { + if ( _Missions[idx]->getMissionSuccess() ) + result = mr_success; + else + result = mr_fail; + } + + CMissionTemplate *tpl = CMissionManager::getInstance()->getTemplate(_Missions[idx]->getTemplateId()); + + if ( tpl && !tpl->Tags.NoList ) + { + _Missions[idx]->clearUsersJournalEntry(); + } + + CMissionManager::getInstance()->deInstanciateMission(_Missions[idx]); + delete _Missions[idx]; + _Missions.erase(_Missions.begin() + idx) ; } //---------------------------------------------------------------------------- void CGuild::addSuccessfulMission(CMissionTemplate * templ) { - /// todo guild mission + /*TMissionHistory &mh = _MissionHistories[templ.Alias]; + mh.Successfull = true;*/ + /// TODO: Add the mission histories } //---------------------------------------------------------------------------- -bool CGuild::processMissionEvent( CMissionEvent & event, TAIAlias alias ) +bool CGuild::processMissionEvent( CMissionEvent & event, TAIAlias alias) { - /// todo guild mission + return true; +} + +//---------------------------------------------------------------------------- +bool CGuild::isMissionSuccessfull(TAIAlias alias) +{ + /*std::map::iterator it(_MissionHistories.find(alias)); + if (it != _MissionHistories.end()) + return it->second.Successfull;*/ + /// TODO: Add the mission histories return false; } diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index ecedbf9ee..342174ebb 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -27,6 +27,7 @@ #include "outpost_manager/outpost_guild_db_updater.h" #include "guild_interface.h" #include "database_guild.h" +#include "mission_manager/mission_guild.h" class CMissionGuild; class CGuildMember; @@ -176,17 +177,30 @@ public: ///\name Mission management //@{ - void removeMission(CMissionGuild * mission, TMissionResult result); + void removeMission(CMissionGuild * mission, TMissionResult result) + { + for (uint i = 0; i < _Missions.size(); i++) + { + if ( _Missions[i] == mission ) + { + removeMission(i, result); + } + } + } + void removeMission( uint idx, TMissionResult result); void addSuccessfulMission(CMissionTemplate * templ); bool processMissionEvent( CMissionEvent & event, TAIAlias alias = CAIAliasTranslator::Invalid); + bool isMissionSuccessfull(TAIAlias alias); ///\return the mission inline std::vector & getMissions() { - return std::vector(); - // To Do + return _Missions; + } + void addMission(CMissionGuild* guildMission) + { + _Missions.push_back(guildMission); + guildMission->updateUsersJournalEntry(); } - void addMission(CMissionGuild* guildMission) {} - bool isMissionSuccessfull(TAIAlias alias) { return false; } //@} /// inventory management @@ -359,6 +373,9 @@ private: /// list of outposts challenged by guild std::vector _ChallengedOutposts; + ///the missions took by the guild + std::vector _Missions; + NLMISC_COMMAND_FRIEND( guildDB ); }; #endif // RY_GUILD_H From c83fd1832bf8cf015f065f863323e09e336b3258 Mon Sep 17 00:00:00 2001 From: sfb Date: Fri, 1 Jul 2011 07:19:46 -0500 Subject: [PATCH 029/215] Changed: Implementing server list in settings page. Added dialog for editing/adding entries. Can add, remove and edit entries. Still need to implement read/write settings and provide a method to connect the table to the publishing options table. --HG-- branch : branch-mission-compiler-qt --- .../plugins/mission_compiler/CMakeLists.txt | 3 +- .../mission_compiler_main_window.cpp | 1 + .../mission_compiler_plugin.cpp | 4 +- .../mission_compiler_settings_page.cpp | 120 +++++++----------- .../mission_compiler_settings_page.h | 12 +- .../mission_compiler_settings_page.ui | 61 +-------- .../mission_compiler/server_entry_dialog.cpp | 89 +++++++++++++ .../mission_compiler/server_entry_dialog.h | 59 +++++++++ .../mission_compiler/server_entry_dialog.ui | 110 +++++++++------- 9 files changed, 270 insertions(+), 189 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt index 4919bceff..f277a4b14 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/CMakeLists.txt @@ -11,7 +11,8 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin. SET(OVQT_PLUG_MISSION_COMPILER_HDR mission_compiler_plugin.h mission_compiler_main_window.h - mission_compiler_settings_page.h) + mission_compiler_settings_page.h + server_entry_dialog.h) SET(OVQT_PLUG_MISSION_COMPILER_UIS mission_compiler_main_window.ui server_entry_dialog.ui mission_compiler_settings_page.ui) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index 95d7ca50f..9bab135d4 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -79,6 +79,7 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : settings->endGroup(); NLLIGO::Register(); + // TODO try/catch exception. Crashes if path invalid. m_ligoConfig.readPrimitiveClass(NLMISC::CPath::lookup("world_editor_classes.xml").c_str(), false); NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &m_ligoConfig; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp index 7bd06de91..d67128a50 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp @@ -17,6 +17,8 @@ #include #include +#include "mission_compiler_settings_page.h" + namespace Plugin { @@ -35,7 +37,7 @@ bool MissionCompilerPlugin::initialize(ExtensionSystem::IPluginManager *pluginMa Q_UNUSED(errorString); _plugMan = pluginManager; - //addAutoReleasedObject(new CZonePainterSettingsPage(this)); + addAutoReleasedObject(new MissionCompilerSettingsPage(this)); addAutoReleasedObject(new CMissionCompilerContext(this)); //addAutoReleasedObject(new CCoreListener(this)); return true; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp index f500933a7..028ac51db 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp @@ -20,7 +20,7 @@ #include "../core/core_constants.h" #include "../core/icore.h" -#include "ui_server_entry_dialog.h" +#include "server_entry_dialog.h" // NeL includes #include @@ -58,12 +58,12 @@ QString MissionCompilerSettingsPage::trName() const QString MissionCompilerSettingsPage::category() const { - return QLatin1String("MissionCompilerSettings"); + return QLatin1String("Mission Compiler"); } QString MissionCompilerSettingsPage::trCategory() const { - return tr("MissionCompilerSettings"); + return tr("Mission Compiler"); } QIcon MissionCompilerSettingsPage::categoryIcon() const @@ -77,19 +77,15 @@ QWidget *MissionCompilerSettingsPage::createPage(QWidget *parent) m_ui.setupUi(m_page); readSettings(); - checkEnabledButton(); - connect(m_ui.addToolButton, SIGNAL(clicked()), this, SLOT(addPath())); - connect(m_ui.removeToolButton, SIGNAL(clicked()), this, SLOT(delPath())); - connect(m_ui.upToolButton, SIGNAL(clicked()), this, SLOT(upPath())); - connect(m_ui.downToolButton, SIGNAL(clicked()), this, SLOT(downPath())); - //connect(m_ui.resetToolButton, SIGNAL(clicked()), m_ui.serversTreeWidget, SLOT(clear())); + connect(m_ui.addToolButton, SIGNAL(clicked()), this, SLOT(addServer())); + connect(m_ui.removeToolButton, SIGNAL(clicked()), this, SLOT(delServer())); + connect(m_ui.serversTableWidget, SIGNAL(cellDoubleClicked(int,int)), this, SLOT(editServer(int,int))); return m_page; } void MissionCompilerSettingsPage::apply() { writeSettings(); - applySearchPaths(); } void MissionCompilerSettingsPage::finish() @@ -98,74 +94,57 @@ void MissionCompilerSettingsPage::finish() m_page = 0; } -void MissionCompilerSettingsPage::applySearchPaths() +void MissionCompilerSettingsPage::editServer(int row, int column) { - QStringList paths, remapExt; - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - if (m_recurse) - paths = settings->value(Core::Constants::RECURSIVE_SEARCH_PATHS).toStringList(); - else - paths = settings->value(Core::Constants::SEARCH_PATHS).toStringList(); + ServerEntryDialog serverEntryDialog; + serverEntryDialog.setModal(true); + serverEntryDialog.show(); - remapExt = settings->value(Core::Constants::REMAP_EXTENSIONS).toStringList(); - settings->endGroup(); + // Copy the values from the row to the dialog. + QTableWidgetItem *item1 = m_ui.serversTableWidget->item(row,0); + QTableWidgetItem *item2 = m_ui.serversTableWidget->item(row,1); + QTableWidgetItem *item3 = m_ui.serversTableWidget->item(row,2); + serverEntryDialog.setServerName(item1->text()); + serverEntryDialog.setTextPath(item2->text()); + serverEntryDialog.setPrimPath(item3->text()); - for (int i = 1; i < remapExt.size(); i += 2) - NLMISC::CPath::remapExtension(remapExt.at(i - 1).toStdString(), remapExt.at(i).toStdString(), true); - - Q_FOREACH(QString path, paths) + if(serverEntryDialog.exec()) { - NLMISC::CPath::addSearchPath(path.toStdString(), m_recurse, false); + item1->setText(serverEntryDialog.getServerName()); + item2->setText(serverEntryDialog.getTextPath()); + item3->setText(serverEntryDialog.getPrimPath()); } } -void MissionCompilerSettingsPage::addPath() +void MissionCompilerSettingsPage::addServer() { - Ui::ServerEntryDialog serverEntryDialog; + ServerEntryDialog serverEntryDialog; + serverEntryDialog.setModal(true); + serverEntryDialog.show(); + - //QString newPath = QFileDialog::getExistingDirectory(m_page, "", lastDir); - //if (!newPath.isEmpty()) - //{ - // QTreeWidgetItem *newItem = new QTreeWidgetItem; - // newItem->setText(newPath); - // newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - // m_ui.serversTreeWidget->addItem(newItem); - // lastDir = newPath; - //} + if(serverEntryDialog.exec()) + { + int row = m_ui.serversTableWidget->rowCount(); + m_ui.serversTableWidget->insertRow(row); + QTableWidgetItem *item1 = new QTableWidgetItem(serverEntryDialog.getServerName()); + QTableWidgetItem *item2 = new QTableWidgetItem(serverEntryDialog.getTextPath()); + QTableWidgetItem *item3 = new QTableWidgetItem(serverEntryDialog.getPrimPath()); - //checkEnabledButton(); + m_ui.serversTableWidget->setItem(row, 0, item1); + m_ui.serversTableWidget->setItem(row, 1, item2); + m_ui.serversTableWidget->setItem(row, 2, item3); + } } -void MissionCompilerSettingsPage::delPath() +void MissionCompilerSettingsPage::delServer() { - //QTreeWidgetItem *removeItem = m_ui.serversTreeWidget->takeItem(m_ui.serversTreeWidget->currentRow()); - //if (!removeItem) - // delete removeItem; - - //checkEnabledButton(); -} - -void MissionCompilerSettingsPage::upPath() -{ - //int currentRow = m_ui.serversTreeWidget->currentRow(); - //if (!(currentRow == 0)) - //{ - // QListWidgetItem *item = m_ui.serversListWidget->takeItem(currentRow); - // m_ui.serversListWidget->insertItem(--currentRow, item); - // m_ui.serversListWidget->setCurrentRow(currentRow); - //} -} - -void MissionCompilerSettingsPage::downPath() -{ - //int currentRow = m_ui.serversListWidget->currentRow(); - //if (!(currentRow == m_ui.serversListWidget->count()-1)) - //{ - // QListWidgetItem *item = m_ui.serversListWidget->takeItem(currentRow); - // m_ui.serversTreeWidget->insertItem(++currentRow, item); - // m_ui.serversTreeWidget->setCurrentRow(currentRow); - //} + QList selectedItems = m_ui.serversTableWidget->selectedItems(); + while(selectedItems.size() > 0) + { + m_ui.serversTableWidget->removeRow(selectedItems.back()->row()); + selectedItems = m_ui.serversTableWidget->selectedItems(); + } } void MissionCompilerSettingsPage::readSettings() @@ -203,15 +182,4 @@ void MissionCompilerSettingsPage::writeSettings() //settings->sync(); } -void MissionCompilerSettingsPage::checkEnabledButton() -{ - //bool bEnabled = true; - //if (m_ui.serversTreeWidget->count() == 0) - // bEnabled = false; - - //m_ui.removeToolButton->setEnabled(bEnabled); - //m_ui.upToolButton->setEnabled(bEnabled); - //m_ui.downToolButton->setEnabled(bEnabled); -} - } /* namespace Plugin */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h index 43fc4ec5c..32b59e050 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h @@ -50,21 +50,15 @@ public: void apply(); void finish(); - // Set of the search paths(not recursive) and the remap extensions (loading from settings file) - void applySearchPaths(); - private Q_SLOTS: - void addPath(); - void delPath(); - void upPath(); - void downPath(); + void addServer(); + void delServer(); + void editServer(int row, int column); private: void readSettings(); void writeSettings(); - void checkEnabledButton(); - bool m_recurse; QWidget *m_page; Ui::MissionCompilerSettingsPage m_ui; }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui index 905cc5185..2ee2c2bce 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.ui @@ -86,64 +86,6 @@ - - - - - 0 - 0 - - - - Up - - - - - - - :/buttons/images/ic_nel_up_item.png:/buttons/images/ic_nel_up_item.png - - - - 20 - 20 - - - - true - - - - - - - - 0 - 0 - - - - Down - - - - - - - :/buttons/images/ic_nel_down_item.png:/buttons/images/ic_nel_down_item.png - - - - 20 - 20 - - - - true - - - @@ -197,6 +139,9 @@ + + QAbstractItemView::NoEditTriggers + true diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.cpp new file mode 100644 index 000000000..b930eb5ec --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.cpp @@ -0,0 +1,89 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Project includes +#include "server_entry_dialog.h" + +#include "ui_server_entry_dialog.h" + +// NeL includes + +// Qt includes +#include + +namespace Plugin +{ + +ServerEntryDialog::ServerEntryDialog(QWidget *parent) + : QDialog(parent), + m_ui(new Ui::ServerEntryDialog) +{ + m_ui->setupUi(this); + + connect(m_ui->serverTextPathButton, SIGNAL(clicked()), this, SLOT(lookupTextPath())); + connect(m_ui->serverPrimPathButton, SIGNAL(clicked()), this, SLOT(lookupPrimPath())); +} + +ServerEntryDialog::~ServerEntryDialog() +{ + delete m_ui; +} + +QString ServerEntryDialog::getServerName() +{ + return m_ui->serverNameEdit->text(); +} + +QString ServerEntryDialog::getTextPath() +{ + return m_ui->serverTextPathEdit->text(); +} + +QString ServerEntryDialog::getPrimPath() +{ + return m_ui->serverPrimPathEdit->text(); +} + +void ServerEntryDialog::setServerName(QString name) +{ + m_ui->serverNameEdit->setText(name); +} + +void ServerEntryDialog::setTextPath(QString path) +{ + m_ui->serverTextPathEdit->setText(path); +} + +void ServerEntryDialog::setPrimPath(QString path) +{ + m_ui->serverPrimPathEdit->setText(path); +} + +void ServerEntryDialog::lookupTextPath() +{ + QString curPath = m_ui->serverTextPathEdit->text(); + QString path = QFileDialog::getExistingDirectory(this, "", curPath); + m_ui->serverTextPathEdit->setText(path); +} + +void ServerEntryDialog::lookupPrimPath() +{ + QString curPath = m_ui->serverPrimPathEdit->text(); + QString path = QFileDialog::getExistingDirectory(this, "", curPath); + m_ui->serverPrimPathEdit->setText(path); +} +} /* namespace Plugin */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.h new file mode 100644 index 000000000..e2a1d5016 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.h @@ -0,0 +1,59 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + +#ifndef SERVER_ENTRY_DIALOG_H +#define SERVER_ENTRY_DIALOG_H + +#include + +namespace Ui { + class ServerEntryDialog; +} + +namespace Plugin +{ +/** +@class ServerEntryDialog +*/ +class ServerEntryDialog : public QDialog +{ + Q_OBJECT + +public: + explicit ServerEntryDialog(QWidget *parent = 0); + ~ServerEntryDialog(); + + QString getServerName(); + QString getTextPath(); + QString getPrimPath(); + + void setServerName(QString name); + void setTextPath(QString path); + void setPrimPath(QString path); + +public Q_SLOTS: + void lookupTextPath(); + void lookupPrimPath(); + +private: + Ui::ServerEntryDialog *m_ui; +}; + +} // namespace Plugin + +#endif // SERVER_ENTRY_DIALOG_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.ui index 5f7c6d827..002af82eb 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.ui @@ -6,15 +6,15 @@ 0 0 - 400 - 300 + 488 + 175 Dialog - + Qt::Horizontal @@ -24,48 +24,70 @@ - - - - Server Name - - - - - - - Server Text Path - - - - - - - Server Primitive Path - - - - - - - - - - - - - - - - ... - - - - - - - ... + + + + Server Settings + + + + + Server Name + + + + + + + + + + Server Text Path + + + + + + + + + + ... + + + + + + + Server Primitive Path + + + + + + + + + + ... + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + From e55bbed5b4262a33154f46e122a40edb7739ef69 Mon Sep 17 00:00:00 2001 From: cemycc Date: Fri, 1 Jul 2011 21:51:41 +0300 Subject: [PATCH 030/215] Changed: #1307 New structure for subwindows from QMdiArea --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/CMakeLists.txt | 4 +- .../translation_manager/editor_worksheet.cpp | 353 +++++++++++++++++ .../translation_manager/editor_worksheet.h | 48 +++ .../translation_manager_editor.h | 35 ++ .../translation_manager_main_window.cpp | 371 +++++++----------- .../translation_manager_main_window.h | 52 ++- .../translation_manager_main_window.ui | 26 +- .../translation_manager_plugin.cpp | 2 +- 8 files changed, 606 insertions(+), 285 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt index ec3980f05..edc0a60e5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -11,7 +11,9 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin. SET(OVQT_PLUG_TRANSLATION_MANAGER_HDR translation_manager_plugin.h translation_manager_main_window.h - translation_manager_settings_page.h) + translation_manager_settings_page.h + translation_manager_editor.h + editor_worksheet.h) SET(OVQT_PLUG_TRANSLATION_MANAGER_UIS translation_manager_settings_page.ui translation_manager_main_window.ui) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp new file mode 100644 index 000000000..ee32e3979 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -0,0 +1,353 @@ +// Object Viewer Qt - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Dzmitry Kamiahin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "editor_worksheet.h" +#include +// Qt includes +#include +#include +#include +#include +#include + +using namespace std; + +struct TEntryInfo +{ + string SheetName; +}; + +set getGenericNames(); +void cleanGenericNames(); +map getSimpleNames(); +void cleanSimpleNames(); +void setPathsForPrimitives(map > config_paths, string ligo_class_file); +void extractBotNamesFromPrimitives(); +string cleanupName(const std::string &name); +ucstring cleanupUcName(const ucstring &name); + +namespace Plugin { + + + +void CEditorWorksheet::open(QString filename) +{ + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + bool hasHashValue = false; + table_editor = new QTableWidget(); + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + table_editor->setColumnCount(wk_file.ColCount - 1); + hasHashValue = true; + } else { + table_editor->setColumnCount(wk_file.ColCount); + } + table_editor->setRowCount(wk_file.size() - 1); + + // read columns name + for(unsigned int i = 0; i < wk_file.ColCount; i++) + { + if(hasHashValue && i == 0) + { + // we don't show the column with hash value + } else { + QTableWidgetItem *col = new QTableWidgetItem(); + ucstring col_name = wk_file.getData(0, i); + col->setText(tr(col_name.toString().c_str())); + if(hasHashValue) + { + table_editor->setHorizontalHeaderItem(i - 1, col); + } else { + table_editor->setHorizontalHeaderItem(i, col); + } + } + } + + // read rows + for(unsigned int i = 1; i < wk_file.size(); i++) + { + for(unsigned int j = 0; j < wk_file.ColCount; j++) + { + if(hasHashValue && j == 0) + { + // we don't show the column with hash value + } else { + QTableWidgetItem *row = new QTableWidgetItem(); + ucstring row_value = wk_file.getData(i, j); + row->setText(tr(row_value.toString().c_str())); + if(hasHashValue) + { + table_editor->setItem(i - 1, j - 1, row); + } else { + table_editor->setItem(i - 1, j, row); + } + } + } + } + setCurrentFile(filename); + setAttribute(Qt::WA_DeleteOnClose); + setWidget(table_editor); + table_editor->resizeColumnsToContents(); + table_editor->resizeRowsToContents(); + // set editor signals + connect(table_editor, SIGNAL(cellChanged(int,int) ), this, SLOT(worksheetEditorChanged(int,int))); + } else { + QErrorMessage error; + error.showMessage("This file is not a worksheet file."); + error.exec(); + } + +} + +void CEditorWorksheet::activateWindow() +{ + showMaximized(); + +} + +void CEditorWorksheet::save() +{ + STRING_MANAGER::TWorksheet wk_file; + loadExcelSheet(current_file.toStdString(), wk_file, true); + uint rowIdx; + uint colIdx = 0; + bool hasHashValue = false; + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + hasHashValue = true; + colIdx = 1; + } + for(int i = 0; i < table_editor->rowCount(); i++) + { + // maybe extra rows ? + if((unsigned)table_editor->rowCount() > (wk_file.size() - 1)) + { + rowIdx = wk_file.size(); + wk_file.resize(rowIdx + table_editor->rowCount() - wk_file.size() + 1); + } + for(int j = 0; j < table_editor->columnCount(); j++) + { + ucstring tvalue; + ucstring colname; + uint rowIdf; + QString tvalueQt = table_editor->item(i, j)->text(); + tvalue = ucstring(tvalueQt.toStdString()); + colname = wk_file.getData(0, j + colIdx); + + rowIdf = uint(i + 1); + if(wk_file.findRow(j + colIdx, colname, rowIdf)) + { + if(wk_file.getData(i + 1, j + colIdx) != tvalue) + { + wk_file.setData(i + 1, j + colIdx, tvalue); + } + } else { + wk_file.setData(i + 1, j + colIdx, tvalue); + } + } + } + if(hasHashValue) + { + // rewrite the hash codes + makeHashCode(wk_file, true); + } + // write to file + ucstring s = prepareExcelSheet(wk_file); + NLMISC::CI18N::writeTextFile(current_file.toStdString(), s, false); + setCurrentFile(current_file); +} + +void CEditorWorksheet::saveAs(QString filename) +{ + STRING_MANAGER::TWorksheet new_file, wk_file; + loadExcelSheet(current_file.toStdString(), wk_file, true); + // set columns + new_file.resize(new_file.size() + 1); + for(unsigned int i = 0; i < wk_file.ColCount; i++) + { + ucstring col_name = wk_file.getData(0, i); + new_file.insertColumn(new_file.ColCount); + new_file.setData(0, new_file.ColCount - 1, col_name); + } + // read all the rows from table + uint rowIdx; + uint colIdx = 0; + bool hasHashValue = false; + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + hasHashValue = true; + colIdx = 1; + } + for(int i = 0; i < table_editor->rowCount(); i++) + { + rowIdx = new_file.size(); + new_file.resize(new_file.size() + 1); + for(int j = 0; j < table_editor->columnCount(); j++) + { + QTableWidgetItem* item = table_editor->item(i, j); + new_file.setData(rowIdx, j + colIdx, ucstring(item->text().toStdString())); + } + } + if(hasHashValue) + { + // rewrite the hash codes + makeHashCode(wk_file, true); + } + ucstring s = prepareExcelSheet(new_file); + NLMISC::CI18N::writeTextFile(filename.toStdString(), s, false); + setCurrentFile(filename); +} + +void CEditorWorksheet::insertRow() +{ + int last_row = table_editor->rowCount(); + table_editor->setRowCount(last_row + 1); + for(int j = 0; j < table_editor->columnCount(); j++) + { + QTableWidgetItem* item = new QTableWidgetItem(); + //item->setText(QString(" ")); + table_editor->setItem(last_row, j, item); + } +} + +void CEditorWorksheet::deleteRow() +{ + int selected_row = table_editor->currentRow(); + QMessageBox msgBox; + msgBox.setText("The row will be deleted."); + msgBox.setInformativeText("Do you want to delete the selected row ?"); + msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); + msgBox.setDefaultButton(QMessageBox::No); + int ret = msgBox.exec(); + + if(ret == QMessageBox::Yes) + { + table_editor->removeRow(selected_row); + } + + table_editor->clearFocus(); + table_editor->clearSelection(); + return; +} + +void CEditorWorksheet::worksheetEditorChanged(int row, int column) +{ + +} + +void CEditorWorksheet::extractBotNames() +{ + bool modified = false; +// get SimpleNames + { + map SimpleNames = getSimpleNames(); + map::iterator it(SimpleNames.begin()), last(SimpleNames.end()); + + for (; it != last; ++it) + { + QList search_results = table_editor->findItems(tr(it->first.c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + QTableWidgetItem *bot_name_row = new QTableWidgetItem(); + bot_name_row->setText(tr(it->first.c_str())); + bot_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 0, bot_name_row); + QTableWidgetItem *translation_name_row = new QTableWidgetItem(); + translation_name_row->setBackgroundColor(QColor("#F75D59")); + translation_name_row->setText(tr(it->first.c_str())); + table_editor ->setItem(currentRow , 1, translation_name_row); + QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); + sheet_name_row->setText(tr(it->second.SheetName.c_str())); + sheet_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 2, sheet_name_row); + if(!modified) modified = true; + } + } + cleanSimpleNames(); + } + // get GenericNames + { + set GenericNames = getGenericNames(); + set::iterator it(GenericNames.begin()), last(GenericNames.end()); + for (; it != last; ++it) + { + string gnName = "gn_" + cleanupName(*it); + QList search_results = table_editor->findItems(tr((*it).c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + QTableWidgetItem *bot_name_row = new QTableWidgetItem(); + bot_name_row->setText(tr((*it).c_str())); + bot_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 0, bot_name_row); + QTableWidgetItem *translation_name_row = new QTableWidgetItem(); + translation_name_row->setBackgroundColor(QColor("#F75D59")); + translation_name_row->setText(tr(gnName.c_str())); + table_editor ->setItem(currentRow , 1, translation_name_row); + QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); + sheet_name_row->setText(" "); + sheet_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 2, sheet_name_row); + if(!modified) modified = true; + } + } + cleanGenericNames(); + } + if(modified) + { + setWindowModified(true); + } + +} + +void CEditorWorksheet::setCurrentFile(QString filename) +{ + QFileInfo *file = new QFileInfo(filename); + current_file = file->canonicalFilePath(); + setWindowModified(false); + setWindowTitle(file->fileName() + "[*]"); + setWindowFilePath(current_file); +} + +void CEditorWorksheet::closeEvent(QCloseEvent *event) +{ + close(); + event->accept(); + +} + +bool CEditorWorksheet::isBotNamesTable() +{ + bool status = true; + if(table_editor->horizontalHeaderItem(0)->text() != "bot name" + || table_editor->horizontalHeaderItem(1)->text() != "translated name" + || table_editor->horizontalHeaderItem(2)->text() != "sheet_name") + { + status = false; + } + + return status; +} + +} + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h new file mode 100644 index 000000000..71e86af84 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -0,0 +1,48 @@ + +#ifndef EDITOR_WORKSHEET_H +#define EDITOR_WORKSHEET_H + +// Nel includes +#include "nel/misc/types_nl.h" +#include "nel/misc/sheet_id.h" +#include "nel/misc/path.h" +#include "nel/misc/diff_tool.h" + +// Qt includes +#include +#include +#include +#include +#include + +#include "translation_manager_editor.h" + +namespace Plugin { + +class CEditorWorksheet : public CEditor +{ + Q_OBJECT +private: + QTableWidget* table_editor; +public: + CEditorWorksheet(QMdiArea* parent) : CEditor(parent) {} + CEditorWorksheet() : CEditor() {} + void open(QString filename); + void save(); + void saveAs(QString filename); + void activateWindow(); + void extractBotNames(); + bool isBotNamesTable(); + void closeEvent(QCloseEvent *event); +private Q_SLOTS: + void worksheetEditorChanged(int,int); + void insertRow(); + void deleteRow(); +private: + void setCurrentFile(QString filename); + +}; + +}; +#endif /* EDITOR_WORKSHEET_H */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h new file mode 100644 index 000000000..605d11d6e --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h @@ -0,0 +1,35 @@ + +#ifndef TRANSLATION_MANAGER_EDITOR_H +#define TRANSLATION_MANAGER_EDITOR_H + +#include +#include +#include +#include + +namespace Plugin { + +class CEditor : public QMdiSubWindow { +Q_OBJECT +protected: + QString current_file; + int editor_type; +public: + CEditor(QMdiArea* parent) : QMdiSubWindow(parent) {} + CEditor() : QMdiSubWindow() {} + virtual void open(QString filename) =0; + virtual void save() =0; + virtual void saveAs(QString filename) =0; + virtual void activateWindow() =0; +public: + QString subWindowFilePath() + { + return current_file; + } +}; + +} + + +#endif /* TRANSLATION_MANAGER_EDITOR_H */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 946ed78fe..8b8c50a22 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -16,6 +16,8 @@ // along with this program. If not, see . #include "translation_manager_main_window.h" +#include "editor_worksheet.h" + // Project system includes #include "../core/icore.h" #include "../core/core_constants.h" @@ -40,6 +42,9 @@ #include #include #include +#include +#include + struct TEntryInfo @@ -63,9 +68,12 @@ CMainWindow::CMainWindow(QWidget *parent) : QMainWindow(parent) { _ui.setupUi(this); + _ui.mdiArea->closeAllSubWindows(); connect(_ui.mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),this, SLOT(activeSubWindowChanged())); - + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); + // set extraction scripts counters execution_count["extract_bot_names"] = 0; @@ -96,26 +104,52 @@ void CMainWindow::createToolbar() connect(extractBotNamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); // Windows menu - windowMapper = new QSignalMapper(this); - connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); windowMenu = new QMenu(tr("&Windows..."), _ui.toolBar); - windowMenu->setIcon(QIcon(Core::Constants::ICON_PILL)); + windowMenu->setIcon(QIcon(Core::Constants::ICON_PILL)); + updateWindowsList(); _ui.toolBar->addAction(windowMenu->menuAction()); connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowsList())); } +void CMainWindow::updateToolbar(QMdiSubWindow *window) +{ + if(_ui.mdiArea->subWindowList().size() > 0) + if(QString(window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + { + QAction *insertRowAct = windowMenu->addAction("Insert new row"); + connect(insertRowAct, SIGNAL(triggered()), window, SLOT(insertRow())); + QAction *deleteRowAct = windowMenu->addAction("Delete row"); + connect(deleteRowAct, SIGNAL(triggered()), window, SLOT(deleteRow())); + + } +} + +void CMainWindow::setActiveSubWindow(QWidget* window) +{ + if (!window) + { + return; + } + QMdiSubWindow *cwindow = qobject_cast(window); + _ui.mdiArea->setActiveSubWindow(cwindow); +} + void CMainWindow::activeSubWindowChanged() { - updateWindowsList(); + } void CMainWindow::updateWindowsList() { - int i = 0; windowMenu->clear(); - QList windows = _ui.mdiArea->subWindowList(); - for (QList::iterator it = windows.begin(); it != windows.end(); ++it) { - QString window_file = QFileInfo((*it)->widget()->windowFilePath()).fileName(); + QMdiSubWindow *current_window = _ui.mdiArea->activeSubWindow(); + QList subWindows = _ui.mdiArea->subWindowList(); + + updateToolbar(current_window); + + for(int i = 0; i < subWindows.size(); ++i) + { + QString window_file = QFileInfo(subWindows.at(i)->windowFilePath()).fileName(); QString action_text; if (i < 9) { action_text = tr("&%1 %2").arg(i + 1).arg(window_file); @@ -124,149 +158,49 @@ void CMainWindow::updateWindowsList() } QAction *action = windowMenu->addAction(action_text); action->setCheckable(true); - action->setChecked((*it) == _ui.mdiArea->activeSubWindow()); - connect(action, SIGNAL(triggered()), windowMapper, SLOT(map())); - windowMapper->setMapping(action, windows.at(i)); - i++; + action->setChecked(subWindows.at(i) == current_window); + connect(action, SIGNAL(triggered()), windowMapper, SLOT(map())); + windowMapper->setMapping(action, subWindows.at(i)); } } void CMainWindow::open() { QString file_name = QFileDialog::getOpenFileName(this); - if (!file_name.isEmpty()) - { - STRING_MANAGER::TWorksheet wk_file; - if(loadExcelSheet(file_name.toStdString(), wk_file, true) == true) - { - bool hasHashValue = false; - QTableWidget *wk_table = new QTableWidget(); - wk_table->setToolTip(file_name); - wk_table->setWindowFilePath(file_name); - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - wk_table->setColumnCount(wk_file.ColCount - 1); - hasHashValue = true; - } else { - wk_table->setColumnCount(wk_file.ColCount); - } - wk_table->setRowCount(wk_file.size() - 1); - // read columns name - - for(unsigned int i = 0; i < wk_file.ColCount; i++) - { - if(hasHashValue && i == 0) - { - // we don't show the column with hash value - } else { - QTableWidgetItem *col = new QTableWidgetItem(); - ucstring col_name = wk_file.getData(0, i); - col->setText(tr(col_name.toString().c_str())); - if(hasHashValue) - { - wk_table->setHorizontalHeaderItem(i - 1, col); - } else { - wk_table->setHorizontalHeaderItem(i, col); - } - } - } - // read rows - - for(unsigned int i = 1; i < wk_file.size(); i++) - { - for(unsigned int j = 0; j < wk_file.ColCount; j++) - { - if(hasHashValue && j == 0) - { - // we don't show the column with hash value - } else { - QTableWidgetItem *row = new QTableWidgetItem(); - ucstring row_value = wk_file.getData(i, j); - row->setText(tr(row_value.toString().c_str())); - if(hasHashValue) - { - wk_table->setItem(i - 1, j - 1, row); - } else { - wk_table->setItem(i - 1, j, row); - } - } + if(!file_name.isEmpty()) + { + list subWindows = convertSubWindowList(_ui.mdiArea->subWindowList()); + list::iterator it = subWindows.begin(); + CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + for(; it != subWindows.end(); ++it) + { + QString sw_file = (*it)->subWindowFilePath(); + if(file_name == sw_file) + { + if((*it) != current_window) + { + (*it)->activateWindow(); } + return; } - - QMdiSubWindow *sub_window = new QMdiSubWindow(_ui.mdiArea); - sub_window->setWidget(wk_table); - wk_table->resizeColumnsToContents(); - wk_table->resizeRowsToContents(); - wk_table->showMaximized(); - sub_window->activateWindow(); - // set editor signals - connect(wk_table, SIGNAL(cellChanged(int,int) ), this, SLOT(sheetEditorChanged(int,int))); - updateWindowsList(); - } else { - QErrorMessage error_settings; - error_settings.showMessage("This file is not a worksheet file."); - error_settings.exec(); + } + if(isWorksheetEditor(file_name)) + { + CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); + new_window->open(file_name); + new_window->activateWindow(); } } } -void CMainWindow::sheetEditorChanged(int row, int column) -{ - saveAct->setEnabled(true); - QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); - if(modifiedCells.find(current_window) != modifiedCells.end()) // founded - { - list cells = modifiedCells[current_window]; - bool overwriteResult = false; - for(list::iterator it = cells.begin(); it != cells.end(); ++it) - { - if((*it).row == row && (*it).col == column ) - overwriteResult = true; - } - if(overwriteResult == false) - { - CCelPos v; - v.row = row; - v.col = column; - cells.push_back(v); - } - } else { // not found - list cells; - CCelPos v; - v.row = row; - v.col = column; - cells.push_back(v); - modifiedCells[current_window] = cells; - } -} - void CMainWindow::save() { - QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); + CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor { - QWidget *subwindow_widget = current_window->widget(); - QTableWidget *table_editor = qobject_cast(subwindow_widget); - QString file_path = table_editor->windowFilePath(); - - if(modifiedCells.find(current_window) != modifiedCells.end()) - { - STRING_MANAGER::TWorksheet wk_file; - loadExcelSheet(file_path.toStdString(), wk_file, true); - list cells = modifiedCells[current_window]; - for(list::iterator it = cells.begin(); it != cells.end(); ++it) - { - QTableWidgetItem* edited_item = table_editor->item((*it).row, (*it).col); - wk_file.setData((*it).row + 1, (*it).col, ucstring(edited_item->text().toStdString())); - cells.erase(it); - } - ucstring s = prepareExcelSheet(wk_file); - NLMISC::CI18N::writeTextFile(file_path.toStdString(), s, false); - if(cells.size() == 0) - modifiedCells.erase(current_window); - } + current_window->save(); } } @@ -280,37 +214,10 @@ void CMainWindow::saveAs() if (!file_name.isEmpty()) { - QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); - + CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor { - QWidget *subwindow_widget = current_window->widget(); - QTableWidget *table_editor = qobject_cast(subwindow_widget); - QString orig_file_path = table_editor->windowFilePath(); - STRING_MANAGER::TWorksheet new_file, wk_file; - loadExcelSheet(orig_file_path.toStdString(), wk_file, true); - // set columns - new_file.resize(new_file.size() + 1); - for(unsigned int i = 0; i < wk_file.ColCount; i++) - { - ucstring col_name = wk_file.getData(0, i); - new_file.insertColumn(new_file.ColCount); - new_file.setData(0, new_file.ColCount - 1, col_name); - } - // read all the rows from table - uint rowIdx; - for(int i = 0; i < table_editor->rowCount(); i++) - { - rowIdx = new_file.size(); - new_file.resize(new_file.size() + 1); - for(int j = 0; j < table_editor->columnCount(); j++) - { - QTableWidgetItem* item = table_editor->item(i, j); - new_file.setData(rowIdx, j, ucstring(item->text().toStdString())); - } - } - ucstring s = prepareExcelSheet(new_file); - NLMISC::CI18N::writeTextFile(file_name.toStdString(), s, false); + current_window->saveAs(file_name); } } @@ -320,72 +227,43 @@ void CMainWindow::extractBotNames() { if(verifySettings() == true) { - QMdiSubWindow *current_window = _ui.mdiArea->currentSubWindow(); - if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + if(QString(editor_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor { + CEditorWorksheet* current_window = qobject_cast(editor_window); + QString file_path = current_window->subWindowFilePath(); + if(!current_window->isBotNamesTable()) + { + list subWindows = convertSubWindowList(_ui.mdiArea->subWindowList()); + list::iterator it = subWindows.begin(); + bool finded = false; + for(; it != subWindows.end(), finded != true; ++it) + { + current_window = qobject_cast((*it)); + file_path = current_window->subWindowFilePath(); + if(current_window->isBotNamesTable()) + { + finded = true; + current_window->activateWindow(); + } + } + if(!finded) + { + open(); + current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + file_path = current_window->windowFilePath(); + } + } if(execution_count["extract_bot_names"] == 0) setPathsForPrimitives(config_paths, ligo_path); extractBotNamesFromPrimitives(); execution_count["extract_bot_names"] = execution_count["extract_bot_names"] + 1; - - QWidget *subwindow_widget = current_window->widget(); - QTableWidget *table_editor = qobject_cast(subwindow_widget); - // get SimpleNames - { - map SimpleNames = getSimpleNames(); - map::iterator it(SimpleNames.begin()), last(SimpleNames.end()); - - for (; it != last; ++it) - { - QList search_results = table_editor->findItems(tr(it->first.c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - const int currentRow = table_editor->rowCount(); - table_editor->setRowCount(currentRow + 1); - QTableWidgetItem *bot_name_row = new QTableWidgetItem(); - bot_name_row->setText(tr(it->first.c_str())); - bot_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 0, bot_name_row); - QTableWidgetItem *translation_name_row = new QTableWidgetItem(); - translation_name_row->setBackgroundColor(QColor("#F75D59")); - translation_name_row->setText(tr(it->first.c_str())); - table_editor ->setItem(currentRow , 1, translation_name_row); - QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); - sheet_name_row->setText(tr(it->second.SheetName.c_str())); - sheet_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 2, sheet_name_row); - } - } - cleanSimpleNames(); - } - // get GenericNames - { - set GenericNames = getGenericNames(); - set::iterator it(GenericNames.begin()), last(GenericNames.end()); - for (; it != last; ++it) - { - string gnName = "gn_" + cleanupName(*it); - QList search_results = table_editor->findItems(tr((*it).c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - const int currentRow = table_editor->rowCount(); - table_editor->setRowCount(currentRow + 1); - QTableWidgetItem *bot_name_row = new QTableWidgetItem(); - bot_name_row->setText(tr((*it).c_str())); - bot_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 0, bot_name_row); - QTableWidgetItem *translation_name_row = new QTableWidgetItem(); - translation_name_row->setBackgroundColor(QColor("#F75D59")); - translation_name_row->setText(tr(gnName.c_str())); - table_editor ->setItem(currentRow , 1, translation_name_row); - QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); - sheet_name_row->setText(" "); - sheet_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 2, sheet_name_row); - } - } - cleanGenericNames(); - } + + current_window->extractBotNames(); + // if(current_window->isWindowModified()) + // { + + // } } } @@ -413,6 +291,13 @@ void CMainWindow::readSettings() settings->endGroup(); } +void CMainWindow::debug(QString text) +{ + QErrorMessage error_settings; + error_settings.showMessage(text); + error_settings.exec(); +} + bool CMainWindow::verifySettings() { bool count_errors = false; @@ -436,14 +321,7 @@ bool CMainWindow::verifySettings() return !count_errors; } - - void CMainWindow::setActiveSubWindow(QWidget *window) - { - if (!window) - return; - _ui.mdiArea->setActiveSubWindow(qobject_cast(window)); - } - + list CMainWindow::convertQStringList(QStringList listq) { std::list stdlist; @@ -456,9 +334,36 @@ list CMainWindow::convertQStringList(QStringList listq) return stdlist; } +list CMainWindow::convertSubWindowList(QList listq) +{ + list subwindows; + QList::iterator it = listq.begin(); + + for(; it != listq.end(); ++it) + { + CEditor* current_window = qobject_cast((*it)); + subwindows.push_back(current_window); + } + + return subwindows; +} + +bool CMainWindow::isWorksheetEditor(QString filename) +{ + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + return true; + } else { + return false; + } +} + bool CCoreListener::closeMainWindow() const { return true; } -} /* namespace Plugin */ \ No newline at end of file +} /* namespace Plugin */ + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 4df21fa0b..13a3ead97 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -33,29 +33,31 @@ #include #include #include -#include +#include #include #include #include - +#include "translation_manager_editor.h" #include "ui_translation_manager_main_window.h" #include class QWidget; + using namespace std; namespace Plugin { - -struct CCelPos -{ - int col; - int row; -}; +class CMdiSubWindow; + +struct WStatus +{ + bool modified; +}; + class CMainWindow : public QMainWindow { Q_OBJECT @@ -64,9 +66,8 @@ public: virtual ~CMainWindow() {} QUndoStack *m_undoStack; private: - Ui::CMainWindow _ui; - - map > modifiedCells; + + Ui::CMainWindow _ui; // actions QAction *openAct; QAction *saveAct; @@ -86,17 +87,22 @@ private Q_SLOTS: void open(); void save(); void saveAs(); - void sheetEditorChanged(int, int); - void setActiveSubWindow(QWidget *window); void activeSubWindowChanged(); + void setActiveSubWindow(QWidget *window); + void updateWindowsList(); + + void debug(QString text); // TODO private: - void compareBotNames(); + void updateToolbar(QMdiSubWindow *window); bool verifySettings(); void readSettings(); void createMenus(); void createToolbar(); - void updateWindowsList(); + list convertQStringList(QStringList listq); + list convertSubWindowList(QList listq); + bool isWorksheetEditor(QString filename); + }; @@ -111,21 +117,9 @@ public: virtual bool closeMainWindow() const; }; -class CMdiSubWindow : public QMdiSubWindow -{ - private: - int window_type; - public: - int getWType() - { - return window_type; - } - void setWType(int nType) - { - window_type = nType; - } -}; } // namespace Plugin + + #endif // SIMPLE_VIEWER_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui index 395574415..71c139e0a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui @@ -16,27 +16,11 @@ - - - - 0 - 0 - - - - Qt::Horizontal - - - - true - - - - - - - - + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index 91e7686d5..890c589e9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -92,7 +92,7 @@ QStringList TranslationManagerPlugin::dependencies() const { QStringList list; list.append(Core::Constants::OVQT_CORE_PLUGIN); - list.append("ObjectViewer"); + //list.append("ObjectViewer"); return list; } From c175338c7f32afb82727c6e94d9890e88bc5b26c Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 4 Jul 2011 12:38:42 +0200 Subject: [PATCH 031/215] Changed: #1304: Adding Guild mission support in function tickUpdate of CCharacter --HG-- branch : gsoc2011-guildmissions --- .../player_manager/character.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/code/ryzom/server/src/entities_game_service/player_manager/character.cpp b/code/ryzom/server/src/entities_game_service/player_manager/character.cpp index 510ae28da..a70762816 100644 --- a/code/ryzom/server/src/entities_game_service/player_manager/character.cpp +++ b/code/ryzom/server/src/entities_game_service/player_manager/character.cpp @@ -1341,6 +1341,22 @@ uint32 CCharacter::tickUpdate() } } + // Adding UpdateCompass for guild missions + { + H_AUTO(CharacterUpdateGuildCompass); + CGuild * guild = CGuildManager::getInstance()->getGuildFromId( _GuildId ); + if ( guild ) + { + const uint size = (uint)guild->getMissions().size(); + for ( uint i = 0; i < size; i++ ) + { + nlassert(guild->getMissions()[i]); + guild->getMissions()[i]->updateCompass(*this, string("")); + guild->getMissions()[i]->updateCompass(*this, string("GROUP:")); + } + } + } + { H_AUTO(CharacterUpdateTargetCoordinatesCompass); // update compass coordinates information From 74a6cc26e6b690574787caf576c3a06a71541315 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 4 Jul 2011 13:57:11 +0200 Subject: [PATCH 032/215] Changed: #1304: Adding guild mission support in functions acceptExchange and processMissionEventList of the CCharacter class --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.cpp | 24 +++++++++++++++++++ .../guild_manager/guild.h | 3 +++ .../player_manager/character.cpp | 23 +++++++++++++++++- 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp index ccf55d54b..1a5c9fff3 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp @@ -728,6 +728,30 @@ bool CGuild::processMissionEvent( CMissionEvent & event, TAIAlias alias) return true; } +//---------------------------------------------------------------------------- +bool CGuild::processGuildMissionEvent(std::list< CMissionEvent *> & eventList, TAIAlias missionAlias) +{ + return true; +} + +//---------------------------------------------------------------------------- +bool CGuild::processGuildMissionStepEvent(std::list< CMissionEvent*> & eventList, TAIAlias missionAlias, uint32 stepIndex) +{ + return true; +} + +//---------------------------------------------------------------------------- +CMissionGuild* CGuild::getMissionByAlias( TAIAlias missionAlias ) +{ + const uint size = (uint)_Missions.size(); + for ( uint i = 0; i < size; i++ ) + { + if ( _Missions[i] && _Missions[i]->getTemplateId() == missionAlias ) + return _Missions[i]; + } + return NULL; +} + //---------------------------------------------------------------------------- bool CGuild::isMissionSuccessfull(TAIAlias alias) { diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index 342174ebb..b3b8e4e01 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -190,6 +190,9 @@ public: void removeMission( uint idx, TMissionResult result); void addSuccessfulMission(CMissionTemplate * templ); bool processMissionEvent( CMissionEvent & event, TAIAlias alias = CAIAliasTranslator::Invalid); + bool processGuildMissionEvent(std::list< CMissionEvent * > & eventList, TAIAlias missionAlias ); + bool processGuildMissionStepEvent(std::list< CMissionEvent* > & eventList, TAIAlias missionAlias, uint32 stepIndex); + CMissionGuild* getMissionByAlias( TAIAlias missionAlias ); bool isMissionSuccessfull(TAIAlias alias); ///\return the mission inline std::vector & getMissions() diff --git a/code/ryzom/server/src/entities_game_service/player_manager/character.cpp b/code/ryzom/server/src/entities_game_service/player_manager/character.cpp index a70762816..b70eb3b88 100644 --- a/code/ryzom/server/src/entities_game_service/player_manager/character.cpp +++ b/code/ryzom/server/src/entities_game_service/player_manager/character.cpp @@ -10708,6 +10708,7 @@ void CCharacter::acceptExchange(uint8 exchangeId) } CTeam * team = TeamManager.getRealTeam( _TeamId ); + CGuild* guild = CGuildManager::getInstance()->getGuildFromId( _GuildId ); if (_BotGift == NULL) { nlwarning("Player %s has no bot gift", _Id.toString().c_str()); @@ -10731,6 +10732,15 @@ void CCharacter::acceptExchange(uint8 exchangeId) } mission = team->getMissionByAlias( missionAlias ); } + else if (type == MISSION_DESC::Guild) + { + if (guild == NULL) + { + nlwarning("CCharacter::acceptExchange : character %s -> no guild",_Id.toString().c_str() ); + return; + } + mission = guild->getMissionByAlias( missionAlias ); + } vector vect; vector exchangePlayerPets; @@ -10764,6 +10774,8 @@ void CCharacter::acceptExchange(uint8 exchangeId) processMissionStepUserEvent( eventList,missionAlias,stepIndex ); else if ( type == MISSION_DESC::Group ) team->processTeamMissionStepEvent( eventList,missionAlias,stepIndex ); + else if ( type == MISSION_DESC::Guild ) + guild->processGuildMissionStepEvent( eventList,missionAlias,stepIndex ); eventList.pop_front(); for ( std::list< CMissionEvent* >::iterator it = eventList.begin(); it != eventList.end(); ++it ) processMissionEvent(*(*it)); @@ -10783,6 +10795,8 @@ void CCharacter::acceptExchange(uint8 exchangeId) processMissionStepUserEvent( eventList,missionAlias,stepIndex ); else if ( type == MISSION_DESC::Group ) team->processTeamMissionStepEvent( eventList,missionAlias,stepIndex ); + else if ( type == MISSION_DESC::Guild ) + guild->processGuildMissionStepEvent( eventList,missionAlias,stepIndex ); eventList.pop_front(); for ( std::list< CMissionEvent* >::iterator it = eventList.begin(); it != eventList.end(); ++it ) processMissionEvent(*(*it)); @@ -11615,7 +11629,7 @@ bool CCharacter::processMissionEventList( std::list< CMissionEvent* > & eventLis bool processed = false; bool firstEvent = true; - CGuild * guild = NULL; + CGuild * guild = CGuildManager::getInstance()->getGuildFromId( _GuildId ); while ( !eventList.empty() ) { bool eventProcessed = false; @@ -11652,6 +11666,13 @@ bool CCharacter::processMissionEventList( std::list< CMissionEvent* > & eventLis eventProcessed = team->processTeamMissionEvent(eventList, alias); } + // THIRD - Check with guild missions (if event not already processed and char belongs to a guild) + if (!eventProcessed && (event.Restriction != CMissionEvent::NoGroup)) + { + if (guild != NULL) + eventProcessed = guild->processGuildMissionEvent(eventList, alias); + } + processed |= eventProcessed; // the first event of the list was processed, so we remove it. From e92762ad8bb1a2b67899eab687125f0d33aad295 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 4 Jul 2011 15:06:11 +0200 Subject: [PATCH 033/215] Changed: #1304: Adding mission histories for CGuild --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.cpp | 49 ++++++++++++++++--- .../guild_manager/guild.h | 8 +++ 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp index 1a5c9fff3..121aed2f9 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp @@ -704,6 +704,8 @@ void CGuild::removeMission( uint idx, TMissionResult result) CMissionTemplate *tpl = CMissionManager::getInstance()->getTemplate(_Missions[idx]->getTemplateId()); + updateMissionHistories( _Missions[idx]->getTemplateId(), result); + if ( tpl && !tpl->Tags.NoList ) { _Missions[idx]->clearUsersJournalEntry(); @@ -717,21 +719,53 @@ void CGuild::removeMission( uint idx, TMissionResult result) //---------------------------------------------------------------------------- void CGuild::addSuccessfulMission(CMissionTemplate * templ) { - /*TMissionHistory &mh = _MissionHistories[templ.Alias]; - mh.Successfull = true;*/ - /// TODO: Add the mission histories + TMissionHistory &mh = _MissionHistories[templ->Alias]; + mh.Successfull = true; +} + +//---------------------------------------------------------------------------- +void CGuild::clearSuccessfulMissions() +{ + _MissionHistories.clear(); +} + +//---------------------------------------------------------------------------- +void CGuild::updateMissionHistories(TAIAlias missionAlias, uint32 result) +{ + TMissionHistory &mh = _MissionHistories[missionAlias]; + + switch(result) + { + case mr_success: + case mr_forced: + mh.Successfull = true; + // validate last try date + _MissionHistories[missionAlias].LastSuccessDate = CTickEventHandler::getGameCycle(); + break; + } } //---------------------------------------------------------------------------- bool CGuild::processMissionEvent( CMissionEvent & event, TAIAlias alias) { - return true; + std::list listEvents; + listEvents.push_back(&event); + return processGuildMissionEvent(listEvents, alias); } //---------------------------------------------------------------------------- bool CGuild::processGuildMissionEvent(std::list< CMissionEvent *> & eventList, TAIAlias missionAlias) { - return true; + for (uint i = 0; i < _Missions.size(); i++ ) + { + nlassert( _Missions[i] ); + if ( missionAlias == CAIAliasTranslator::Invalid || _Missions[i]->getTemplateId() == missionAlias ) + { + if ( processGuildMissionStepEvent( eventList, _Missions[i]->getTemplateId() ,0xFFFFFFFF) ) + return true; + } + } + return false; } //---------------------------------------------------------------------------- @@ -755,10 +789,9 @@ CMissionGuild* CGuild::getMissionByAlias( TAIAlias missionAlias ) //---------------------------------------------------------------------------- bool CGuild::isMissionSuccessfull(TAIAlias alias) { - /*std::map::iterator it(_MissionHistories.find(alias)); + std::map::iterator it(_MissionHistories.find(alias)); if (it != _MissionHistories.end()) - return it->second.Successfull;*/ - /// TODO: Add the mission histories + return it->second.Successfull; return false; } diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index b3b8e4e01..61dc97dd2 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -32,6 +32,10 @@ class CMissionGuild; class CGuildMember; +/* Storage class for mission history data. +*/ +struct TMissionHistory; + /** * A guild in ryzom @@ -189,6 +193,8 @@ public: } void removeMission( uint idx, TMissionResult result); void addSuccessfulMission(CMissionTemplate * templ); + void clearSuccessfulMissions(); + void updateMissionHistories(TAIAlias missionAlias, uint32 result); bool processMissionEvent( CMissionEvent & event, TAIAlias alias = CAIAliasTranslator::Invalid); bool processGuildMissionEvent(std::list< CMissionEvent * > & eventList, TAIAlias missionAlias ); bool processGuildMissionStepEvent(std::list< CMissionEvent* > & eventList, TAIAlias missionAlias, uint32 stepIndex); @@ -378,6 +384,8 @@ private: ///the missions took by the guild std::vector _Missions; + /// Successful missions + std::map _MissionHistories; NLMISC_COMMAND_FRIEND( guildDB ); }; From e46422c04126d4d5d9a0cdf948ca54cdf955c47b Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 4 Jul 2011 15:57:02 +0200 Subject: [PATCH 034/215] Changed: #1304: Add implementation of functions to process mission events for guild missions --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.cpp | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp index 121aed2f9..158508202 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp @@ -771,6 +771,72 @@ bool CGuild::processGuildMissionEvent(std::list< CMissionEvent *> & eventList, T //---------------------------------------------------------------------------- bool CGuild::processGuildMissionStepEvent(std::list< CMissionEvent*> & eventList, TAIAlias missionAlias, uint32 stepIndex) { + CMissionGuild * mission = getMissionByAlias( missionAlias ); + if (!mission ) + { + nlwarning("invalid missionAlias"); + return false; + } + // I don't know if i should pass _EId to this function + CMissionEvent::TResult result = mission->processEvent( TheDataset.getDataSetRow( _EId) ,eventList,stepIndex ); + if ( result == CMissionEvent::Nothing ) + return false; + else if ( result == CMissionEvent::MissionFailed ) + return true; + + CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( mission->getTemplateId() ); + nlassert( templ ); + if ( result == CMissionEvent::MissionEnds ) + { + CMissionEventMissionDone * event = new CMissionEventMissionDone(templ->Alias); + eventList.push_back(event); + + addSuccessfulMission(templ); + + for ( std::map::iterator it = getMembersBegin(); + it != getMembersEnd();++it ) + { + CCharacter * user = PlayerManager.getChar( it->first ); + if ( user ) + { + if ( templ->Tags.NoList == false ) + CCharacter::sendDynamicSystemMessage( user->getEntityRowId(),"EGS_MISSION_SUCCESS"); + } + } + + CMissionManager::getInstance()->missionDoneOnce(templ); + mission->stopChildren(); + + // only remove no list missions, other must be manually removed by user + if ( templ->Tags.NoList || mission->isChained() || templ->Tags.AutoRemove ) + { + mission->updateEncyclopedia(); + removeMission(mission, mr_success); + } + else + { + mission->setSuccessFlag(); + mission->updateUsersJournalEntry(); + } + return true; + } + else if ( result == CMissionEvent::StepEnds ) + { + if ( templ->Tags.NoList == false ) + { + for ( std::map::iterator it = getMembersBegin(); + it != getMembersEnd();++it ) + { + CCharacter * user = PlayerManager.getChar( it->first ); + if ( user ) + { + if ( templ->Tags.NoList == false ) + CCharacter::sendDynamicSystemMessage( user->getEntityRowId(),"EGS_MISSION_STEP_SUCCESS"); + } + } + } + } + mission->updateUsersJournalEntry(); return true; } From a4f222c6511c8aff6ed0172126e86e9108ef37f8 Mon Sep 17 00:00:00 2001 From: cemycc Date: Tue, 5 Jul 2011 05:01:13 +0300 Subject: [PATCH 035/215] Changed: #1307 Added extraction words options for: item, creature, sbrick, sphrase --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/CMakeLists.txt | 4 +- .../translation_manager/editor_worksheet.cpp | 108 +++- .../translation_manager/editor_worksheet.h | 21 +- .../translation_manager/extract_bot_names.cpp | 467 +-------------- .../translation_manager/extract_bot_names.h | 111 ++++ .../extract_new_sheet_names.cpp | 154 +++++ .../extract_new_sheet_names.h | 70 +++ .../translation_manager_editor.h | 16 + .../translation_manager_main_window.cpp | 190 +++++-- .../translation_manager_main_window.h | 24 +- .../translation_manager_plugin.cpp | 17 + .../translation_manager_plugin.h | 17 + .../translation_manager_settings_page.cpp | 120 +--- .../translation_manager_settings_page.h | 11 +- .../translation_manager_settings_page.ui | 533 ++++++------------ 15 files changed, 835 insertions(+), 1028 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt index edc0a60e5..56195d6e0 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -13,7 +13,9 @@ SET(OVQT_PLUG_TRANSLATION_MANAGER_HDR translation_manager_plugin.h translation_manager_main_window.h translation_manager_settings_page.h translation_manager_editor.h - editor_worksheet.h) + editor_worksheet.h + extract_new_sheet_names.h + extract_bot_names.h) SET(OVQT_PLUG_TRANSLATION_MANAGER_UIS translation_manager_settings_page.ui translation_manager_main_window.ui) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index ee32e3979..d19d28723 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -1,6 +1,6 @@ -// Object Viewer Qt - MMORPG Framework +// Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin +// Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -24,22 +24,10 @@ #include #include +#include "extract_bot_names.h" + using namespace std; -struct TEntryInfo -{ - string SheetName; -}; - -set getGenericNames(); -void cleanGenericNames(); -map getSimpleNames(); -void cleanSimpleNames(); -void setPathsForPrimitives(map > config_paths, string ligo_class_file); -void extractBotNamesFromPrimitives(); -string cleanupName(const std::string &name); -ucstring cleanupUcName(const ucstring &name); - namespace Plugin { @@ -251,12 +239,15 @@ void CEditorWorksheet::worksheetEditorChanged(int row, int column) } -void CEditorWorksheet::extractBotNames() +void CEditorWorksheet::extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig) { bool modified = false; -// get SimpleNames + ExtractBotNames ebn; + ebn.setRequiredSettings(filters, level_design_path); + ebn.extractBotNamesFromPrimitives(ligoConfig); + // get SimpleNames { - map SimpleNames = getSimpleNames(); + map SimpleNames = ebn.getSimpleNames(); map::iterator it(SimpleNames.begin()), last(SimpleNames.end()); for (; it != last; ++it) @@ -281,15 +272,15 @@ void CEditorWorksheet::extractBotNames() if(!modified) modified = true; } } - cleanSimpleNames(); + ebn.cleanSimpleNames(); } // get GenericNames { - set GenericNames = getGenericNames(); + set GenericNames = ebn.getGenericNames(); set::iterator it(GenericNames.begin()), last(GenericNames.end()); for (; it != last; ++it) { - string gnName = "gn_" + cleanupName(*it); + string gnName = "gn_" + ebn.cleanupName(*it); QList search_results = table_editor->findItems(tr((*it).c_str()), Qt::MatchExactly); if(search_results.size() == 0) { @@ -310,7 +301,7 @@ void CEditorWorksheet::extractBotNames() if(!modified) modified = true; } } - cleanGenericNames(); + ebn.cleanGenericNames(); } if(modified) { @@ -319,6 +310,77 @@ void CEditorWorksheet::extractBotNames() } +void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordListBuilder& wordListBuilder) +{ + uint i; + + // **** Load the excel sheet + // load + TWorksheet workSheet; + if(!loadExcelSheet(filename.toStdString(), workSheet, true)) + { + nlwarning("Error reading '%s'. Aborted", filename.toStdString().c_str()); + return; + } + // get the key column index + uint keyColIndex = 0; + if(!workSheet.findCol(columnId.toStdString(), keyColIndex)) + { + nlwarning("Error: Don't find the column '%s'. '%s' Aborted", columnId.toStdString().c_str(), filename.toStdString().c_str()); + return; + } + // get the name column index + uint nameColIndex; + if(!workSheet.findCol(ucstring("name"), nameColIndex)) + { + nlwarning("Error: Don't find the column 'name'. '%s' Aborted", filename.toStdString().c_str()); + return; + } + + // **** List all words with the builder given + std::vector allWords; + if(!wordListBuilder.buildWordList(allWords, filename.toStdString())) + { + return; + } + bool modified = false; + for(i = 0; i < allWords.size(); i++) + { + string keyName = allWords[i]; + QList search_results = table_editor->findItems(tr(keyName.c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + + int knPos = 0, nPos = 0; + if(workSheet.getData(0, 0) == ucstring("*HASH_VALUE")) + { + knPos = keyColIndex - 1; + nPos = nameColIndex - 1; + } else { + knPos = keyColIndex; + nPos = nameColIndex; + } + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + // keyName row + QTableWidgetItem *key_name_row = new QTableWidgetItem(); + key_name_row->setText(tr(keyName.c_str())); + key_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, knPos, key_name_row); + // nameColumn key + QTableWidgetItem *name_row = new QTableWidgetItem(); + name_row->setText(QString("") + tr(keyName.c_str())); + name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, nPos, name_row); + if(!modified) modified = true; + } + } + if(modified) + { + setWindowModified(true); + } +} + void CEditorWorksheet::setCurrentFile(QString filename) { QFileInfo *file = new QFileInfo(filename); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 71e86af84..497e23913 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -1,3 +1,19 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . #ifndef EDITOR_WORKSHEET_H #define EDITOR_WORKSHEET_H @@ -7,6 +23,7 @@ #include "nel/misc/sheet_id.h" #include "nel/misc/path.h" #include "nel/misc/diff_tool.h" +#include "nel/ligo/ligo_config.h" // Qt includes #include @@ -16,6 +33,7 @@ #include #include "translation_manager_editor.h" +#include "extract_new_sheet_names.h" namespace Plugin { @@ -31,7 +49,8 @@ public: void save(); void saveAs(QString filename); void activateWindow(); - void extractBotNames(); + void extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig); + void extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder); bool isBotNamesTable(); void closeEvent(QCloseEvent *event); private Q_SLOTS: diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp index 66d7d1c69..4de3d889d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp @@ -1,5 +1,6 @@ -// Ryzom - MMORPG Framework +// Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -14,67 +15,18 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -#include "nel/misc/types_nl.h" -#include "nel/misc/config_file.h" -#include "nel/misc/sheet_id.h" -#include "nel/misc/path.h" -#include "nel/misc/diff_tool.h" -#include "nel/georges/u_form.h" -#include "nel/georges/u_form_elm.h" -#include "nel/georges/load_form.h" -#include "nel/ligo/ligo_config.h" -#include "nel/ligo/primitive.h" -#include "nel/ligo/primitive_utils.h" +#include "extract_bot_names.h" -using namespace std; -using namespace NLMISC; -using namespace NLLIGO; -using namespace STRING_MANAGER; -vector Filters; - -static CLigoConfig LigoConfig; static bool RemoveOlds = false; -struct TCreatureInfo + + +namespace Plugin { - CSheetId SheetId; - bool ForceSheetName; - bool DisplayName; - - void readGeorges (const NLMISC::CSmartPtr &form, const NLMISC::CSheetId &sheetId) - { - const NLGEORGES::UFormElm &item=form->getRootNode(); - - SheetId=sheetId; - item.getValueByName(ForceSheetName, "3d data.ForceDisplayCreatureName"); - item.getValueByName(DisplayName, "3d data.DisplayName"); - } - - void serial(NLMISC::IStream &f) - { - f.serial(SheetId); - f.serial(ForceSheetName); - f.serial(DisplayName); - } - - - static uint getVersion () - { - return 1; - } - - void removed() - { - } - -}; - -std::map Creatures; - -TCreatureInfo *getCreature(const std::string &sheetName) +TCreatureInfo *ExtractBotNames::getCreature(const std::string &sheetName) { CSheetId id(sheetName+".creature"); @@ -84,7 +36,7 @@ TCreatureInfo *getCreature(const std::string &sheetName) return NULL; } -string cleanupName(const std::string &name) +string ExtractBotNames::cleanupName(const std::string &name) { string ret; @@ -99,7 +51,7 @@ string cleanupName(const std::string &name) return ret; } -ucstring cleanupUcName(const ucstring &name) +ucstring ExtractBotNames::cleanupUcName(const ucstring &name) { ucstring ret; @@ -118,7 +70,7 @@ ucstring cleanupUcName(const ucstring &name) /* Removes first and last '$' */ -ucstring makeGroupName(const ucstring & translationName) +ucstring ExtractBotNames::makeGroupName(const ucstring & translationName) { ucstring ret = translationName; if (ret.size() >= 2) @@ -136,36 +88,31 @@ ucstring makeGroupName(const ucstring & translationName) return ret; } -struct TEntryInfo -{ - string SheetName; -}; -set GenericNames; -map SimpleNames; -set Functions; -set getGenericNames() + + +set ExtractBotNames::getGenericNames() { return GenericNames; } -map getSimpleNames() +map ExtractBotNames::getSimpleNames() { return SimpleNames; } -void cleanSimpleNames() +void ExtractBotNames::cleanSimpleNames() { SimpleNames.clear(); } -void cleanGenericNames() +void ExtractBotNames::cleanGenericNames() { GenericNames.clear(); } -string removeAndStoreFunction(const std::string &fullName) +string ExtractBotNames::removeAndStoreFunction(const std::string &fullName) { string::size_type pos = fullName.find("$"); if (pos == string::npos) @@ -193,7 +140,7 @@ string removeAndStoreFunction(const std::string &fullName) } -void addGenericName(const std::string &name, const std::string &sheetName) +void ExtractBotNames::addGenericName(const std::string &name, const std::string &sheetName) { TCreatureInfo *c = getCreature(sheetName); if (!c || c->ForceSheetName || !c->DisplayName) @@ -213,7 +160,7 @@ void addGenericName(const std::string &name, const std::string &sheetName) } } -void addSimpleName(const std::string &name, const std::string &sheetName) +void ExtractBotNames::addSimpleName(const std::string &name, const std::string &sheetName) { TCreatureInfo *c = getCreature(sheetName); if (!c || c->ForceSheetName || !c->DisplayName) @@ -238,18 +185,9 @@ void addSimpleName(const std::string &name, const std::string &sheetName) } } -void setPathsForPrimitives(map > config_paths, string ligo_class_file) +void ExtractBotNames::setRequiredSettings(list filters, string level_design_path) { - for (std::list::iterator it = config_paths["paths"].begin(); it != config_paths["paths"].end(); ++it) - { - CPath::addSearchPath(*it, true, false); - } - for (std::list::iterator it = config_paths["pathsR"].begin(); it != config_paths["pathsR"].end(); ++it) - { - CPath::addSearchPath(*it, false, false); - } - - for (std::list::iterator it = config_paths["filters"].begin(); it != config_paths["filters"].end(); ++it) + for (std::list::iterator it = filters.begin(); it != filters.end(); ++it) { Filters.push_back(*it); } @@ -262,35 +200,28 @@ void setPathsForPrimitives(map > config_paths, string ligo_c if (Creatures.empty()) { - for (std::list::iterator it = config_paths["georges"].begin(); it != config_paths["georges"].end(); ++it) - CPath::addSearchPath((*it).c_str(), true, false); - - loadForm("creature", PACKED_SHEETS_NAME, Creatures, true); + loadForm("creature", PACKED_SHEETS_NAME, Creatures, true); } - - - //------------------------------------------------------------------- - // init ligo config - string ligoPath = CPath::lookup(ligo_class_file, true, true); - LigoConfig.readPrimitiveClass(ligoPath.c_str(), false); - NLLIGO::Register(); - - CPrimitiveContext::instance().CurrentLigoConfig = &LigoConfig; + } -void extractBotNamesFromPrimitives() +void ExtractBotNames::extractBotNamesFromPrimitives(CLigoConfig ligoConfig) { + //------------------------------------------------------------------- // ok, ready for the real work, // first, read the primitives files and parse the primitives vector files; CPath::getFileList("primitive", files); + for (uint i=0; i > config_paths, string ligo_class_file, string trans_path, string work_path) -{ - - /* - //------------------------------------------------------------------- - // step 2 : load the reference file - - nlinfo("Looking for missing translation:"); - - string work_path_file = work_path + "/bot_names.txt"; - string trans_path_file = trans_path + "/bot_names.txt"; - string title_path_file = work_path + "/title_words_wk.txt"; - - TWorksheet botNames; - if (!CFile::fileExists(work_path_file) || !loadExcelSheet(work_path_file, botNames)) - { - botNames.resize(botNames.size() + 1); - botNames.insertColumn(botNames.ColCount); - botNames.setData(0,botNames.ColCount - 1,ucstring("bot name")); - botNames.insertColumn(botNames.ColCount); - botNames.setData(0,botNames.ColCount - 1,ucstring("translated name")); - botNames.insertColumn(botNames.ColCount); - botNames.setData(0,botNames.ColCount - 1,ucstring("sheet_name")); - } - - TWorksheet transBotNames; - if (!CFile::fileExists(trans_path_file) || !loadExcelSheet(trans_path_file, transBotNames)) - { - transBotNames.resize(transBotNames.size() + 1); - transBotNames.insertColumn(transBotNames.ColCount); - transBotNames.setData(0,transBotNames.ColCount - 1,ucstring("*HASH_VALUE")); - transBotNames.insertColumn(transBotNames.ColCount); - transBotNames.setData(0,transBotNames.ColCount - 1,ucstring("bot name")); - transBotNames.insertColumn(transBotNames.ColCount); - transBotNames.setData(0,transBotNames.ColCount - 1,ucstring("translated name")); - transBotNames.insertColumn(transBotNames.ColCount); - transBotNames.setData(0,transBotNames.ColCount - 1,ucstring("sheet_name")); - } - - TWorksheet fcts; - if (!CFile::fileExists(title_path_file) || !loadExcelSheet(title_path_file, fcts)) - { - fcts.resize(fcts.size() + 1); - fcts.insertColumn(fcts.ColCount); - fcts.setData(0,fcts.ColCount - 1,ucstring("title_id")); - fcts.insertColumn(fcts.ColCount); - fcts.setData(0,fcts.ColCount - 1,ucstring("name")); - fcts.insertColumn(fcts.ColCount); - fcts.setData(0,fcts.ColCount - 1,ucstring("women_name")); - } - - loadExcelSheet(work_path_file, botNames, true); - loadExcelSheet(trans_path_file, transBotNames, true); - loadExcelSheet(title_path_file, fcts, true); - - // add missing element - - uint nbAddSimpleName = 0; - uint nbAddFunction = 0; - uint nbAddGenericName = 0; - - uint botIdCol; - nlverify(botNames.findId(botIdCol)); - uint transIdCol; - nlverify(transBotNames.findId(transIdCol)); - uint fctsIdCol; - nlverify(fcts.findId(fctsIdCol)); - - // special treatment to add the sheet_name col - { - uint sheetCol; - if (!botNames.findCol(ucstring("sheet_name"), sheetCol)) - { - botNames.insertColumn(botNames.ColCount); - botNames.setData(0, botNames.ColCount-1, ucstring("sheet_name")); - } - - if (!transBotNames.findCol(ucstring("sheet_name"), sheetCol)) - { - transBotNames.insertColumn(transBotNames.ColCount); - transBotNames.setData(0, transBotNames.ColCount-1, ucstring("sheet_name")); - } - } - // 1 - simple names - { - nlinfo(" Simple names..."); - - - map::iterator first(SimpleNames.begin()), last(SimpleNames.end()); - for (; first != last; ++first) - { - uint rowIdx; - if (!botNames.findRow(botIdCol, first->first, rowIdx)) - { - // we need to add the entry - rowIdx = botNames.size(); - botNames.resize(botNames.size()+1); - - botNames.setData(rowIdx, ucstring("bot name"), first->first); - botNames.setData(rowIdx, ucstring("translated name"), first->first); - botNames.setData(rowIdx, ucstring("sheet_name"), first->second.SheetName); - - nbAddSimpleName++; - } - else - { - // set/update the sheet name info - // try to restore the existing translation - uint transRowIdx; - if (transBotNames.findRow(transIdCol, first->first, transRowIdx)) - { - ucstring wkBotName = botNames.getData(rowIdx, ucstring("bot name")); - ucstring wkSheetName = botNames.getData(rowIdx, ucstring("sheet_name")); - ucstring wkTranslationName = botNames.getData(rowIdx, ucstring("translated name")); - ucstring ucWkHash; - uint64 hash = CI18N::makeHash(wkBotName + wkTranslationName +wkSheetName); - CI18N::hashToUCString(hash, ucWkHash); - ucstring trUcHash = transBotNames[transRowIdx][0]; - bool isWkTranslationNameAGroupName = wkTranslationName.find(ucstring("$")) != ucstring::npos; - bool hashIsValide = std::equal(ucWkHash.begin(), ucWkHash.end(), trUcHash.begin()+1); - // Hash is equal get the translation - if (hashIsValide && !isWkTranslationNameAGroupName) - { - wkTranslationName = transBotNames.getData(transRowIdx, ucstring("translated name")); - wkSheetName = transBotNames.getData(transRowIdx, ucstring("sheet_name")); - botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); - botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); - hash = CI18N::makeHash(wkBotName + wkTranslationName + wkSheetName); - // update the hash code - CI18N::hashToUCString(hash, transBotNames[transRowIdx][0]); - } - // bots_name.txt has been manually changed. We trust what the Level Designer has done. We don't destroy is work. - // or it is a simple - else - { - //use the "translated name" of the manually changed work/bot_name.txt - botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); - botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); - } - } - } - } - } - - // 2 - generic names - - { - nlinfo(" Generic names..."); - - set::iterator first(GenericNames.begin()), last(GenericNames.end()); - for (; first != last; ++first) - { - string gnName = "gn_" + cleanupName(*first); - - ucstring fctsTitleId; - ucstring fctsName; - // add or modify the bot names - uint rowIdx; - if (!botNames.findRow(botIdCol, *first, rowIdx)) - { - // we need to add the entry - rowIdx = botNames.size(); - botNames.resize(botNames.size()+1); - - botNames.setData(rowIdx, ucstring("bot name"), *first); - botNames.setData(rowIdx, ucstring("translated name"), ucstring("$") + gnName + "$"); - botNames.setData(rowIdx, ucstring("sheet_name"), ucstring()); - fctsTitleId = gnName; - fctsName = *first; - - nbAddSimpleName++; - } - else - { - // look in the translated table to remember the translated name to write it in the string file - ucstring wkBotName = botNames.getData(rowIdx, ucstring("bot name")); - ucstring wkTranslationName = botNames.getData(rowIdx, ucstring("translated name")); - ucstring wkSheetName = botNames.getData(rowIdx, ucstring("sheet_name")); - - - nlinfo("Bot name:%s\n",wkBotName.toString().c_str()); - bool isWkTranslationNameAGroupName = wkTranslationName.find(ucstring("$")) != ucstring::npos; - - if ( isWkTranslationNameAGroupName ) //work name looks like "$gn_***$: do not modify - { - - //Do not change work/bot_name.txt - // update work/world_title.txt - - ucstring transName; - fctsTitleId = makeGroupName(wkTranslationName); - uint transRowIdx; - if (transBotNames.findRow(transIdCol, *first, transRowIdx)) - { - transName = transBotNames.getData(transRowIdx, ucstring("translated name")); - - if (transName.find(ucstring("$")) != ucstring::npos) - { - transName = fctsTitleId; - } - } - else - { - transName = fctsTitleId; - } - //Do not touch anything - botNames.setData(rowIdx, ucstring("translated name"), wkTranslationName); - botNames.setData(rowIdx, ucstring("sheet_name"), wkSheetName); - // fctsTitleId = makeGroupName(wkTranslationName); - fctsName = transName; - - } - else // WkTranslationName != "$gn*$" - { - uint transRowIdx; - ucstring transName; - ucstring wkSheetName; - // Get the translation as a simple name. - if (transBotNames.findRow(transIdCol, *first, transRowIdx)) - { - - transName = transBotNames.getData(transRowIdx, ucstring("translated name")); - ucstring trSheetName = transBotNames.getData(transRowIdx, ucstring("sheet_name")); - - //tr."translation name" is - if (transName.find(ucstring("$")) != ucstring::npos) - { - //get Translation, update hash - botNames[rowIdx][1] = transName; - botNames[rowIdx][2] = trSheetName; - fctsTitleId = makeGroupName(transName); - fctsName = makeGroupName(transName); - ucstring trNewUcHash; - uint64 hash = CI18N::makeHash(wkBotName + transName +trSheetName); - CI18N::hashToUCString(hash, trNewUcHash); - transBotNames[transRowIdx][0] = ucstring("_") + trNewUcHash; - } - else //botNames."translated name" != $gn_$ && tansName."translated name" != $gn_$ - { - - // get the translation back - //update work/bot_name.txt - wkTranslationName = ucstring("$")+gnName+"$"; - botNames[rowIdx][0] = wkBotName; - botNames[rowIdx][1] = wkTranslationName; - botNames[rowIdx][2] = wkSheetName; - - //update translated/bot_name.txt - - fctsName = transName; //transName - fctsTitleId = gnName; - ucstring trNewUcHash; - uint64 hash = CI18N::makeHash(botNames[rowIdx][0] + botNames[rowIdx][1] +botNames[rowIdx][2]); - CI18N::hashToUCString(hash, trNewUcHash); - transBotNames[transRowIdx][0] = ucstring("_") + trNewUcHash; - } - - } - else //There is no translation yet - { - fctsName = wkTranslationName; - wkTranslationName = ucstring("$")+gnName+"$"; - botNames[rowIdx][0] = wkBotName; - botNames[rowIdx][1] = wkTranslationName; - botNames[rowIdx][2] = wkSheetName; - fctsTitleId = gnName; - - - } - } - - } - - - // look for a corresponding entry - uint gnNameRow; - - - if (!fcts.findRow(fctsIdCol, fctsTitleId, gnNameRow)) - { - - // not found, add it - gnNameRow = fcts.size(); - fcts.resize(fcts.size()+1); - fcts.setData(gnNameRow, ucstring("title_id"), fctsTitleId); - fcts.setData(gnNameRow, ucstring("name"), fctsName); - nbAddGenericName++; - - } - else //Update - { - - } - } - } - - - // 3 - functions - { - nlinfo(" Functions..."); - - set::iterator first(Functions.begin()), last(Functions.end()); - for (; first != last; ++first) - { - string fctName = *first; - // look for a corresponding entry - uint functionRow; - if (!fcts.findRow(fctsIdCol, fctName, functionRow)) - { - // not found, add it - functionRow = fcts.size(); - fcts.resize(fcts.size()+1); - - fcts.setData(functionRow, ucstring("title_id"), fctName); - fcts.setData(functionRow, ucstring("name"), *first); - - nbAddFunction++; - } - } - } - - // display resum\E9 - nlinfo("Adding %u new simple name", nbAddSimpleName); - nlinfo("Adding %u new generic name", nbAddGenericName); - nlinfo("Adding %u new function name", nbAddFunction); - - // saving the modified files - ucstring s = prepareExcelSheet(botNames); - CI18N::writeTextFile(work_path_file, s, false); - s = prepareExcelSheet(transBotNames); - CI18N::writeTextFile(trans_path_file, s, false); - s = prepareExcelSheet(fcts); - CI18N::writeTextFile(title_path_file, s, false); -*/ - return 0; -} - + +} \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h new file mode 100644 index 000000000..9c4ea51d2 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h @@ -0,0 +1,111 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef EXTRACT_BOT_NAMES_H +#define EXTRACT_BOT_NAMES_H + +#include "nel/misc/types_nl.h" +#include "nel/misc/config_file.h" +#include "nel/misc/sheet_id.h" +#include "nel/misc/path.h" +#include "nel/misc/diff_tool.h" +#include "nel/georges/u_form.h" +#include "nel/georges/u_form_elm.h" +#include "nel/georges/load_form.h" +#include "nel/ligo/ligo_config.h" +#include "nel/ligo/primitive.h" +#include "nel/ligo/primitive_utils.h" + +using namespace std; +using namespace NLMISC; +using namespace NLLIGO; +using namespace STRING_MANAGER; + +namespace Plugin +{ + +struct TCreatureInfo +{ + CSheetId SheetId; + bool ForceSheetName; + bool DisplayName; + + + void readGeorges (const NLMISC::CSmartPtr &form, const NLMISC::CSheetId &sheetId) + { + const NLGEORGES::UFormElm &item=form->getRootNode(); + + SheetId=sheetId; + item.getValueByName(ForceSheetName, "3d data.ForceDisplayCreatureName"); + item.getValueByName(DisplayName, "3d data.DisplayName"); + } + + void serial(NLMISC::IStream &f) + { + f.serial(SheetId); + f.serial(ForceSheetName); + f.serial(DisplayName); + } + + + static uint getVersion () + { + return 1; + } + + void removed() + { + } + +}; + +struct TEntryInfo +{ + string SheetName; +}; + +struct ExtractBotNames +{ +private: + vector Filters; + std::map Creatures; + set GenericNames; + map SimpleNames; + set Functions; +private: + TCreatureInfo *getCreature(const std::string &sheetName); + ucstring makeGroupName(const ucstring & translationName); + string removeAndStoreFunction(const std::string &fullName); + void addGenericName(const std::string &name, const std::string &sheetName); + void addSimpleName(const std::string &name, const std::string &sheetName); +public: + void extractBotNamesFromPrimitives(CLigoConfig ligoConfig); + void setRequiredSettings(list filters, string level_design_path); + set getGenericNames(); + map getSimpleNames(); + string cleanupName(const std::string &name); + ucstring cleanupUcName(const ucstring &name); + void cleanSimpleNames(); + void cleanGenericNames(); + +}; + +} + + +#endif /* EXTRACT_BOT_NAMES_H */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp new file mode 100644 index 000000000..e881177bb --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp @@ -0,0 +1,154 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "extract_new_sheet_names.h" + +using namespace std; +using namespace NLMISC; +using namespace NLLIGO; +using namespace STRING_MANAGER; + +namespace Plugin { + + + +// *************************************************************************** +/* + * Specialisation of IWordListBuilder to list sheets in a directory + */ + + +bool CSheetWordListBuilder::buildWordList(std::vector &allWords, string workSheetFileName) + { + SheetExt= toLower(SheetExt); + nlinfo("aaaa"); + // verify the directory is correct + if(!CFile::isDirectory(SheetPath)) + { + nlwarning("Error: Directory '%s' not found. '%s' Aborted", SheetPath.c_str(), workSheetFileName.c_str()); + return false; + } + + // list all files. + std::vector allFiles; + allFiles.reserve(100000); + CPath::getPathContent(SheetPath, true, false, true, allFiles, NULL); + + // Keep only the extension we want, and remove "_" (parent) + allWords.clear(); + allWords.reserve(allFiles.size()); + for(uint i=0;i &allWords, string workSheetFileName) + { + // verify the directory is correct + if(!CFile::isDirectory(PrimPath)) + { + nlwarning("Error: Directory '%s' not found. '%s' Aborted", PrimPath.c_str(), workSheetFileName.c_str()); + return false; + } + + // list all files. + std::vector allFiles; + allFiles.reserve(100000); + CPath::getPathContent(PrimPath, true, false, true, allFiles, NULL); + + // parse all primitive that match the filter + allWords.clear(); + allWords.reserve(100000); + // to avoid duplicate + set allWordSet; + for(uint i=0;i setPlace; + TPrimitiveSet placeRes; + setPlace.buildSet(PrimDoc.RootNode, predCont, placeRes); + // for all found + for (uint placeId= 0; placeId < placeRes.size(); ++placeId) + { + string primName; + if(placeRes[placeId]->getPropertyByName(listProp[cid], primName) && !primName.empty()) + { + primName= toLower(primName); + // avoid duplicate + if(allWordSet.insert(primName).second) + { + allWords.push_back(primName); + } + } + } + } + } + + return true; + } + +} \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h new file mode 100644 index 000000000..007018a08 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h @@ -0,0 +1,70 @@ +// Ryzom - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef EXTRACT_NEW_SHEET_NAMES_H +#define EXTRACT_NEW_SHEET_NAMES_H + +#include "nel/misc/types_nl.h" +#include "nel/misc/config_file.h" +#include "nel/misc/sheet_id.h" +#include "nel/misc/path.h" +#include "nel/misc/diff_tool.h" +#include "nel/misc/algo.h" +#include "nel/georges/u_form.h" +#include "nel/georges/u_form_elm.h" +#include "nel/georges/load_form.h" +#include "nel/ligo/ligo_config.h" +#include "nel/ligo/primitive.h" +#include "nel/ligo/primitive_utils.h" + +using namespace std; +using namespace NLMISC; +using namespace NLLIGO; +using namespace STRING_MANAGER; + +namespace Plugin { + + +// *************************************************************************** +/* + * Interface to build the whole list of words (key id) for a specific worksheet + */ +struct IWordListBuilder +{ + virtual bool buildWordList(std::vector &allWords, string workSheetFileName) =0; + +}; + +struct CSheetWordListBuilder : public IWordListBuilder +{ + string SheetExt; + string SheetPath; + + virtual bool buildWordList(std::vector &allWords, string workSheetFileName); +}; + +struct CRegionPrimWordListBuilder : public IWordListBuilder +{ + string PrimPath; + vector PrimFilter; + virtual bool buildWordList(std::vector &allWords, string workSheetFileName); +}; + +} + + +#endif /* EXTRACT_NEW_SHEET_NAMES_H */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h index 605d11d6e..7af2b061d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h @@ -1,3 +1,19 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . #ifndef TRANSLATION_MANAGER_EDITOR_H #define TRANSLATION_MANAGER_EDITOR_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 8b8c50a22..95a5fe95f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -1,6 +1,6 @@ -// Object Viewer Qt - MMORPG Framework +// Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin +// Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -46,21 +46,6 @@ #include - -struct TEntryInfo -{ - string SheetName; -}; - -set getGenericNames(); -void cleanGenericNames(); -map getSimpleNames(); -void cleanSimpleNames(); -void setPathsForPrimitives(map > config_paths, string ligo_class_file); -void extractBotNamesFromPrimitives(); -string cleanupName(const std::string &name); -ucstring cleanupUcName(const ucstring &name); - namespace Plugin { @@ -74,9 +59,8 @@ CMainWindow::CMainWindow(QWidget *parent) windowMapper = new QSignalMapper(this); connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); - // set extraction scripts counters - execution_count["extract_bot_names"] = 0; - + initialize_settings["georges"] = false; + initialize_settings["ligo"] = false; readSettings(); createToolbar(); m_undoStack = new QUndoStack(this); @@ -99,9 +83,33 @@ void CMainWindow::createToolbar() QMenu *wordsExtractionMenu = new QMenu("&Words extraction..."); wordsExtractionMenu->setIcon(QIcon(Core::Constants::ICON_SETTINGS)); _ui.toolBar->addAction(wordsExtractionMenu->menuAction()); + // extract bot names QAction *extractBotNamesAct = wordsExtractionMenu->addAction("&Extract bot names..."); extractBotNamesAct->setStatusTip(tr("Extract bot names from primitives.")); connect(extractBotNamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); + // signal mapper for extraction words + QSignalMapper *wordsExtractionMapper = new QSignalMapper(this); + connect(wordsExtractionMapper, SIGNAL(mapped(QString)), this, SLOT(extractWords(QString))); + // extract item words + QAction *extractItemWordsAct = wordsExtractionMenu->addAction("&Extract item words..."); + extractItemWordsAct->setStatusTip(tr("Extract item words")); + connect(extractItemWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractItemWordsAct, "item"); + // extract creature words + QAction *extractCreatureWordsAct = wordsExtractionMenu->addAction("&Extract creature words..."); + extractCreatureWordsAct->setStatusTip(tr("Extract creature words")); + connect(extractCreatureWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractCreatureWordsAct, "creature"); + // extract sbrick words + QAction *extractSbrickWordsAct = wordsExtractionMenu->addAction("&Extract sbrick words..."); + extractSbrickWordsAct->setStatusTip(tr("Extract sbrick words")); + connect(extractSbrickWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractSbrickWordsAct, "sbrick"); + // extract sphrase words + QAction *extractSphraseWordsAct = wordsExtractionMenu->addAction("&Extract sphrase words..."); + extractSphraseWordsAct->setStatusTip(tr("Extract sphrase words")); + connect(extractSphraseWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractSphraseWordsAct, "sphrase"); // Windows menu windowMenu = new QMenu(tr("&Windows..."), _ui.toolBar); @@ -194,6 +202,29 @@ void CMainWindow::open() } +void CMainWindow::openWorkFile(QString file) +{ + QFileInfo* file_path = new QFileInfo(QString("%1/%2").arg(QString(work_path.c_str())).arg(file)); + if(file_path->exists()) + { + if(isWorksheetEditor(file_path->filePath())) + { + CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); + new_window->open(file_path->filePath()); + new_window->activateWindow(); + } + } else { + QErrorMessage error; + QString text; + text.append("The "); + text.append(file_path->fileName()); + text.append(" file don't exists."); + error.showMessage(text); + error.exec(); + } + +} + void CMainWindow::save() { CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); @@ -223,49 +254,101 @@ void CMainWindow::saveAs() } } +void CMainWindow::initializeSettings(bool georges = false) +{ + if(georges == true && initialize_settings["georges"] == false) + { + CPath::addSearchPath(level_design_path + "/DFN", true, false); + CPath::addSearchPath(level_design_path + "/Game_elem/Creature", true, false); + initialize_settings["georges"] = true; + } + + if(initialize_settings["ligo"] == false) + { + //------------------------------------------------------------------- + // init ligo config + string ligoPath = CPath::lookup("world_editor_classes.xml", true, true); + ligoConfig.readPrimitiveClass(ligoPath.c_str(), false); + NLLIGO::Register(); + NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &ligoConfig; + initialize_settings["ligo"] = true; + } +} + +void CMainWindow::extractWords(QString type) +{ + CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + CEditorWorksheet* current_window = qobject_cast(editor_window); + + // initializeSettings(false); + + CSheetWordListBuilder builder; + QString column_name; + + if(type == "item") + { + column_name = "item ID"; + builder.SheetExt = "sitem"; + builder.SheetPath = level_design_path + "/game_element/sitem"; + } else if(type == "creature") { + column_name = "creature ID"; + builder.SheetExt = "creature"; + builder.SheetPath = level_design_path + "/Game_elem/Creature/fauna"; + } else if(type == "sbrick") { + column_name = "sbrick ID"; + builder.SheetExt = "sbrick"; + builder.SheetPath = level_design_path + "/game_element/sbrick"; + } else if(type == "sphrase") { + column_name = "sphrase ID"; + builder.SheetExt = "sphrase"; + builder.SheetPath = level_design_path + "/game_element/sphrase"; + } + current_window->extractWords(current_window->windowFilePath(), column_name, builder); +} + void CMainWindow::extractBotNames() { if(verifySettings() == true) { + CEditorWorksheet* current_window; + if(_ui.mdiArea->subWindowList().size() > 0) + { CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); if(QString(editor_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor { - CEditorWorksheet* current_window = qobject_cast(editor_window); + current_window = qobject_cast(editor_window); QString file_path = current_window->subWindowFilePath(); if(!current_window->isBotNamesTable()) { list subWindows = convertSubWindowList(_ui.mdiArea->subWindowList()); list::iterator it = subWindows.begin(); bool finded = false; - for(; it != subWindows.end(), finded != true; ++it) + + for(; it != subWindows.end(); ++it) { - current_window = qobject_cast((*it)); - file_path = current_window->subWindowFilePath(); - if(current_window->isBotNamesTable()) - { - finded = true; - current_window->activateWindow(); - } + current_window = qobject_cast((*it)); + file_path = current_window->subWindowFilePath(); + if(current_window->isBotNamesTable()) + { + finded = true; + current_window->activateWindow(); + } } if(!finded) { - open(); + openWorkFile("bot_names_wk.txt"); current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); file_path = current_window->windowFilePath(); } - } - if(execution_count["extract_bot_names"] == 0) - setPathsForPrimitives(config_paths, ligo_path); - extractBotNamesFromPrimitives(); - execution_count["extract_bot_names"] = execution_count["extract_bot_names"] + 1; - - current_window->extractBotNames(); - // if(current_window->isWindowModified()) - // { - - // } - + } } + } else { + openWorkFile("bot_names_wk.txt"); + current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + QString file_path = current_window->windowFilePath(); + } + initializeSettings(true); + current_window->extractBotNames(filters, level_design_path, ligoConfig); } } @@ -273,22 +356,14 @@ void CMainWindow::readSettings() { QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("translationmanager"); - - list paths = convertQStringList(settings->value("paths").toStringList()); /* paths */ - config_paths["paths"] = paths; - list pathsR = convertQStringList(settings->value("pathsR").toStringList()); /* pathsR */ - config_paths["pathsR"] = pathsR; - list georges = convertQStringList(settings->value("georges").toStringList()); /* georges */ - config_paths["georges"] = georges; - list filters = convertQStringList(settings->value("filters").toStringList()); /* filters */ - config_paths["filters"] = filters; - + filters = convertQStringList(settings->value("filters").toStringList()); /* filters */ languages = convertQStringList(settings->value("trlanguages").toStringList()); /* languages */ - ligo_path = settings->value("ligo").toString().toStdString(); translation_path = settings->value("translation").toString().toStdString(); work_path = settings->value("work").toString().toStdString(); - - settings->endGroup(); + settings->endGroup(); + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + level_design_path = settings->value(Core::Constants::LEVELDESIGN_PATH).toString().toStdString(); + settings->endGroup(); } void CMainWindow::debug(QString text) @@ -305,10 +380,7 @@ bool CMainWindow::verifySettings() QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("translationmanager"); - if(settings->value("paths").toList().count() == 0 - || settings->value("pathsR").toList().count() == 0 - || settings->value("georges").toList().count() == 0 - || settings->value("filters").toList().count() == 0) + if(settings->value("filters").toList().count() == 0) { QErrorMessage error_settings; error_settings.showMessage("Please write all the paths on the settings dialog."); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 13a3ead97..b0d59fe29 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -1,6 +1,6 @@ -// Object Viewer Qt - MMORPG Framework +// Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin +// Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -27,6 +27,7 @@ #include "nel/misc/sheet_id.h" #include "nel/misc/path.h" #include "nel/misc/diff_tool.h" +#include "nel/ligo/ligo_config.h" // Qt includes #include @@ -51,13 +52,6 @@ using namespace std; namespace Plugin { -class CMdiSubWindow; - -struct WStatus -{ - bool modified; -}; - class CMainWindow : public QMainWindow { Q_OBJECT @@ -75,15 +69,16 @@ private: QMenu *windowMenu; QSignalMapper *windowMapper; // config - map > config_paths; + map initialize_settings; + list filters; list languages; - string ligo_path; + string level_design_path; string translation_path; string work_path; - // counts - map execution_count; + NLLIGO::CLigoConfig ligoConfig; private Q_SLOTS: void extractBotNames(); + void extractWords(QString); void open(); void save(); void saveAs(); @@ -93,12 +88,13 @@ private Q_SLOTS: void debug(QString text); // TODO private: + void openWorkFile(QString file); void updateToolbar(QMdiSubWindow *window); bool verifySettings(); void readSettings(); void createMenus(); void createToolbar(); - + void initializeSettings(bool georges); list convertQStringList(QStringList listq); list convertSubWindowList(QList listq); bool isWorksheetEditor(QString filename); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index 890c589e9..caf677a0d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -1,3 +1,20 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + // Project includes #include "translation_manager_plugin.h" #include "translation_manager_settings_page.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h index 42515cb2f..ed42b4882 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -1,3 +1,20 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + #ifndef TRANSLATION_MANAGER_PLUGIN_H #define TRANSLATION_MANAGER_PLUGIN_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp index 078a9c18c..4870da41b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp @@ -1,6 +1,6 @@ -// Object Viewer Qt - MMORPG Framework +// Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin +// Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -69,12 +69,6 @@ QWidget *CTranslationManagerSettingsPage::createPage(QWidget *parent) _currentPage = new QWidget(parent); _ui.setupUi(_currentPage); readSettings(); - connect(_ui.paths_add, SIGNAL(clicked()), this, SLOT(pathAdd())); - connect(_ui.paths_del, SIGNAL(clicked()), this, SLOT(pathDel())); - connect(_ui.pathsR_add, SIGNAL(clicked()), this, SLOT(pathRAdd())); - connect(_ui.pathsR_del, SIGNAL(clicked()), this, SLOT(pathRDel())); - connect(_ui.georges_add, SIGNAL(clicked()), this, SLOT(georgeAdd())); - connect(_ui.georges_del, SIGNAL(clicked()), this, SLOT(georgeDel())); connect(_ui.filter_add, SIGNAL(clicked()), this, SLOT(filterAdd())); connect(_ui.filter_del, SIGNAL(clicked()), this, SLOT(filterDel())); connect(_ui.lang_add, SIGNAL(clicked()), this, SLOT(languageAdd())); @@ -85,66 +79,6 @@ QWidget *CTranslationManagerSettingsPage::createPage(QWidget *parent) return _currentPage; } -void CTranslationManagerSettingsPage::pathAdd() -{ - QString newPath = QFileDialog::getExistingDirectory(_currentPage, "", lastDir); - if (!newPath.isEmpty()) - { - QListWidgetItem *newItem = new QListWidgetItem; - newItem->setText(newPath); - newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - _ui.paths_list->addItem(newItem); - lastDir = newPath; - } -} - -void CTranslationManagerSettingsPage::pathDel() -{ - QListWidgetItem *removeItem = _ui.paths_list->takeItem(_ui.paths_list->currentRow()); - if (!removeItem) - delete removeItem; -} - -void CTranslationManagerSettingsPage::pathRAdd() -{ - QString newPath = QFileDialog::getExistingDirectory(_currentPage, "", lastDir); - if (!newPath.isEmpty()) - { - QListWidgetItem *newItem = new QListWidgetItem; - newItem->setText(newPath); - newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - _ui.pathsR_list->addItem(newItem); - lastDir = newPath; - } -} - -void CTranslationManagerSettingsPage::pathRDel() -{ - QListWidgetItem *removeItem = _ui.pathsR_list->takeItem(_ui.pathsR_list->currentRow()); - if (!removeItem) - delete removeItem; -} - -void CTranslationManagerSettingsPage::georgeAdd() -{ - QString newPath = QFileDialog::getExistingDirectory(_currentPage, "", lastDir); - if (!newPath.isEmpty()) - { - QListWidgetItem *newItem = new QListWidgetItem; - newItem->setText(newPath); - newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - _ui.georges_list->addItem(newItem); - lastDir = newPath; - } -} - -void CTranslationManagerSettingsPage::georgeDel() -{ - QListWidgetItem *removeItem = _ui.georges_list->takeItem(_ui.georges_list->currentRow()); - if (!removeItem) - delete removeItem; -} - void CTranslationManagerSettingsPage::filterAdd() { QString newValue = _ui.filter_edit->text(); @@ -208,15 +142,12 @@ void CTranslationManagerSettingsPage::apply() void CTranslationManagerSettingsPage::readSettings() { - QStringList paths, pathsR, georges, filters, languages; + QStringList filters, languages; QString ligo, translation, work; QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("translationmanager"); - paths = settings->value("paths").toStringList(); /* paths */ - pathsR = settings->value("pathsR").toStringList(); /* pathsR */ - georges = settings->value("georges").toStringList(); /* georges */ filters = settings->value("filters").toStringList(); /* filters */ languages = settings->value("trlanguages").toStringList(); /* languages */ ligo = settings->value("ligo").toString(); @@ -224,30 +155,6 @@ void CTranslationManagerSettingsPage::readSettings() work = settings->value("work").toString(); settings->endGroup(); - // paths - Q_FOREACH(QString path, paths) - { - QListWidgetItem *newItem = new QListWidgetItem; - newItem->setText(path); - newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - _ui.paths_list->addItem(newItem); - } - // pathsR - Q_FOREACH(QString pathR, pathsR) - { - QListWidgetItem *newItem = new QListWidgetItem; - newItem->setText(pathR); - newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - _ui.pathsR_list->addItem(newItem); - } - // georges - Q_FOREACH(QString george, georges) - { - QListWidgetItem *newItem = new QListWidgetItem; - newItem->setText(george); - newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - _ui.georges_list->addItem(newItem); - } // filter Q_FOREACH(QString filter, filters) { @@ -264,8 +171,6 @@ void CTranslationManagerSettingsPage::readSettings() newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.lang_list->addItem(newItem); } - // ligo - _ui.ligo_edit->setText(ligo); // translation _ui.translation_edit->setText(translation); // work @@ -275,25 +180,14 @@ void CTranslationManagerSettingsPage::readSettings() void CTranslationManagerSettingsPage::writeSettings() { - QStringList paths, pathsR, georges, filters, languages; + QStringList filters, languages; QString ligo, translation, work; - // paths - for (int i = 0; i < _ui.paths_list->count(); ++i) - paths << _ui.paths_list->item(i)->text(); - // pathsR - for (int i = 0; i < _ui.pathsR_list->count(); ++i) - pathsR << _ui.pathsR_list->item(i)->text(); - // georges - for (int i = 0; i < _ui.georges_list->count(); ++i) - georges << _ui.georges_list->item(i)->text(); // filters for (int i = 0; i < _ui.filter_list->count(); ++i) filters << _ui.filter_list->item(i)->text(); // languages for (int i = 0; i < _ui.lang_list->count(); ++i) languages << _ui.lang_list->item(i)->text(); - // ligo path - ligo = _ui.ligo_edit->text(); // translations path translation = _ui.translation_edit->text(); // work path @@ -301,12 +195,8 @@ void CTranslationManagerSettingsPage::writeSettings() QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("translationmanager"); - settings->setValue("paths", paths); - settings->setValue("pathsR", pathsR); - settings->setValue("georges", georges); settings->setValue("filters", filters); - settings->setValue("trlanguages", languages); - settings->setValue("ligo", ligo); + settings->setValue("trlanguages", languages); settings->setValue("translation", translation); settings->setValue("work", work); settings->endGroup(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h index 7a082f199..1cf19787a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h @@ -1,6 +1,6 @@ -// Object Viewer Qt - MMORPG Framework +// Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin +// Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as @@ -15,7 +15,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . - #ifndef TRANSLATION_MANAGER_SETTINGS_PAGE_H #define TRANSLATION_MANAGER_SETTINGS_PAGE_H @@ -49,12 +48,6 @@ public: virtual void apply(); virtual void finish() {} private Q_SLOTS: - void pathAdd(); - void pathDel(); - void pathRAdd(); - void pathRDel(); - void georgeAdd(); - void georgeDel(); void filterAdd(); void filterDel(); void languageAdd(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui index 4c932b4f5..aaa4337f4 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui @@ -6,388 +6,181 @@ 0 0 - 490 - 496 + 533 + 478 Form - - - - - 1 - - - - Core paths - - - - - - - - - - Paths - - - - - - - Qt::Horizontal - - - - 318 - 20 - - - - - - - - dwadwadwa - - - - :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png - - - Qt::ToolButtonIconOnly - - - true - - - - - - - ... - - - - :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png - - - true - - - - - - - - - - - - - - - - - - Paths non recursives - - - - - - - Qt::Horizontal - - - - 218 - 20 - - - - - - - - - - - - :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png - - - true - - - - - - - ... - - - - :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png - - - true - - - - - - - - - - - - - - - - - - Georges Paths - - - - - - - Qt::Horizontal - - - - 258 - 20 - - - - - - - - - - - - :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png - - - true - - - - - - - - - - - :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png - - - true - - - - - - - - - - - - - - - Translation files paths - - - - - 9 - 190 - 454 - 161 - + + + + 0 + 10 + 531 + 421 + + + + Translation Manager Plugin + + + + + 0 + 30 + 521 + 232 + + + + + + + Filters - - - - - Ligo class file - This is the name of the world_editor_classes.xml file. - - - - - - - - - - Work directory - - - - - - - - - - - - ... - - - - - - - - - Translation directory - - - - - - - - - - - - ... - - - - - - - - - - 9 - 10 - 211 - 181 - + + + + + + + + - - - - - Filters - - - - - - - - - - - - - - :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png - - - true - - - - - - - - - - - :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png - - - true - - - - - - - - - - - - 240 - 10 - 221 - 181 - + + + :/core/icons/ic_nel_add_item.png:/core/icons/ic_nel_add_item.png + + + true - - - - - Languages - - - - - - - - - - - - - - :/icons/ic_nel_add_item.png:/icons/ic_nel_add_item.png - - - true - - - - - - - - - - - :/icons/ic_nel_delete_item.png:/icons/ic_nel_delete_item.png - - - true - - - - - - - - - - - + + + + + + + + + :/core/icons/ic_nel_delete_item.png:/core/icons/ic_nel_delete_item.png + + + true + + + + + + + Languages + + + + + + + + + + + + + + :/core/icons/ic_nel_add_item.png:/core/icons/ic_nel_add_item.png + + + true + + + + + + + + + + + :/core/icons/ic_nel_delete_item.png:/core/icons/ic_nel_delete_item.png + + + true + + + + + + + + + + + + + + + 0 + 340 + 521 + 60 + + + + + + + Translation directory + + + + + + + + + + ... + + + + + + + + + 0 + 270 + 521 + 60 + + + + + + + Work directory + + + + + + + + + + ... + + + + + + - - + From d54daf3ef254232a66f56f18fb11f5e7008337c5 Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 6 Jul 2011 04:56:58 +0300 Subject: [PATCH 036/215] Added: #1307 Added extraction from primitives --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_worksheet.cpp | 135 +++++++++++- .../translation_manager/editor_worksheet.h | 2 + .../extract_new_sheet_names.cpp | 13 +- .../extract_new_sheet_names.h | 1 + .../translation_manager_main_window.cpp | 203 +++++++++++++----- .../translation_manager_main_window.h | 3 +- 6 files changed, 292 insertions(+), 65 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index d19d28723..83c67e7a6 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -25,6 +25,7 @@ #include #include "extract_bot_names.h" +#include "translation_manager_constants.h" using namespace std; @@ -38,7 +39,7 @@ void CEditorWorksheet::open(QString filename) if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) { bool hasHashValue = false; - table_editor = new QTableWidget(); + table_editor = new QTableWidget(); if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) { table_editor->setColumnCount(wk_file.ColCount - 1); @@ -105,8 +106,7 @@ void CEditorWorksheet::open(QString filename) void CEditorWorksheet::activateWindow() { - showMaximized(); - + showMaximized(); } void CEditorWorksheet::save() @@ -209,7 +209,6 @@ void CEditorWorksheet::insertRow() for(int j = 0; j < table_editor->columnCount(); j++) { QTableWidgetItem* item = new QTableWidgetItem(); - //item->setText(QString(" ")); table_editor->setItem(last_row, j, item); } } @@ -236,7 +235,8 @@ void CEditorWorksheet::deleteRow() void CEditorWorksheet::worksheetEditorChanged(int row, int column) { - + if(!isWindowModified()) + setWindowModified(true); } void CEditorWorksheet::extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig) @@ -378,9 +378,73 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis if(modified) { setWindowModified(true); + table_editor->scrollToBottom(); } } +void CEditorWorksheet::mergeWorksheetFile(QString filename) +{ + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + bool hasHashValue = false; + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + table_editor->setColumnCount(wk_file.ColCount - 1); + hasHashValue = true; + } else { + table_editor->setColumnCount(wk_file.ColCount); + } + table_editor->setRowCount(wk_file.size() - 1); + + // read columns name + for(unsigned int i = 0; i < wk_file.ColCount; i++) + { + if(hasHashValue && i == 0) + { + // we don't show the column with hash value + } else { + QTableWidgetItem *col = new QTableWidgetItem(); + ucstring col_name = wk_file.getData(0, i); + col->setText(tr(col_name.toString().c_str())); + if(hasHashValue) + { + table_editor->setHorizontalHeaderItem(i - 1, col); + } else { + table_editor->setHorizontalHeaderItem(i, col); + } + } + } + + // read rows + for(unsigned int i = 1; i < wk_file.size(); i++) + { + for(unsigned int j = 0; j < wk_file.ColCount; j++) + { + if(hasHashValue && j == 0) + { + // we don't show the column with hash value + } else { + QTableWidgetItem *row = new QTableWidgetItem(); + ucstring row_value = wk_file.getData(i, j); + row->setText(tr(row_value.toString().c_str())); + if(hasHashValue) + { + table_editor->setItem(i - 1, j - 1, row); + } else { + table_editor->setItem(i - 1, j, row); + } + } + } + } + } else { + QErrorMessage error; + error.showMessage("This file is not a worksheet file."); + error.exec(); + } + +} + void CEditorWorksheet::setCurrentFile(QString filename) { QFileInfo *file = new QFileInfo(filename); @@ -392,9 +456,35 @@ void CEditorWorksheet::setCurrentFile(QString filename) void CEditorWorksheet::closeEvent(QCloseEvent *event) { - close(); - event->accept(); - + if(isWindowModified()) + { + QMessageBox msgBox; + msgBox.setText("The document has been modified."); + msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Save: + save(); + event->accept(); + close(); + break; + case QMessageBox::Discard: + event->accept(); + close(); + break; + case QMessageBox::Cancel: + event->ignore(); + break; + default: + break; + } + } else { + event->accept(); + close(); + } } bool CEditorWorksheet::isBotNamesTable() @@ -410,6 +500,35 @@ bool CEditorWorksheet::isBotNamesTable() return status; } +bool CEditorWorksheet::isSheetTable(QString type) +{ + QString column_name; + if(type.toAscii() == Constants::WK_ITEM) + { + column_name = "item ID"; + } else if(type.toAscii() == Constants::WK_CREATURE) { + column_name = "creature ID"; + } else if(type.toAscii() == Constants::WK_SBRICK) { + column_name = "sbrick ID"; + } else if(type.toAscii() == Constants::WK_SPHRASE) { + column_name = "sphrase ID"; + } else if(type.toAscii() == Constants::WK_PLACE) { + column_name = "placeId"; + } else if(type.toAscii() == Constants::WK_CONTINENT) { + column_name = "placeId"; + } else if(type.toAscii() == Constants::WK_STABLE) { + column_name = "placeId"; + } + bool status = true; + if(table_editor->horizontalHeaderItem(0)->text() != column_name + || table_editor->horizontalHeaderItem(1)->text() != "name") + { + status = false; + } + + return status; +} + } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 497e23913..39f4aafa1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -49,9 +49,11 @@ public: void save(); void saveAs(QString filename); void activateWindow(); + void mergeWorksheetFile(QString filename); void extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig); void extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder); bool isBotNamesTable(); + bool isSheetTable(QString type); void closeEvent(QCloseEvent *event); private Q_SLOTS: void worksheetEditorChanged(int,int); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp index e881177bb..e0626bc6b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp @@ -108,12 +108,12 @@ bool CRegionPrimWordListBuilder::buildWordList(std::vector &allWords, st // ok, read the file CPrimitives PrimDoc; CPrimitiveContext::instance().CurrentPrimitive = &PrimDoc; - // if (!loadXmlPrimitiveFile(PrimDoc, allFiles[i], LigoConfig)) - // { - // nlwarning("Error: cannot open file '%s'. '%s' Aborted", allFiles[i].c_str(), workSheetFileName.c_str()); - // CPrimitiveContext::instance().CurrentPrimitive = NULL; - // return false; - // } + if (!loadXmlPrimitiveFile(PrimDoc, allFiles[i], LigoConfig)) + { + nlwarning("Error: cannot open file '%s'. '%s' Aborted", allFiles[i].c_str(), workSheetFileName.c_str()); + CPrimitiveContext::instance().CurrentPrimitive = NULL; + return false; + } CPrimitiveContext::instance().CurrentPrimitive = NULL; // For all primitives of interest @@ -141,6 +141,7 @@ bool CRegionPrimWordListBuilder::buildWordList(std::vector &allWords, st // avoid duplicate if(allWordSet.insert(primName).second) { + nlinfo(primName.c_str()); //TODO: delete allWords.push_back(primName); } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h index 007018a08..8c9151e2e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h @@ -60,6 +60,7 @@ struct CRegionPrimWordListBuilder : public IWordListBuilder { string PrimPath; vector PrimFilter; + NLLIGO::CLigoConfig LigoConfig; virtual bool buildWordList(std::vector &allWords, string workSheetFileName); }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 95a5fe95f..daff3923d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -16,6 +16,7 @@ // along with this program. If not, see . #include "translation_manager_main_window.h" +#include "translation_manager_constants.h" #include "editor_worksheet.h" // Project system includes @@ -86,7 +87,9 @@ void CMainWindow::createToolbar() // extract bot names QAction *extractBotNamesAct = wordsExtractionMenu->addAction("&Extract bot names..."); extractBotNamesAct->setStatusTip(tr("Extract bot names from primitives.")); - connect(extractBotNamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); + connect(extractBotNamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); + // Words extraction + // ----------------------------- // signal mapper for extraction words QSignalMapper *wordsExtractionMapper = new QSignalMapper(this); connect(wordsExtractionMapper, SIGNAL(mapped(QString)), this, SLOT(extractWords(QString))); @@ -94,23 +97,37 @@ void CMainWindow::createToolbar() QAction *extractItemWordsAct = wordsExtractionMenu->addAction("&Extract item words..."); extractItemWordsAct->setStatusTip(tr("Extract item words")); connect(extractItemWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractItemWordsAct, "item"); + wordsExtractionMapper->setMapping(extractItemWordsAct, tr(Constants::WK_ITEM)); // extract creature words QAction *extractCreatureWordsAct = wordsExtractionMenu->addAction("&Extract creature words..."); extractCreatureWordsAct->setStatusTip(tr("Extract creature words")); connect(extractCreatureWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractCreatureWordsAct, "creature"); + wordsExtractionMapper->setMapping(extractCreatureWordsAct, tr(Constants::WK_CREATURE)); // extract sbrick words QAction *extractSbrickWordsAct = wordsExtractionMenu->addAction("&Extract sbrick words..."); extractSbrickWordsAct->setStatusTip(tr("Extract sbrick words")); connect(extractSbrickWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractSbrickWordsAct, "sbrick"); + wordsExtractionMapper->setMapping(extractSbrickWordsAct, tr(Constants::WK_SBRICK)); // extract sphrase words QAction *extractSphraseWordsAct = wordsExtractionMenu->addAction("&Extract sphrase words..."); extractSphraseWordsAct->setStatusTip(tr("Extract sphrase words")); connect(extractSphraseWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractSphraseWordsAct, "sphrase"); - + wordsExtractionMapper->setMapping(extractSphraseWordsAct, tr(Constants::WK_SPHRASE)); + // extract place and region names + QAction *extractPlaceNamesAct = wordsExtractionMenu->addAction("&Extract place and region names..."); + extractPlaceNamesAct->setStatusTip(tr("Extract place and region names")); + connect(extractPlaceNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractPlaceNamesAct, tr(Constants::WK_PLACE)); + // extract continent names + QAction *extractContinentNamesAct = wordsExtractionMenu->addAction("&Extract continent names..."); + extractContinentNamesAct->setStatusTip(tr("Extract continent names")); + connect(extractContinentNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractContinentNamesAct, tr(Constants::WK_CONTINENT)); + // extract stable names + QAction *extractStableNamesAct = wordsExtractionMenu->addAction("&Extract stable names..."); + extractStableNamesAct->setStatusTip(tr("Extract stable names")); + connect(extractStableNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractStableNamesAct, tr(Constants::WK_STABLE)); // Windows menu windowMenu = new QMenu(tr("&Windows..."), _ui.toolBar); windowMenu->setIcon(QIcon(Core::Constants::ICON_PILL)); @@ -144,7 +161,7 @@ void CMainWindow::setActiveSubWindow(QWidget* window) void CMainWindow::activeSubWindowChanged() { - + } void CMainWindow::updateWindowsList() @@ -215,11 +232,7 @@ void CMainWindow::openWorkFile(QString file) } } else { QErrorMessage error; - QString text; - text.append("The "); - text.append(file_path->fileName()); - text.append(" file don't exists."); - error.showMessage(text); + error.showMessage(QString("The %1 file don't exists.").arg(file_path->fileName())); error.exec(); } @@ -227,11 +240,14 @@ void CMainWindow::openWorkFile(QString file) void CMainWindow::save() { - CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - - if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + if(_ui.mdiArea->subWindowList().size() > 0) { - current_window->save(); + CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + + if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + { + current_window->save(); + } } } @@ -275,35 +291,111 @@ void CMainWindow::initializeSettings(bool georges = false) } } -void CMainWindow::extractWords(QString type) +void CMainWindow::extractWords(QString typeq) { - CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - CEditorWorksheet* current_window = qobject_cast(editor_window); - - // initializeSettings(false); - - CSheetWordListBuilder builder; - QString column_name; - - if(type == "item") - { - column_name = "item ID"; - builder.SheetExt = "sitem"; - builder.SheetPath = level_design_path + "/game_element/sitem"; - } else if(type == "creature") { - column_name = "creature ID"; - builder.SheetExt = "creature"; - builder.SheetPath = level_design_path + "/Game_elem/Creature/fauna"; - } else if(type == "sbrick") { - column_name = "sbrick ID"; - builder.SheetExt = "sbrick"; - builder.SheetPath = level_design_path + "/game_element/sbrick"; - } else if(type == "sphrase") { - column_name = "sphrase ID"; - builder.SheetExt = "sphrase"; - builder.SheetPath = level_design_path + "/game_element/sphrase"; - } - current_window->extractWords(current_window->windowFilePath(), column_name, builder); + if(verifySettings() == true) + { + CEditorWorksheet* current_window; + if(_ui.mdiArea->subWindowList().size() > 0) + { + CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + if(QString(editor_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + { + current_window = qobject_cast(editor_window); + QString file_path = current_window->subWindowFilePath(); + if(!current_window->isSheetTable(typeq)) + { + list subWindows = convertSubWindowList(_ui.mdiArea->subWindowList()); + list::iterator it = subWindows.begin(); + bool finded = false; + + for(; it != subWindows.end(); ++it) + { + current_window = qobject_cast((*it)); + file_path = current_window->subWindowFilePath(); + if(current_window->isSheetTable(typeq)) + { + finded = true; + current_window->activateWindow(); + } + } + if(!finded) + { + openWorkFile(typeq); + if(_ui.mdiArea->subWindowList().size() > 0) + { + current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + QString file_path = current_window->windowFilePath(); + } else { + return; + } + } + } + } + } else { + openWorkFile(typeq); + if(_ui.mdiArea->subWindowList().size() > 0) + { + current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + QString file_path = current_window->windowFilePath(); + } else { + return; + } + + } + QString column_name; + // Sheet extraction + CSheetWordListBuilder builderS; + // Primitives extraction + CRegionPrimWordListBuilder builderP; + bool isSheet = false; + if(typeq.toAscii() == Constants::WK_ITEM) { + column_name = "item ID"; + builderS.SheetExt = "sitem"; + builderS.SheetPath = level_design_path + "/game_element/sitem"; + isSheet = true; + } else if(typeq.toAscii() == Constants::WK_CREATURE) { + column_name = "creature ID"; + builderS.SheetExt = "creature"; + builderS.SheetPath = level_design_path + "/Game_elem/Creature/fauna"; + isSheet = true; + } else if(typeq.toAscii() == Constants::WK_SBRICK) { + column_name = "sbrick ID"; + builderS.SheetExt = "sbrick"; + builderS.SheetPath = level_design_path + "/game_element/sbrick"; + isSheet = true; + } else if(typeq.toAscii() == Constants::WK_SPHRASE) { + column_name = "sphrase ID"; + builderS.SheetExt = "sphrase"; + builderS.SheetPath = level_design_path + "/game_element/sphrase"; + isSheet = true; + } else if(typeq.toAscii() == Constants::WK_PLACE) { + column_name = "placeId"; + builderP.PrimPath = primitives_path; + builderP.PrimFilter.push_back("region_*.primitive"); + builderP.PrimFilter.push_back("indoors_*.primitive"); + isSheet = false; + } else if(typeq.toAscii() == Constants::WK_CONTINENT) { + column_name = "placeId"; + builderP.PrimPath = primitives_path; + builderP.PrimFilter.push_back("continent_*.primitive"); + isSheet = false; + } else if(typeq.toAscii() == Constants::WK_STABLE) { + column_name = "placeId"; + builderP.PrimPath = primitives_path; + builderP.PrimFilter.push_back("stable_*.primitive"); + isSheet = false; + } + + if(isSheet) + { + current_window->extractWords(current_window->windowFilePath(), column_name, builderS); + } else { + initializeSettings(false); + current_window->extractWords(current_window->windowFilePath(), column_name, builderP); + } + } + } void CMainWindow::extractBotNames() @@ -336,16 +428,26 @@ void CMainWindow::extractBotNames() } if(!finded) { - openWorkFile("bot_names_wk.txt"); - current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - file_path = current_window->windowFilePath(); + openWorkFile(tr(Constants::WK_BOTNAMES)); + if(_ui.mdiArea->subWindowList().size() > 0) + { + current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + QString file_path = current_window->windowFilePath(); + } else { + return; + } } } } } else { - openWorkFile("bot_names_wk.txt"); - current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - QString file_path = current_window->windowFilePath(); + openWorkFile(tr(Constants::WK_BOTNAMES)); + if(_ui.mdiArea->subWindowList().size() > 0) + { + current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + QString file_path = current_window->windowFilePath(); + } else { + return; + } } initializeSettings(true); current_window->extractBotNames(filters, level_design_path, ligoConfig); @@ -363,6 +465,7 @@ void CMainWindow::readSettings() settings->endGroup(); settings->beginGroup(Core::Constants::DATA_PATH_SECTION); level_design_path = settings->value(Core::Constants::LEVELDESIGN_PATH).toString().toStdString(); + primitives_path = QString(Core::Constants::PRIMITIVES_PATH).toStdString(); settings->endGroup(); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index b0d59fe29..2ee796bd0 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -73,12 +73,13 @@ private: list filters; list languages; string level_design_path; + string primitives_path; string translation_path; string work_path; NLLIGO::CLigoConfig ligoConfig; private Q_SLOTS: void extractBotNames(); - void extractWords(QString); + void extractWords(QString typeq); void open(); void save(); void saveAs(); From 766420cd11803a307bdb2ee5d25e68abc938469a Mon Sep 17 00:00:00 2001 From: cemycc Date: Fri, 8 Jul 2011 18:22:51 +0300 Subject: [PATCH 037/215] Added: #1307 Merge options for translation files from local directory and from FTP server --HG-- branch : gsoc2011-translationovqt --- .../src/plugins/core/core_constants.h | 117 ----------------- .../translation_manager/CMakeLists.txt | 9 +- .../translation_manager/editor_worksheet.cpp | 119 +++++++++--------- .../translation_manager/editor_worksheet.h | 1 + .../extract_new_sheet_names.cpp | 1 - .../translation_manager_main_window.cpp | 82 ++++++++---- .../translation_manager_main_window.h | 4 +- 7 files changed, 132 insertions(+), 201 deletions(-) delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h deleted file mode 100644 index 2acf4f731..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h +++ /dev/null @@ -1,117 +0,0 @@ -// Object Viewer Qt - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2010 Dzmitry Kamiahin -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef CORE_CONSTANTS_H -#define CORE_CONSTANTS_H - -namespace Core -{ -namespace Constants -{ - -const char * const OVQT_VERSION_LONG = "0.1"; -const char * const OVQT_VENDOR = "Ryzom Core"; -const char * const OVQT_YEAR = "2010, 2011"; -const char * const OVQT_CORE_PLUGIN = "Core"; - -//mainwindow -const char * const MAIN_WINDOW = "ObjectViewerQt.MainWindow"; - -//menubar -const char * const MENU_BAR = "ObjectViewerQt.MenuBar"; - -//menus -const char * const M_FILE = "ObjectViewerQt.Menu.File"; -const char * const M_EDIT = "ObjectViewerQt.Menu.Edit"; -const char * const M_VIEW = "ObjectViewerQt.Menu.View"; -const char * const M_SCENE = "ObjectViewerQt.Menu.Scene"; -const char * const M_TOOLS = "ObjectViewerQt.Menu.Tools"; -const char * const M_WINDOW = "ObjectViewerQt.Menu.Window"; -const char * const M_HELP = "ObjectViewerQt.Menu.Help"; - -const char * const M_FILE_RECENTFILES = "ObjectViewerQt.Menu.File.RecentFiles"; -const char * const M_SHEET = "ObjectViewerQt.Menu.Sheet"; - -//actions -const char * const NEW = "ObjectViewerQt.New"; -const char * const OPEN = "ObjectViewerQt.Open"; -const char * const SAVE = "ObjectViewerQt.Save"; -const char * const SAVE_AS = "ObjectViewerQt.SaveAs"; -const char * const SAVE_ALL = "ObjectViewerQt.SaveAll"; -const char * const EXIT = "ObjectViewerQt.Exit"; - -const char * const UNDO = "ObjectViewerQt.Undo"; -const char * const REDO = "ObjectViewerQt.Redo"; -const char * const CUT = "ObjectViewerQt.Cut"; -const char * const COPY = "ObjectViewerQt.Copy"; -const char * const PASTE = "ObjectViewerQt.Paste"; -const char * const DEL = "ObjectViewerQt.Del"; -const char * const FIND = "ObjectViewerQt.Find"; -const char * const SELECT_ALL = "ObjectViewerQt.SelectAll"; -const char * const GOTO_POS = "ObjectViewerQt.Goto"; - -const char * const SETTINGS = "ObjectViewerQt.Settings"; -const char * const TOGGLE_FULLSCREEN = "ObjectViewerQt.ToggleFullScreen"; - -const char * const CLOSE = "ObjectViewerQt.Close"; -const char * const CLOSEALL = "ObjectViewerQt.CloseAll"; -const char * const CLOSEOTHERS = "ObjectViewerQt.CloseOthers"; -const char * const ABOUT = "ObjectViewerQt.About"; -const char * const ABOUT_PLUGINS = "ObjectViewerQt.AboutPlugins"; -const char * const ABOUT_QT = "ObjectViewerQt.AboutQt"; - -//settings -const char * const SETTINGS_CATEGORY_GENERAL = "general"; -const char * const SETTINGS_CATEGORY_GENERAL_ICON = ":/icons/ic_nel_generic_settings.png"; -const char * const SETTINGS_TR_CATEGORY_GENERAL = QT_TR_NOOP("General"); - -const char * const MAIN_WINDOW_SECTION = "MainWindow"; -const char * const MAIN_WINDOW_STATE = "WindowState"; -const char * const MAIN_WINDOW_GEOMETRY = "WindowGeometry"; -const char * const QT_STYLE = "QtStyle"; -const char * const QT_PALETTE = "QtPalette"; - -const char * const LANGUAGE = "Language"; -const char * const PLUGINS_PATH = "PluginPath"; -const char * const DATA_PATH_SECTION = "DataPath"; -const char * const SEARCH_PATHS = "SearchPaths"; -const char * const RECURSIVE_SEARCH_PATHS = "RecursiveSearchPathes"; -const char * const LEVELDESIGN_PATH = "LevelDesignPath"; -const char * const ASSETS_PATH = "AssetsPath"; -const char * const REMAP_EXTENSIONS = "RemapExtensions"; - -const char * const LOG_SECTION = "LogSettings"; -const char * const LOG_ERROR = "LogError"; -const char * const LOG_WARNING = "LogWarning"; -const char * const LOG_DEBUG = "LogDebug"; -const char * const LOG_ASSERT = "LogAssert"; -const char * const LOG_INFO = "LogInfo"; - -//resources -const char * const ICON_NEL = ":/core/images/nel.png"; -const char * const ICON_SETTINGS = ":/core/images/preferences.png"; -const char * const ICON_PILL = ":/core/icons/ic_nel_pill.png"; -const char * const ICON_OPEN = ":/core/icons/ic_nel_open.png"; -const char * const ICON_NEW = ":/core/icons/ic_nel_new.png"; -const char * const ICON_SAVE = ":/core/icons/ic_nel_save.png"; -const char * const ICON_SAVE_AS = ":/core/icons/ic_nel_save_as.png"; -const char * const ICON_CRASH = ":/core/icons/ic_nel_crash.png"; - -} // namespace Constants -} // namespace Core - -#endif // CORE_CONSTANTS_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt index 56195d6e0..59f11c4f8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -13,15 +13,20 @@ SET(OVQT_PLUG_TRANSLATION_MANAGER_HDR translation_manager_plugin.h translation_manager_main_window.h translation_manager_settings_page.h translation_manager_editor.h + source_selection.h + ftp_selection.h editor_worksheet.h extract_new_sheet_names.h extract_bot_names.h) SET(OVQT_PLUG_TRANSLATION_MANAGER_UIS translation_manager_settings_page.ui - translation_manager_main_window.ui) + translation_manager_main_window.ui + source_selection.ui + ftp_selection.ui) SET(QT_USE_QTGUI TRUE) SET(QT_USE_QTOPENGL TRUE) +SET(QT_USE_QTNETWORK TRUE) QT4_WRAP_CPP(OVQT_PLUG_TRANSLATION_MANAGER_MOC_SRC ${OVQT_PLUG_TRANSLATION_MANAGER_HDR}) QT4_WRAP_UI(OVQT_PLUG_TRANSLATION_MANAGER_UI_HDRS ${OVQT_PLUG_TRANSLATION_MANAGER_UIS}) @@ -34,7 +39,7 @@ SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) ADD_LIBRARY(ovqt_plugin_translation_manager MODULE ${SRC} ${OVQT_PLUG_TRANSLATION_MANAGER_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_TRANSLATION_MANAGER_UI_HDRS}) -TARGET_LINK_LIBRARIES(ovqt_plugin_translation_manager ovqt_plugin_core nelmisc nel3d nelligo nelgeorges ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY}) +TARGET_LINK_LIBRARIES(ovqt_plugin_translation_manager ovqt_plugin_core nelmisc nel3d nelligo nelgeorges ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY} ${QT_QTNETWORK_LIBRARY} ) NL_DEFAULT_PROPS(ovqt_plugin_translation_manager "NeL, Tools, 3D: Object Viewer Qt Plugin: Translation Manager") NL_ADD_RUNTIME_FLAGS(ovqt_plugin_translation_manager) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index 83c67e7a6..de20db5ec 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -139,14 +139,14 @@ void CEditorWorksheet::save() colname = wk_file.getData(0, j + colIdx); rowIdf = uint(i + 1); - if(wk_file.findRow(j + colIdx, colname, rowIdf)) + if(wk_file.findRow(j + colIdx, colname, rowIdf)) // search for the row { - if(wk_file.getData(i + 1, j + colIdx) != tvalue) + if(wk_file.getData(i + 1, j + colIdx) != tvalue) // verify the current value { - wk_file.setData(i + 1, j + colIdx, tvalue); + wk_file.setData(i + 1, j + colIdx, tvalue); // change the value } } else { - wk_file.setData(i + 1, j + colIdx, tvalue); + wk_file.setData(i + 1, j + colIdx, tvalue); // insert the value } } } @@ -382,67 +382,74 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis } } +bool CEditorWorksheet::compareWorksheetFile(QString filename) +{ + STRING_MANAGER::TWorksheet wk_file; + int colIndex = 0; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + colIndex = 1; + } + if(wk_file.ColCount - colIndex != table_editor->columnCount()) + { + return false; + } + for(int i = 0; i < table_editor->columnCount(); i++) + { + QString item = table_editor->horizontalHeaderItem(i)->text(); + ucstring itemC = wk_file.getData(0, i+ colIndex); + if(item.toStdString() != itemC.toString()) + { + nlwarning(item.toStdString().c_str()); + nlwarning(itemC.toString().c_str()); + return false; + } + } + } else { + return false; + } + + return true; +} + void CEditorWorksheet::mergeWorksheetFile(QString filename) { STRING_MANAGER::TWorksheet wk_file; if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) { - bool hasHashValue = false; + bool hasHashValue = false; + int colIndex = 0; if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) { - table_editor->setColumnCount(wk_file.ColCount - 1); hasHashValue = true; - } else { - table_editor->setColumnCount(wk_file.ColCount); - } - table_editor->setRowCount(wk_file.size() - 1); - - // read columns name - for(unsigned int i = 0; i < wk_file.ColCount; i++) - { - if(hasHashValue && i == 0) - { - // we don't show the column with hash value - } else { - QTableWidgetItem *col = new QTableWidgetItem(); - ucstring col_name = wk_file.getData(0, i); - col->setText(tr(col_name.toString().c_str())); - if(hasHashValue) - { - table_editor->setHorizontalHeaderItem(i - 1, col); - } else { - table_editor->setHorizontalHeaderItem(i, col); - } - } - } - + colIndex = 1; + } // read rows for(unsigned int i = 1; i < wk_file.size(); i++) { - for(unsigned int j = 0; j < wk_file.ColCount; j++) + // search with the first column + ucstring rowId = wk_file.getData(i,colIndex); + QList search_results = table_editor->findItems(tr(rowId.toString().c_str()), Qt::MatchExactly); + if(search_results.size() == 0) { - if(hasHashValue && j == 0) - { - // we don't show the column with hash value - } else { - QTableWidgetItem *row = new QTableWidgetItem(); - ucstring row_value = wk_file.getData(i, j); - row->setText(tr(row_value.toString().c_str())); - if(hasHashValue) - { - table_editor->setItem(i - 1, j - 1, row); - } else { - table_editor->setItem(i - 1, j, row); - } - } - } - } - } else { - QErrorMessage error; - error.showMessage("This file is not a worksheet file."); - error.exec(); - } - + const int lastRow = table_editor->rowCount(); + table_editor->setRowCount(lastRow + 1); + for(unsigned int j = 0; j < table_editor->columnCount(); j++) + { + ucstring rowValue = wk_file.getData(i, j + colIndex); // get the value + QTableWidgetItem *row = new QTableWidgetItem(); + row->setText(QString(rowValue.toString().c_str())); // set the value in table item + table_editor->setItem(lastRow, j, row); + } + } + } + } else { + QErrorMessage error; + error.showMessage("This file is not a worksheet file."); + error.exec(); + } } void CEditorWorksheet::setCurrentFile(QString filename) @@ -514,11 +521,7 @@ bool CEditorWorksheet::isSheetTable(QString type) column_name = "sphrase ID"; } else if(type.toAscii() == Constants::WK_PLACE) { column_name = "placeId"; - } else if(type.toAscii() == Constants::WK_CONTINENT) { - column_name = "placeId"; - } else if(type.toAscii() == Constants::WK_STABLE) { - column_name = "placeId"; - } + } bool status = true; if(table_editor->horizontalHeaderItem(0)->text() != column_name || table_editor->horizontalHeaderItem(1)->text() != "name") diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 39f4aafa1..95ee140d1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -50,6 +50,7 @@ public: void saveAs(QString filename); void activateWindow(); void mergeWorksheetFile(QString filename); + bool compareWorksheetFile(QString filename); void extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig); void extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder); bool isBotNamesTable(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp index e0626bc6b..f2369fc1c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp @@ -141,7 +141,6 @@ bool CRegionPrimWordListBuilder::buildWordList(std::vector &allWords, st // avoid duplicate if(allWordSet.insert(primName).second) { - nlinfo(primName.c_str()); //TODO: delete allWords.push_back(primName); } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index daff3923d..548aa4b37 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -18,6 +18,7 @@ #include "translation_manager_main_window.h" #include "translation_manager_constants.h" #include "editor_worksheet.h" +#include "ftp_selection.h" // Project system includes #include "../core/icore.h" @@ -114,20 +115,15 @@ void CMainWindow::createToolbar() connect(extractSphraseWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); wordsExtractionMapper->setMapping(extractSphraseWordsAct, tr(Constants::WK_SPHRASE)); // extract place and region names - QAction *extractPlaceNamesAct = wordsExtractionMenu->addAction("&Extract place and region names..."); - extractPlaceNamesAct->setStatusTip(tr("Extract place and region names")); + QAction *extractPlaceNamesAct = wordsExtractionMenu->addAction("&Extract place names..."); + extractPlaceNamesAct->setStatusTip(tr("Extract place names from primitives")); connect(extractPlaceNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); wordsExtractionMapper->setMapping(extractPlaceNamesAct, tr(Constants::WK_PLACE)); - // extract continent names - QAction *extractContinentNamesAct = wordsExtractionMenu->addAction("&Extract continent names..."); - extractContinentNamesAct->setStatusTip(tr("Extract continent names")); - connect(extractContinentNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractContinentNamesAct, tr(Constants::WK_CONTINENT)); - // extract stable names - QAction *extractStableNamesAct = wordsExtractionMenu->addAction("&Extract stable names..."); - extractStableNamesAct->setStatusTip(tr("Extract stable names")); - connect(extractStableNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractStableNamesAct, tr(Constants::WK_STABLE)); + // Merge options + // ----------------------------- + QAction *mergeSingleFileAct = wordsExtractionMenu->addAction("&Merge worksheet file..."); + mergeSingleFileAct->setStatusTip(tr("Merge worksheet file from local or remote directory")); + connect(mergeSingleFileAct, SIGNAL(triggered()), this, SLOT(mergeSingleFile())); // Windows menu windowMenu = new QMenu(tr("&Windows..."), _ui.toolBar); windowMenu->setIcon(QIcon(Core::Constants::ICON_PILL)); @@ -375,16 +371,6 @@ void CMainWindow::extractWords(QString typeq) builderP.PrimFilter.push_back("region_*.primitive"); builderP.PrimFilter.push_back("indoors_*.primitive"); isSheet = false; - } else if(typeq.toAscii() == Constants::WK_CONTINENT) { - column_name = "placeId"; - builderP.PrimPath = primitives_path; - builderP.PrimFilter.push_back("continent_*.primitive"); - isSheet = false; - } else if(typeq.toAscii() == Constants::WK_STABLE) { - column_name = "placeId"; - builderP.PrimPath = primitives_path; - builderP.PrimFilter.push_back("stable_*.primitive"); - isSheet = false; } if(isSheet) @@ -454,6 +440,58 @@ void CMainWindow::extractBotNames() } } +void CMainWindow::mergeSingleFile() +{ + CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + CSourceDialog *dialog = new CSourceDialog(this); + map methods; + // create items + QListWidgetItem* local_item = new QListWidgetItem(); + local_item->setText("Local directory"); + methods[local_item] = 0; + QListWidgetItem* ftp_item = new QListWidgetItem(); + ftp_item->setText("From a FTP server"); + methods[ftp_item] = 1; + + dialog->setSourceOptions(methods); + dialog->show(); + dialog->exec(); + if(dialog->selected_item == local_item) // Local directory + { + QString file_name; + if (_ui.mdiArea->subWindowList().size() > 0) + { + file_name = QFileDialog::getOpenFileName(this); + } else { + return; + } + + if(QString(editor_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + { + editor_window->activateWindow(); + CEditorWorksheet* current_window = qobject_cast(editor_window); + if(current_window->windowFilePath() == file_name) + return; + if(current_window->compareWorksheetFile(file_name)) + { + current_window->mergeWorksheetFile(file_name); + } else { + QErrorMessage error; + error.showMessage(QString("The file: %1 has different columns from the current file in editor.").arg(file_name)); + error.exec(); + } + } + } else if(dialog->selected_item == ftp_item) { // Ftp directory + CFtpSelection* ftp_dialog = new CFtpSelection(this); + ftp_dialog->show(); + ftp_dialog->exec(); + } else { + return; + } + + +} + void CMainWindow::readSettings() { QSettings *settings = Core::ICore::instance()->settings(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 2ee796bd0..95e4cbaa1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -38,9 +38,11 @@ #include #include #include +#include #include "translation_manager_editor.h" +#include "source_selection.h" #include "ui_translation_manager_main_window.h" #include @@ -86,7 +88,7 @@ private Q_SLOTS: void activeSubWindowChanged(); void setActiveSubWindow(QWidget *window); void updateWindowsList(); - + void mergeSingleFile(); void debug(QString text); // TODO private: void openWorkFile(QString file); From 4c0141184041197b78bdea764b82f5f39aea3bcb Mon Sep 17 00:00:00 2001 From: cemycc Date: Fri, 8 Jul 2011 18:31:59 +0300 Subject: [PATCH 038/215] Added: #1307 Ui deffs for merge source selection and ftp selection --HG-- branch : gsoc2011-translationovqt --- .../TranslationManagerPlugin.cbp | 32 ++++ .../TranslationManagerPlugin.layout | 4 + .../translation_manager/ftp_selection.cpp | 121 ++++++++++++++ .../translation_manager/ftp_selection.h | 46 ++++++ .../translation_manager/ftp_selection.ui | 135 ++++++++++++++++ .../nbproject/Package-Default.bash | 75 +++++++++ .../nbproject/configurations.xml | 147 ++++++++++++++++++ .../nbproject/private/configurations.xml | 41 +++++ .../nbproject/private/private.properties | 0 .../nbproject/private/private.xml | 17 ++ .../nbproject/project.properties | 0 .../translation_manager/nbproject/project.xml | 23 +++ .../translation_manager/source_selection.cpp | 37 +++++ .../translation_manager/source_selection.h | 37 +++++ .../translation_manager/source_selection.ui | 70 +++++++++ .../translation_manager_constants.h | 27 ++++ 16 files changed, 812 insertions(+) create mode 100644 code/TranslationManagerPlugin/TranslationManagerPlugin.cbp create mode 100644 code/TranslationManagerPlugin/TranslationManagerPlugin.layout create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/Package-Default.bash create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/configurations.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/configurations.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.properties create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.properties create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h diff --git a/code/TranslationManagerPlugin/TranslationManagerPlugin.cbp b/code/TranslationManagerPlugin/TranslationManagerPlugin.cbp new file mode 100644 index 000000000..f8c6d49c1 --- /dev/null +++ b/code/TranslationManagerPlugin/TranslationManagerPlugin.cbp @@ -0,0 +1,32 @@ + + + + + + diff --git a/code/TranslationManagerPlugin/TranslationManagerPlugin.layout b/code/TranslationManagerPlugin/TranslationManagerPlugin.layout new file mode 100644 index 000000000..23b67ddb1 --- /dev/null +++ b/code/TranslationManagerPlugin/TranslationManagerPlugin.layout @@ -0,0 +1,4 @@ + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp new file mode 100644 index 000000000..dbc1144a8 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp @@ -0,0 +1,121 @@ + +#include "ftp_selection.h" + +#include +#include +namespace Plugin +{ + CFtpSelection::CFtpSelection(QWidget *parent): QDialog(parent) + { + _ui.setupUi(this); + connect(_ui.connectButton, SIGNAL(clicked()), this, SLOT(ConnectButtonClicked())); + connect(_ui.doneButton, SIGNAL(clicked()), this, SLOT(DoneButtonClicked())); + connect(_ui.cancelButton, SIGNAL(clicked()), this, SLOT(CancelButtonClicked())); + + // file list + connect(_ui.fileList, SIGNAL(itemActivated(QTreeWidgetItem*,int)),this, SLOT(processItem(QTreeWidgetItem*,int))); + _ui.fileList->setEnabled(false); + _ui.fileList->setRootIsDecorated(false); + _ui.fileList->setHeaderLabels(QStringList() << tr("Name") << tr("Size") << tr("Owner") << tr("Group") << tr("Time")); + _ui.fileList->header()->setStretchLastSection(false); + } + + void CFtpSelection::ConnectButtonClicked() + { + conn = new QFtp(this); + connect(conn, SIGNAL(commandFinished(int,bool)), this, SLOT(FtpCommandFinished(int,bool))); + connect(conn, SIGNAL(listInfo(QUrlInfo)), this, SLOT(AddToList(QUrlInfo))); + + QUrl url(_ui.url->text()); + if (!url.isValid() || url.scheme().toLower() != QLatin1String("ftp")) { + conn->connectToHost(_ui.url->text(), 21); + conn->login(); + } else { + conn->connectToHost(url.host(), url.port(21)); + + if (!url.userName().isEmpty()) + conn->login(QUrl::fromPercentEncoding(url.userName().toLatin1()), url.password()); + else + conn->login(); + if (!url.path().isEmpty()) + conn->cd(url.path()); + } + } + + void CFtpSelection::FtpCommandFinished(int, bool error) + { + if (conn->currentCommand() == QFtp::ConnectToHost) + { + if (error) + { + QMessageBox::information(this, tr("FTP"), + tr("Unable to connect to the FTP server " + "at %1. Please check that the host " + "name is correct.") + .arg(_ui.url->text())); + return; + } + + return; + } + + if (conn->currentCommand() == QFtp::Login) + { + conn->list(); + } + + if (conn->currentCommand() == QFtp::List) + { + if (isDirectory.isEmpty()) { + _ui.fileList->addTopLevelItem(new QTreeWidgetItem(QStringList() << tr(""))); + _ui.fileList->setEnabled(false); + } + } + } + + void CFtpSelection::AddToList(const QUrlInfo &urlInfo) + { + QTreeWidgetItem *item = new QTreeWidgetItem; + item->setText(0, urlInfo.name()); + item->setText(1, QString::number(urlInfo.size())); + item->setText(2, urlInfo.owner()); + item->setText(3, urlInfo.group()); + item->setText(4, urlInfo.lastModified().toString("MMM dd yyyy")); + + QPixmap pixmap(urlInfo.isDir() ? ":/images/dir.png" : ":/images/file.png"); + item->setIcon(0, pixmap); + + isDirectory[urlInfo.name()] = urlInfo.isDir(); + _ui.fileList->addTopLevelItem(item); + if (!_ui.fileList->currentItem()) { + _ui.fileList->setCurrentItem(_ui.fileList->topLevelItem(0)); + _ui.fileList->setEnabled(true); + } + } + + void CFtpSelection::processItem(QTreeWidgetItem* item, int) + { + QString name = item->text(0); + if (isDirectory.value(name)) + { + _ui.fileList->clear(); + isDirectory.clear(); + currentPath += '/'; + currentPath += name; + conn->cd(name); + conn->list(); + //TODO: cursor + return; + } + } + + void CFtpSelection::DoneButtonClicked() + { + + } + + void CFtpSelection::CancelButtonClicked() + { + + } +} diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h new file mode 100644 index 000000000..195abfc85 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h @@ -0,0 +1,46 @@ +/* + * File: ftp_selection.h + * Author: cemycc + * + * Created on July 8, 2011, 4:03 PM + */ + +#ifndef FTP_SELECTION_H +#define FTP_SELECTION_H + +#include +#include +#include +#include +#include +#include + +#include "ui_ftp_selection.h" + +using namespace std; + +namespace Plugin { + + class CFtpSelection : public QDialog + { + Q_OBJECT + private: + Ui::FtpSelectionDialog _ui; + QFtp *conn; + QHash isDirectory; + QString currentPath; + private Q_SLOTS: + void processItem(QTreeWidgetItem*,int); + void CancelButtonClicked(); + void ConnectButtonClicked(); + void DoneButtonClicked(); + void FtpCommandFinished(int, bool error); + void AddToList(const QUrlInfo &urlInfo); + public: + CFtpSelection(QWidget* parent = 0); + ~CFtpSelection() {} + }; +} + +#endif /* FTP_SELECTION_H */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui new file mode 100644 index 000000000..c23a7137b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui @@ -0,0 +1,135 @@ + + + FtpSelectionDialog + + + + 0 + 0 + 383 + 547 + + + + Dialog + + + + + 10 + 10 + 371 + 501 + + + + FTP Server informations + + + + + 0 + 130 + 371 + 411 + + + + Content + + + + + 0 + 20 + 141 + 21 + + + + Please select the file + + + + + + 0 + 40 + 361 + 311 + + + + + 1 + + + + + + + + 1 + 29 + 361 + 107 + + + + + + + Ftp server + + + + + + + + + + Connect + + + + + + + + + + + + + + + + + 10 + 500 + 361 + 33 + + + + + + + Done + + + + + + + Cancel + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/Package-Default.bash b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/Package-Default.bash new file mode 100644 index 000000000..d50edf611 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/Package-Default.bash @@ -0,0 +1,75 @@ +#!/bin/bash -x + +# +# Generated - do not edit! +# + +# Macros +TOP=`pwd` +CND_PLATFORM=GNU-Linux-x86 +CND_CONF=Default +CND_DISTDIR=dist +CND_BUILDDIR=build +NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging +TMPDIRNAME=tmp-packaging +OUTPUT_PATH=../../../../../../../build/bin/object_viewer_qt +OUTPUT_BASENAME=object_viewer_qt +PACKAGE_TOP_DIR=translationmanager/ + +# Functions +function checkReturnCode +{ + rc=$? + if [ $rc != 0 ] + then + exit $rc + fi +} +function makeDirectory +# $1 directory path +# $2 permission (optional) +{ + mkdir -p "$1" + checkReturnCode + if [ "$2" != "" ] + then + chmod $2 "$1" + checkReturnCode + fi +} +function copyFileToTmpDir +# $1 from-file path +# $2 to-file path +# $3 permission +{ + cp "$1" "$2" + checkReturnCode + if [ "$3" != "" ] + then + chmod $3 "$2" + checkReturnCode + fi +} + +# Setup +cd "${TOP}" +mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package +rm -rf ${NBTMPDIR} +mkdir -p ${NBTMPDIR} + +# Copy files and create directories and links +cd "${TOP}" +makeDirectory "${NBTMPDIR}/translationmanager/bin" +copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 + + +# Generate tar file +cd "${TOP}" +rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/translationmanager.tar +cd ${NBTMPDIR} +tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/translationmanager.tar * +checkReturnCode + +# Cleanup +cd "${TOP}" +rm -rf ${NBTMPDIR} diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/configurations.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/configurations.xml new file mode 100644 index 000000000..66a3d7f05 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/configurations.xml @@ -0,0 +1,147 @@ + + + + + + + + + + + + + ../../../../../../../build/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ui_translation_manager_settings_page.h + + + + + + + + + + + + + ../../../../../../include/nel/misc/app_context.h + ../../../../../../include/nel/misc/common.h + ../../../../../../include/nel/misc/debug.h + ../../../../../../include/nel/misc/displayer.h + ../../../../../../include/nel/misc/log.h + ../../../../../../include/nel/misc/mem_displayer.h + ../../../../../../include/nel/misc/mutex.h + ../../../../../../include/nel/misc/string_common.h + ../../../../../../include/nel/misc/time_nl.h + ../../../../../../include/nel/misc/types_nl.h + + + + + + + + ../../extension_system/iplugin.h + ../../extension_system/iplugin_manager.h + ../../extension_system/iplugin_spec.h + + + + ../core/core_constants.h + ../core/core_global.h + ../core/icontext.h + ../core/icore.h + ../core/icore_listener.h + ../core/imenu_manager.h + ../core/ioptions_page.h + + + + + editor_worksheet.cpp + editor_worksheet.h + extract_bot_names.cpp + extract_bot_names.h + extract_new_sheet_names.cpp + extract_new_sheet_names.h + source_selection.cpp + source_selection.h + translation_manager_constants.h + translation_manager_editor.h + translation_manager_main_window.cpp + translation_manager_main_window.h + translation_manager_plugin.cpp + translation_manager_plugin.h + translation_manager_settings_page.cpp + translation_manager_settings_page.h + + + ../../../../../../../build/Makefile + + + ^(nbproject)$ + + . + + ../../../../../../../build/Makefile + + + + LOCAL_SOURCES + default + + + + ../../../../../../../build + ${MAKE} -f Makefile ovqt_plugin_translation_manager + ${MAKE} -f Makefile clean + ../../../../../../../build/bin/object_viewer_qt + + + + + + ../../../../../../include + + + + + + + ../../../../../../../build/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager + ../../../../../../include + + + + + + + ../../../../../../../build/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager + ../../../../../../include + + + + + + + ../../../../../../../build/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/configurations.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/configurations.xml new file mode 100644 index 000000000..c4febd942 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/configurations.xml @@ -0,0 +1,41 @@ + + + ../../../../../../../build/Makefile + + + + localhost + 2 + + + + + + + + + + + + + + + + + gdb + + + + "${OUTPUT_PATH}" + + "${OUTPUT_PATH}" + + true + 0 + 0 + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.properties b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.properties new file mode 100644 index 000000000..e69de29bb diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.xml new file mode 100644 index 000000000..c6e5619d5 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.xml @@ -0,0 +1,17 @@ + + + + true + + + 0 + 0 + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.properties b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.properties new file mode 100644 index 000000000..e69de29bb diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.xml new file mode 100644 index 000000000..68607a434 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.xml @@ -0,0 +1,23 @@ + + + org.netbeans.modules.cnd.makeproject + + + translation_manager + + cpp + h + UTF-8 + + + . + + + + Default + 0 + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp new file mode 100644 index 000000000..6bc048b0b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp @@ -0,0 +1,37 @@ + +#include + +#include "source_selection.h" + +namespace Plugin +{ + + +CSourceDialog::CSourceDialog(QWidget *parent): QDialog(parent) +{ + _ui.setupUi(this); + // Set signal and slot for "OK Button" + + connect(_ui.ok_button, SIGNAL(clicked()), this, SLOT(OkButtonClicked())); + // Set signal and slot for "Cancel Button" + connect(_ui.cancel_button, SIGNAL(clicked()), this, SLOT(reject())); + _ui.listWidget->setSortingEnabled(false); +} + +void CSourceDialog::setSourceOptions(map options) +{ + map::iterator it; + + for(it = options.begin(); it != options.end(); ++it) + { + _ui.listWidget->addItem((*it).first); + } +} + +void CSourceDialog::OkButtonClicked() +{ + selected_item = _ui.listWidget->currentItem(); + reject(); +} + +} diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h new file mode 100644 index 000000000..074ad5b86 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h @@ -0,0 +1,37 @@ + + +#ifndef SOURCE_SELECTION_H +#define SOURCE_SELECTION_H + +#include +#include +#include +#include +#include "ui_source_selection.h" +#include + +using namespace std; + +namespace Plugin +{ + class CSourceDialog : public QDialog + { + Q_OBJECT + private: + Ui::SourceSelectionDialog _ui; + + private Q_SLOTS: + + void OkButtonClicked(); + public: + CSourceDialog(QWidget *parent = 0); + ~CSourceDialog(){} + void setSourceOptions(map options); + QListWidgetItem *selected_item; + + }; +} + + +#endif /* SOURCE_SELECTION_H */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.ui new file mode 100644 index 000000000..6e6f822fe --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.ui @@ -0,0 +1,70 @@ + + + SourceSelectionDialog + + + Qt::WindowModal + + + true + + + + 0 + 0 + 396 + 197 + + + + Dialog + + + true + + + + + 10 + 10 + 381 + 181 + + + + Select source for merge operation + + + + + 10 + 30 + 361 + 141 + + + + + + + + + + OK + + + + + + + Cancel + + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h new file mode 100644 index 000000000..b6bdc8315 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h @@ -0,0 +1,27 @@ +/* + * File: translation_manager_constants.h + * Author: cemycc + * + * Created on July 5, 2011, 9:15 PM + */ + +#ifndef TRANSLATION_MANAGER_CONSTANTS_H +#define TRANSLATION_MANAGER_CONSTANTS_H + +namespace Plugin +{ + namespace Constants + { + const char * const WK_BOTNAMES = "bot_names_wk.txt"; + const char * const WK_ITEM = "item_words_wk.txt"; + const char * const WK_CREATURE = "creature_words_wk.txt"; + const char * const WK_SBRICK = "sbrick_words_wk.txt"; + const char * const WK_SPHRASE = "sphrase_words_wk.txt"; + const char * const WK_PLACE = "place_words_wk.txt"; + const char * const WK_CONTINENT = "place_words_wk.txt"; + const char * const WK_STABLE = "place_words_wk.txt"; + } +} + +#endif /* TRANSLATION_MANAGER_CONSTANTS_H */ + From 03e08b730a6f33d8b2ffe4f0af5a4be52977289a Mon Sep 17 00:00:00 2001 From: cemycc Date: Tue, 12 Jul 2011 22:02:05 +0300 Subject: [PATCH 039/215] Changed: #1307 Added implementation of undo/redo framework partial --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_worksheet.cpp | 17 ++++++++-- .../translation_manager/editor_worksheet.h | 33 +++++++++++++++++-- .../translation_manager_editor.h | 6 ++++ .../translation_manager_main_window.cpp | 7 ++-- 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index de20db5ec..5f38d29f1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -95,7 +95,8 @@ void CEditorWorksheet::open(QString filename) table_editor->resizeColumnsToContents(); table_editor->resizeRowsToContents(); // set editor signals - connect(table_editor, SIGNAL(cellChanged(int,int) ), this, SLOT(worksheetEditorChanged(int,int))); + connect(table_editor, SIGNAL(itemChanged(QTableWidgetItem*) ), this, SLOT(worksheetEditorChanged(QTableWidgetItem*))); + connect(table_editor, SIGNAL(itemDoubleClicked(QTableWidgetItem*) ), this, SLOT(worksheetEditorCellEntered(QTableWidgetItem*))); } else { QErrorMessage error; error.showMessage("This file is not a worksheet file."); @@ -233,8 +234,19 @@ void CEditorWorksheet::deleteRow() return; } -void CEditorWorksheet::worksheetEditorChanged(int row, int column) +void CEditorWorksheet::worksheetEditorCellEntered(QTableWidgetItem * item) { + temp_content = item->text(); +} + +void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem * item) +{ + if(temp_content != item->text()) + { + QString i_text = item->text(); + current_stack->push(new CUndoWorksheetCommand(item, temp_content, i_text)); + } + if(!isWindowModified()) setWindowModified(true); } @@ -494,6 +506,7 @@ void CEditorWorksheet::closeEvent(QCloseEvent *event) } } + bool CEditorWorksheet::isBotNamesTable() { bool status = true; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 95ee140d1..3723a6b70 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -31,6 +31,9 @@ #include #include #include +#include +#include + #include "translation_manager_editor.h" #include "extract_new_sheet_names.h" @@ -40,11 +43,12 @@ namespace Plugin { class CEditorWorksheet : public CEditor { Q_OBJECT -private: - QTableWidget* table_editor; +private: + QString temp_content; public: CEditorWorksheet(QMdiArea* parent) : CEditor(parent) {} CEditorWorksheet() : CEditor() {} + QTableWidget* table_editor; void open(QString filename); void save(); void saveAs(QString filename); @@ -57,7 +61,8 @@ public: bool isSheetTable(QString type); void closeEvent(QCloseEvent *event); private Q_SLOTS: - void worksheetEditorChanged(int,int); + void worksheetEditorCellEntered(QTableWidgetItem * item); + void worksheetEditorChanged(QTableWidgetItem * item); void insertRow(); void deleteRow(); private: @@ -65,6 +70,28 @@ private: }; +class CUndoWorksheetCommand : public QUndoCommand +{ +public: + CUndoWorksheetCommand(QTableWidgetItem* item, const QString &ocontent, QString ccontent) : QUndoCommand("Insert characters in cells"), m_item(item), m_ocontent(ocontent), m_ccontent(ccontent) { } + + virtual void redo() + { + + //m_ccontent = m_item->text(); + m_item->setText(m_ccontent); + + } + virtual void undo() + { + m_item->setText(m_ocontent); + } +private: + QTableWidgetItem* m_item; + QString m_ocontent; + QString m_ccontent; }; + +} #endif /* EDITOR_WORKSHEET_H */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h index 7af2b061d..ff947b1d1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h @@ -22,12 +22,14 @@ #include #include #include +#include namespace Plugin { class CEditor : public QMdiSubWindow { Q_OBJECT protected: + QUndoStack* current_stack; QString current_file; int editor_type; public: @@ -42,6 +44,10 @@ public: { return current_file; } + void setUndoStack(QUndoStack* stack) + { + current_stack = stack; + } }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 548aa4b37..6cf9c3f1d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -65,7 +65,9 @@ CMainWindow::CMainWindow(QWidget *parent) initialize_settings["ligo"] = false; readSettings(); createToolbar(); - m_undoStack = new QUndoStack(this); + m_undoStack = new QUndoStack(this); + _ui.toolBar->addAction(m_undoStack->createUndoAction(this)); + _ui.toolBar->addAction(m_undoStack->createRedoAction(this)); } void CMainWindow::createToolbar() @@ -207,7 +209,8 @@ void CMainWindow::open() } if(isWorksheetEditor(file_name)) { - CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); + CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); + new_window->setUndoStack(m_undoStack); new_window->open(file_name); new_window->activateWindow(); } From d035dfcb0534c8ef8f21a9e33a977d575b418a2b Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 13 Jul 2011 01:43:42 +0300 Subject: [PATCH 040/215] Changed: #1307 Minor fix on QMainWindow reported here: http://ryzomcore.etherpad.opennel.org/12 --HG-- branch : gsoc2011-translationovqt --- .../src/plugins/core/core_constants.h | 2 +- .../translation_manager/editor_worksheet.cpp | 3 +- .../translation_manager/editor_worksheet.h | 175 ++++++------ .../translation_manager_main_window.cpp | 267 ++++++++---------- .../translation_manager_main_window.h | 17 +- .../translation_manager_settings_page.cpp | 16 +- 6 files changed, 220 insertions(+), 260 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h index 562947716..ffd21b49a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h @@ -91,7 +91,7 @@ const char * const DATA_PATH_SECTION = "DataPath"; const char * const SEARCH_PATHS = "SearchPaths"; const char * const RECURSIVE_SEARCH_PATHS = "RecursiveSearchPathes"; const char * const LEVELDESIGN_PATH = "LevelDesignPath"; -const char * const PRIMITIVES_PATH = "/home/cemycc/Ryzom/work/ryzom/code/ryzom/common/data_leveldesign/primitives"; +const char * const PRIMITIVES_PATH = "D:/Ryzom/ryzom/code/ryzom/common/data_leveldesign/primitives"; const char * const ASSETS_PATH = "AssetsPath"; const char * const REMAP_EXTENSIONS = "RemapExtensions"; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index 5f38d29f1..6d233dab9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -243,8 +243,7 @@ void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem * item) { if(temp_content != item->text()) { - QString i_text = item->text(); - current_stack->push(new CUndoWorksheetCommand(item, temp_content, i_text)); + current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); } if(!isWindowModified()) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 3723a6b70..f25a786ec 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -1,97 +1,100 @@ -// Translation Manager Plugin - OVQT Plugin -// Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Emanuel Costea -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef EDITOR_WORKSHEET_H -#define EDITOR_WORKSHEET_H - -// Nel includes -#include "nel/misc/types_nl.h" -#include "nel/misc/sheet_id.h" -#include "nel/misc/path.h" -#include "nel/misc/diff_tool.h" -#include "nel/ligo/ligo_config.h" - -// Qt includes -#include -#include -#include -#include -#include -#include -#include - - -#include "translation_manager_editor.h" -#include "extract_new_sheet_names.h" - -namespace Plugin { - -class CEditorWorksheet : public CEditor -{ - Q_OBJECT -private: - QString temp_content; -public: - CEditorWorksheet(QMdiArea* parent) : CEditor(parent) {} - CEditorWorksheet() : CEditor() {} - QTableWidget* table_editor; - void open(QString filename); - void save(); - void saveAs(QString filename); - void activateWindow(); - void mergeWorksheetFile(QString filename); - bool compareWorksheetFile(QString filename); - void extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig); - void extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder); - bool isBotNamesTable(); - bool isSheetTable(QString type); - void closeEvent(QCloseEvent *event); -private Q_SLOTS: - void worksheetEditorCellEntered(QTableWidgetItem * item); - void worksheetEditorChanged(QTableWidgetItem * item); - void insertRow(); - void deleteRow(); -private: - void setCurrentFile(QString filename); - -}; - +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef EDITOR_WORKSHEET_H +#define EDITOR_WORKSHEET_H + +// Nel includes +#include "nel/misc/types_nl.h" +#include "nel/misc/sheet_id.h" +#include "nel/misc/path.h" +#include "nel/misc/diff_tool.h" +#include "nel/ligo/ligo_config.h" + +// Qt includes +#include +#include +#include +#include +#include +#include +#include + + +#include "translation_manager_editor.h" +#include "extract_new_sheet_names.h" + +namespace Plugin { + +class CEditorWorksheet : public CEditor +{ + Q_OBJECT +private: + QString temp_content; +public: + CEditorWorksheet(QMdiArea* parent) : CEditor(parent) {} + CEditorWorksheet() : CEditor() {} + QTableWidget* table_editor; + void open(QString filename); + void save(); + void saveAs(QString filename); + void activateWindow(); + void mergeWorksheetFile(QString filename); + bool compareWorksheetFile(QString filename); + void extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig); + void extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder); + bool isBotNamesTable(); + bool isSheetTable(QString type); + void closeEvent(QCloseEvent *event); +private Q_SLOTS: + void worksheetEditorCellEntered(QTableWidgetItem * item); + void worksheetEditorChanged(QTableWidgetItem * item); + void insertRow(); + void deleteRow(); +private: + void setCurrentFile(QString filename); + +}; + class CUndoWorksheetCommand : public QUndoCommand { public: - CUndoWorksheetCommand(QTableWidgetItem* item, const QString &ocontent, QString ccontent) : QUndoCommand("Insert characters in cells"), m_item(item), m_ocontent(ocontent), m_ccontent(ccontent) { } + CUndoWorksheetCommand(QTableWidget *table, QTableWidgetItem* item, const QString &ocontent, QUndoCommand *parent = 0) : QUndoCommand("Insert characters in cells", parent), m_table(table), m_item(item), m_ocontent(ocontent) + { + m_ccontent = m_item->text(); + } - virtual void redo() + void redo() { - - //m_ccontent = m_item->text(); - m_item->setText(m_ccontent); - + m_item->setText(m_ccontent); } - virtual void undo() - { + void undo() + { m_item->setText(m_ocontent); + } + private: + QTableWidget* m_table; QTableWidgetItem* m_item; - QString m_ocontent; QString m_ccontent; -}; - -} -#endif /* EDITOR_WORKSHEET_H */ - + QString m_ocontent; +}; + +} +#endif /* EDITOR_WORKSHEET_H */ + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 6cf9c3f1d..466b57a38 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -17,7 +17,6 @@ #include "translation_manager_main_window.h" #include "translation_manager_constants.h" -#include "editor_worksheet.h" #include "ftp_selection.h" // Project system includes @@ -63,11 +62,12 @@ CMainWindow::CMainWindow(QWidget *parent) initialize_settings["georges"] = false; initialize_settings["ligo"] = false; + + connect(Core::ICore::instance(), SIGNAL(changeSettings()), this, SLOT(readSettings())); readSettings(); createToolbar(); - m_undoStack = new QUndoStack(this); - _ui.toolBar->addAction(m_undoStack->createUndoAction(this)); - _ui.toolBar->addAction(m_undoStack->createRedoAction(this)); + m_undoStack = new QUndoStack(this); + } void CMainWindow::createToolbar() @@ -132,6 +132,18 @@ void CMainWindow::createToolbar() updateWindowsList(); _ui.toolBar->addAction(windowMenu->menuAction()); connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowsList())); + + // Undo, Redo actions + // ----------------------------- + Core::ICore *core = Core::ICore::instance(); + Core::IMenuManager *menuManager = core->menuManager(); + QAction* undoAction = menuManager->action(Core::Constants::UNDO); + if (undoAction != 0) + _ui.toolBar->addAction(undoAction); + + QAction* redoAction = menuManager->action(Core::Constants::REDO); + if (redoAction != 0) + _ui.toolBar->addAction(redoAction); } void CMainWindow::updateToolbar(QMdiSubWindow *window) @@ -159,7 +171,7 @@ void CMainWindow::setActiveSubWindow(QWidget* window) void CMainWindow::activeSubWindowChanged() { - + //TODO: nothing to be done here atm } void CMainWindow::updateWindowsList() @@ -192,35 +204,26 @@ void CMainWindow::open() QString file_name = QFileDialog::getOpenFileName(this); if(!file_name.isEmpty()) { - list subWindows = convertSubWindowList(_ui.mdiArea->subWindowList()); - list::iterator it = subWindows.begin(); - CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - for(; it != subWindows.end(); ++it) + CEditor *editor = getEditorByWindowFilePath(file_name); + if(editor != NULL) + { + editor->activateWindow(); + return; + } + if(isWorksheetEditor(file_name)) { - QString sw_file = (*it)->subWindowFilePath(); - if(file_name == sw_file) - { - if((*it) != current_window) - { - (*it)->activateWindow(); - } - return; - } - } - if(isWorksheetEditor(file_name)) - { CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); new_window->setUndoStack(m_undoStack); new_window->open(file_name); new_window->activateWindow(); - } + } } } void CMainWindow::openWorkFile(QString file) { - QFileInfo* file_path = new QFileInfo(QString("%1/%2").arg(QString(work_path.c_str())).arg(file)); + QFileInfo* file_path = new QFileInfo(QString("%1/%2").arg(work_path).arg(file)); if(file_path->exists()) { if(isWorksheetEditor(file_path->filePath())) @@ -273,8 +276,8 @@ void CMainWindow::initializeSettings(bool georges = false) { if(georges == true && initialize_settings["georges"] == false) { - CPath::addSearchPath(level_design_path + "/DFN", true, false); - CPath::addSearchPath(level_design_path + "/Game_elem/Creature", true, false); + CPath::addSearchPath(level_design_path.toStdString() + "/DFN", true, false); + CPath::addSearchPath(level_design_path.toStdString() + "/Game_elem/Creature", true, false); initialize_settings["georges"] = true; } @@ -294,54 +297,21 @@ void CMainWindow::extractWords(QString typeq) { if(verifySettings() == true) { - CEditorWorksheet* current_window; - if(_ui.mdiArea->subWindowList().size() > 0) - { - CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - if(QString(editor_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor - { - current_window = qobject_cast(editor_window); - QString file_path = current_window->subWindowFilePath(); - if(!current_window->isSheetTable(typeq)) - { - list subWindows = convertSubWindowList(_ui.mdiArea->subWindowList()); - list::iterator it = subWindows.begin(); - bool finded = false; - - for(; it != subWindows.end(); ++it) - { - current_window = qobject_cast((*it)); - file_path = current_window->subWindowFilePath(); - if(current_window->isSheetTable(typeq)) - { - finded = true; - current_window->activateWindow(); - } - } - if(!finded) - { - openWorkFile(typeq); - if(_ui.mdiArea->subWindowList().size() > 0) - { - current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - QString file_path = current_window->windowFilePath(); - } else { - return; - } - } - } - } - } else { - openWorkFile(typeq); - if(_ui.mdiArea->subWindowList().size() > 0) - { - current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - QString file_path = current_window->windowFilePath(); - } else { - return; - } - - } + CEditorWorksheet* editor_window = getEditorByWorksheetType(typeq); + if(editor_window != NULL) + { + editor_window->activateWindow(); + QString file_path = editor_window->windowFilePath(); + } else { + openWorkFile(typeq); + editor_window = getEditorByWorksheetType(typeq); + if(editor_window != NULL) + { + editor_window->activateWindow(); + QString file_path = editor_window->windowFilePath(); + } else return; + } + QString column_name; // Sheet extraction CSheetWordListBuilder builderS; @@ -351,26 +321,26 @@ void CMainWindow::extractWords(QString typeq) if(typeq.toAscii() == Constants::WK_ITEM) { column_name = "item ID"; builderS.SheetExt = "sitem"; - builderS.SheetPath = level_design_path + "/game_element/sitem"; + builderS.SheetPath = level_design_path.append("/game_element/sitem").toStdString(); isSheet = true; } else if(typeq.toAscii() == Constants::WK_CREATURE) { column_name = "creature ID"; builderS.SheetExt = "creature"; - builderS.SheetPath = level_design_path + "/Game_elem/Creature/fauna"; + builderS.SheetPath = level_design_path.append("/Game_elem/Creature/fauna").toStdString(); isSheet = true; } else if(typeq.toAscii() == Constants::WK_SBRICK) { column_name = "sbrick ID"; builderS.SheetExt = "sbrick"; - builderS.SheetPath = level_design_path + "/game_element/sbrick"; + builderS.SheetPath = level_design_path.append("/game_element/sbrick").toStdString(); isSheet = true; } else if(typeq.toAscii() == Constants::WK_SPHRASE) { column_name = "sphrase ID"; builderS.SheetExt = "sphrase"; - builderS.SheetPath = level_design_path + "/game_element/sphrase"; + builderS.SheetPath = level_design_path.append("/game_element/sphrase").toStdString(); isSheet = true; } else if(typeq.toAscii() == Constants::WK_PLACE) { column_name = "placeId"; - builderP.PrimPath = primitives_path; + builderP.PrimPath = primitives_path.toStdString(); builderP.PrimFilter.push_back("region_*.primitive"); builderP.PrimFilter.push_back("indoors_*.primitive"); isSheet = false; @@ -378,12 +348,12 @@ void CMainWindow::extractWords(QString typeq) if(isSheet) { - current_window->extractWords(current_window->windowFilePath(), column_name, builderS); + editor_window->extractWords(editor_window->windowFilePath(), column_name, builderS); } else { initializeSettings(false); - current_window->extractWords(current_window->windowFilePath(), column_name, builderP); - } - } + editor_window->extractWords(editor_window->windowFilePath(), column_name, builderP); + } + } } @@ -391,55 +361,22 @@ void CMainWindow::extractBotNames() { if(verifySettings() == true) { - CEditorWorksheet* current_window; - if(_ui.mdiArea->subWindowList().size() > 0) - { - CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - if(QString(editor_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor - { - current_window = qobject_cast(editor_window); - QString file_path = current_window->subWindowFilePath(); - if(!current_window->isBotNamesTable()) - { - list subWindows = convertSubWindowList(_ui.mdiArea->subWindowList()); - list::iterator it = subWindows.begin(); - bool finded = false; - - for(; it != subWindows.end(); ++it) - { - current_window = qobject_cast((*it)); - file_path = current_window->subWindowFilePath(); - if(current_window->isBotNamesTable()) - { - finded = true; - current_window->activateWindow(); - } - } - if(!finded) - { - openWorkFile(tr(Constants::WK_BOTNAMES)); - if(_ui.mdiArea->subWindowList().size() > 0) - { - current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - QString file_path = current_window->windowFilePath(); - } else { - return; - } - } - } - } - } else { - openWorkFile(tr(Constants::WK_BOTNAMES)); - if(_ui.mdiArea->subWindowList().size() > 0) - { - current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - QString file_path = current_window->windowFilePath(); - } else { - return; - } - } + CEditorWorksheet* editor_window = getEditorByWorksheetType(NULL); + if(editor_window != NULL) + { + editor_window->activateWindow(); + QString file_path = editor_window->windowFilePath(); + } else { + openWorkFile(Constants::WK_BOTNAMES); + editor_window = getEditorByWorksheetType(NULL); + if(editor_window != NULL) + { + editor_window->activateWindow(); + QString file_path = editor_window->windowFilePath(); + } else return; + } initializeSettings(true); - current_window->extractBotNames(filters, level_design_path, ligoConfig); + editor_window->extractBotNames(convertQStringList(filters), level_design_path.toStdString(), ligoConfig); } } @@ -498,15 +435,17 @@ void CMainWindow::mergeSingleFile() void CMainWindow::readSettings() { QSettings *settings = Core::ICore::instance()->settings(); + // translation manager settings settings->beginGroup("translationmanager"); - filters = convertQStringList(settings->value("filters").toStringList()); /* filters */ - languages = convertQStringList(settings->value("trlanguages").toStringList()); /* languages */ - translation_path = settings->value("translation").toString().toStdString(); - work_path = settings->value("work").toString().toStdString(); + filters = settings->value("filters").toStringList(); + languages = settings->value("trlanguages").toStringList(); + translation_path = settings->value("translation").toString(); + work_path = settings->value("work").toString(); settings->endGroup(); + // core settings settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - level_design_path = settings->value(Core::Constants::LEVELDESIGN_PATH).toString().toStdString(); - primitives_path = QString(Core::Constants::PRIMITIVES_PATH).toStdString(); + level_design_path = settings->value(Core::Constants::LEVELDESIGN_PATH).toString(); + primitives_path = QString(Core::Constants::PRIMITIVES_PATH); //TODO settings->endGroup(); } @@ -521,23 +460,53 @@ bool CMainWindow::verifySettings() { bool count_errors = false; - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup("translationmanager"); - - if(settings->value("filters").toList().count() == 0) + if(level_design_path.isNull() || primitives_path.isNull() || work_path.isNull()) { QErrorMessage error_settings; error_settings.showMessage("Please write all the paths on the settings dialog."); error_settings.exec(); count_errors = true; } - - settings->endGroup(); return !count_errors; } +CEditor *CMainWindow::getEditorByWindowFilePath(const QString &fileName) +{ + Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) + { + CEditor *currentEditor = qobject_cast(subWindow); + if(currentEditor->subWindowFilePath() == fileName) + return currentEditor; + } + return NULL; +} + +CEditorWorksheet *CMainWindow::getEditorByWorksheetType(const QString &type = NULL) +{ + Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) + { + CEditor *currentEditor = qobject_cast(subWindow); + if(QString(currentEditor->widget()->metaObject()->className()) == "QTableWidget") + { + CEditorWorksheet *editor = qobject_cast(currentEditor); + if(!type.isNull()) + if(editor->isSheetTable(type)) + { + return editor; + } + else + if(editor->isBotNamesTable()) + { + return editor; + } + } + } + return NULL; +} + + list CMainWindow::convertQStringList(QStringList listq) { std::list stdlist; @@ -550,20 +519,6 @@ list CMainWindow::convertQStringList(QStringList listq) return stdlist; } -list CMainWindow::convertSubWindowList(QList listq) -{ - list subwindows; - QList::iterator it = listq.begin(); - - for(; it != listq.end(); ++it) - { - CEditor* current_window = qobject_cast((*it)); - subwindows.push_back(current_window); - } - - return subwindows; -} - bool CMainWindow::isWorksheetEditor(QString filename) { STRING_MANAGER::TWorksheet wk_file; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 95e4cbaa1..ffb87b4b8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -45,6 +45,7 @@ #include "source_selection.h" #include "ui_translation_manager_main_window.h" #include +#include "editor_worksheet.h" class QWidget; @@ -72,12 +73,12 @@ private: QSignalMapper *windowMapper; // config map initialize_settings; - list filters; - list languages; - string level_design_path; - string primitives_path; - string translation_path; - string work_path; + QList filters; + QList languages; + QString level_design_path; + QString primitives_path; + QString translation_path; + QString work_path; NLLIGO::CLigoConfig ligoConfig; private Q_SLOTS: void extractBotNames(); @@ -99,7 +100,9 @@ private: void createToolbar(); void initializeSettings(bool georges); list convertQStringList(QStringList listq); - list convertSubWindowList(QList listq); + CEditor* getEditorByWindowFilePath(const QString &fileName); + // Worksheet specific functions + CEditorWorksheet* getEditorByWorksheetType(const QString &type); bool isWorksheetEditor(QString filename); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp index 4870da41b..fbd713ac6 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp @@ -46,17 +46,17 @@ QString CTranslationManagerSettingsPage::id() const QString CTranslationManagerSettingsPage::trName() const { - return tr("Translation Manager page"); + return tr("Translation Manager"); } QString CTranslationManagerSettingsPage::category() const { - return QLatin1String("General"); + return QLatin1String("Translation Manager"); } QString CTranslationManagerSettingsPage::trCategory() const { - return tr("General"); + return tr("Translation Manager"); } QIcon CTranslationManagerSettingsPage::categoryIcon() const @@ -195,12 +195,12 @@ void CTranslationManagerSettingsPage::writeSettings() QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("translationmanager"); - settings->setValue("filters", filters); - settings->setValue("trlanguages", languages); - settings->setValue("translation", translation); - settings->setValue("work", work); + settings->setValue("filters", filters); + settings->setValue("trlanguages", languages); + settings->setValue("translation", translation); + settings->setValue("work", work); settings->endGroup(); - settings->sync(); + settings->sync(); } From 8ad55aae3640a9d30ef509c23e858fe73f345025 Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 13 Jul 2011 10:39:39 +0300 Subject: [PATCH 041/215] Changed: #1307 Fixing a bug with the extraction options --HG-- branch : gsoc2011-translationovqt --- .../src/plugins/translation_manager/editor_worksheet.cpp | 2 +- .../src/plugins/translation_manager/editor_worksheet.h | 4 ++-- .../translation_manager/extract_new_sheet_names.cpp | 1 - .../translation_manager_main_window.cpp | 7 ++++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index 6d233dab9..f34fdb430 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -243,7 +243,7 @@ void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem * item) { if(temp_content != item->text()) { - current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); + //current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); } if(!isWindowModified()) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index f25a786ec..2c8539922 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -80,11 +80,11 @@ public: void redo() { - m_item->setText(m_ccontent); + //m_item->setText(m_ccontent); } void undo() { - m_item->setText(m_ocontent); + //m_item->setText(m_ocontent); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp index f2369fc1c..b9411c74e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp @@ -34,7 +34,6 @@ namespace Plugin { bool CSheetWordListBuilder::buildWordList(std::vector &allWords, string workSheetFileName) { SheetExt= toLower(SheetExt); - nlinfo("aaaa"); // verify the directory is correct if(!CFile::isDirectory(SheetPath)) { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 466b57a38..20d7e57c1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -483,7 +483,7 @@ CEditor *CMainWindow::getEditorByWindowFilePath(const QString &fileName) return NULL; } -CEditorWorksheet *CMainWindow::getEditorByWorksheetType(const QString &type = NULL) +CEditorWorksheet *CMainWindow::getEditorByWorksheetType(const QString &type) { Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) { @@ -491,16 +491,17 @@ CEditorWorksheet *CMainWindow::getEditorByWorksheetType(const QString &type = NU if(QString(currentEditor->widget()->metaObject()->className()) == "QTableWidget") { CEditorWorksheet *editor = qobject_cast(currentEditor); - if(!type.isNull()) + if(type != NULL) { if(editor->isSheetTable(type)) { return editor; } - else + } else { if(editor->isBotNamesTable()) { return editor; } + } } } return NULL; From 6fbe07b6f45bc864d56eeac567c744eb46eb60e9 Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 13 Jul 2011 19:47:15 +0300 Subject: [PATCH 042/215] Changed: #1307 Added Undo Commands for edit, insertand delete items on table and for words extraction --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_worksheet.cpp | 44 ++++-- .../translation_manager/editor_worksheet.h | 130 ++++++++++++++++-- .../translation_manager/source_selection.cpp | 9 +- .../translation_manager/source_selection.h | 27 ++-- .../translation_manager_main_window.cpp | 23 +--- .../translation_manager_main_window.h | 9 +- 6 files changed, 179 insertions(+), 63 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index f34fdb430..5323ded1a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -15,8 +15,6 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -#include "editor_worksheet.h" -#include // Qt includes #include #include @@ -24,8 +22,11 @@ #include #include +// Project includes +#include "editor_worksheet.h" #include "extract_bot_names.h" #include "translation_manager_constants.h" +#include using namespace std; @@ -206,12 +207,7 @@ void CEditorWorksheet::saveAs(QString filename) void CEditorWorksheet::insertRow() { int last_row = table_editor->rowCount(); - table_editor->setRowCount(last_row + 1); - for(int j = 0; j < table_editor->columnCount(); j++) - { - QTableWidgetItem* item = new QTableWidgetItem(); - table_editor->setItem(last_row, j, item); - } + current_stack->push(new CUndoWorksheetNewCommand(table_editor, last_row)); } void CEditorWorksheet::deleteRow() @@ -223,10 +219,9 @@ void CEditorWorksheet::deleteRow() msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); msgBox.setDefaultButton(QMessageBox::No); int ret = msgBox.exec(); - if(ret == QMessageBox::Yes) { - table_editor->removeRow(selected_row); + current_stack->push(new CUndoWorksheetDeleteCommand(table_editor, selected_row)); } table_editor->clearFocus(); @@ -237,6 +232,7 @@ void CEditorWorksheet::deleteRow() void CEditorWorksheet::worksheetEditorCellEntered(QTableWidgetItem * item) { temp_content = item->text(); + current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); } void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem * item) @@ -250,9 +246,12 @@ void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem * item) setWindowModified(true); } + void CEditorWorksheet::extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig) { bool modified = false; + QList new_items; + ExtractBotNames ebn; ebn.setRequiredSettings(filters, level_design_path); ebn.extractBotNamesFromPrimitives(ligoConfig); @@ -271,7 +270,7 @@ void CEditorWorksheet::extractBotNames(list filters, string level_design QTableWidgetItem *bot_name_row = new QTableWidgetItem(); bot_name_row->setText(tr(it->first.c_str())); bot_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 0, bot_name_row); + table_editor ->setItem(currentRow, 0, bot_name_row); QTableWidgetItem *translation_name_row = new QTableWidgetItem(); translation_name_row->setBackgroundColor(QColor("#F75D59")); translation_name_row->setText(tr(it->first.c_str())); @@ -281,6 +280,12 @@ void CEditorWorksheet::extractBotNames(list filters, string level_design sheet_name_row->setBackgroundColor(QColor("#F75D59")); table_editor ->setItem(currentRow, 2, sheet_name_row); if(!modified) modified = true; + CTableWidgetItemStore bot_name_row_s(bot_name_row, currentRow, 0); + new_items.push_back(bot_name_row_s); + CTableWidgetItemStore translation_name_row_s(translation_name_row, currentRow, 1); + new_items.push_back(translation_name_row_s); + CTableWidgetItemStore sheet_name_row_s(sheet_name_row, currentRow, 2); + new_items.push_back(sheet_name_row_s); } } ebn.cleanSimpleNames(); @@ -310,10 +315,18 @@ void CEditorWorksheet::extractBotNames(list filters, string level_design sheet_name_row->setBackgroundColor(QColor("#F75D59")); table_editor ->setItem(currentRow, 2, sheet_name_row); if(!modified) modified = true; + CTableWidgetItemStore bot_name_row_s(bot_name_row, currentRow, 0); + new_items.push_back(bot_name_row_s); + CTableWidgetItemStore translation_name_row_s(translation_name_row, currentRow, 1); + new_items.push_back(translation_name_row_s); + CTableWidgetItemStore sheet_name_row_s(sheet_name_row, currentRow, 2); + new_items.push_back(sheet_name_row_s); } } ebn.cleanGenericNames(); - } + } + + current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); if(modified) { setWindowModified(true); @@ -355,6 +368,7 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis return; } bool modified = false; + QList new_items; for(i = 0; i < allWords.size(); i++) { string keyName = allWords[i]; @@ -384,8 +398,14 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis name_row->setBackgroundColor(QColor("#F75D59")); table_editor ->setItem(currentRow, nPos, name_row); if(!modified) modified = true; + CTableWidgetItemStore key_name_row_s(key_name_row, currentRow, knPos); + new_items.push_back(key_name_row_s); + CTableWidgetItemStore name_row_s(name_row, currentRow, nPos); + new_items.push_back(name_row_s); + } } + current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); if(modified) { setWindowModified(true); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 2c8539922..983dba4bc 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -32,9 +32,9 @@ #include #include #include -#include - +#include +// Project includes #include "translation_manager_editor.h" #include "extract_new_sheet_names.h" @@ -70,29 +70,141 @@ private: }; +struct CTableWidgetItemStore +{ +public: + CTableWidgetItemStore(QTableWidgetItem *item, int row, int column) : + m_item(item), + m_row(row), + m_column(column) { } + QTableWidgetItem *m_item; + int m_row; + int m_column; +}; + class CUndoWorksheetCommand : public QUndoCommand { public: CUndoWorksheetCommand(QTableWidget *table, QTableWidgetItem* item, const QString &ocontent, QUndoCommand *parent = 0) : QUndoCommand("Insert characters in cells", parent), m_table(table), m_item(item), m_ocontent(ocontent) { - m_ccontent = m_item->text(); + m_ccontent = m_ocontent; } void redo() { - //m_item->setText(m_ccontent); + if(m_item->text() == m_ocontent) + { + m_item->setText(m_ccontent); + } } void undo() { - //m_item->setText(m_ocontent); - - } - + if(m_item->text() != m_ocontent) + { + m_ccontent = m_item->text(); + } + m_item->setText(m_ocontent); + } private: QTableWidget* m_table; QTableWidgetItem* m_item; - QString m_ccontent; QString m_ocontent; + QString m_ccontent; +}; + +class CUndoWorksheetNewCommand : public QUndoCommand +{ +public: + CUndoWorksheetNewCommand(QTableWidget *table, int rowID, QUndoCommand *parent = 0) : QUndoCommand("Insert a new row", parent), m_table(table), m_rowID(rowID) + { } + + void redo() + { + m_table->setRowCount(m_rowID + 1); + for(int j = 0; j < m_table->columnCount(); j++) + { + QTableWidgetItem* item = new QTableWidgetItem(); + m_table->setItem(m_rowID, j, item); + } + } + + void undo() + { + m_table->removeRow(m_rowID); + } +private: + QTableWidget* m_table; + int m_rowID; + +}; + +class CUndoWorksheetExtraction : public QUndoCommand +{ +public: + CUndoWorksheetExtraction(QList items, QTableWidget *table, QUndoCommand *parent = 0) : QUndoCommand("Word extraction", parent), + m_items(items), + m_table(table) + { } + + void redo() + { + Q_FOREACH(CTableWidgetItemStore is, m_items) + { + m_table->setItem(is.m_row, is.m_column, is.m_item); + } + + } + + void undo() + { + Q_FOREACH(CTableWidgetItemStore is, m_items) + { + m_table->setItem(is.m_row, is.m_column, is.m_item); + m_table->takeItem(is.m_row, is.m_column); + } + + } + +private: + QList m_items; + QTableWidget* m_table; +}; + +class CUndoWorksheetDeleteCommand : public QUndoCommand +{ +public: + CUndoWorksheetDeleteCommand(QTableWidget *table, int rowID, QUndoCommand *parent = 0) : QUndoCommand("Delete row", parent), m_table(table), m_rowID(rowID) + { } + + void redo() + { + for(int i = 0; i < m_table->columnCount(); i++) + { + QTableWidgetItem* item = new QTableWidgetItem(); + QTableWidgetItem* table_item = m_table->item(m_rowID, i); + item->setText(table_item->text()); + m_deletedItems.push_back(item); + } + m_table->removeRow(m_rowID); + } + + void undo() + { + int lastRow = m_table->rowCount(); + m_table->setRowCount(m_table->rowCount() + 1); + int i = 0; + Q_FOREACH(QTableWidgetItem* item, m_deletedItems) + { + m_table->setItem(lastRow, i, item); + i++; + } + m_deletedItems.clear(); + } + +private: + QList m_deletedItems; + QTableWidget* m_table; + int m_rowID; }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp index 6bc048b0b..cf38ed589 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp @@ -10,12 +10,11 @@ namespace Plugin CSourceDialog::CSourceDialog(QWidget *parent): QDialog(parent) { _ui.setupUi(this); - // Set signal and slot for "OK Button" - + // Set signal and slot for "OK Button" connect(_ui.ok_button, SIGNAL(clicked()), this, SLOT(OkButtonClicked())); - // Set signal and slot for "Cancel Button" - connect(_ui.cancel_button, SIGNAL(clicked()), this, SLOT(reject())); - _ui.listWidget->setSortingEnabled(false); + // Set signal and slot for "Cancel Button" + connect(_ui.cancel_button, SIGNAL(clicked()), this, SLOT(reject())); + _ui.listWidget->setSortingEnabled(false); } void CSourceDialog::setSourceOptions(map options) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h index 074ad5b86..f15a8778a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h @@ -14,22 +14,21 @@ using namespace std; namespace Plugin { - class CSourceDialog : public QDialog - { - Q_OBJECT - private: - Ui::SourceSelectionDialog _ui; - - private Q_SLOTS: - void OkButtonClicked(); - public: - CSourceDialog(QWidget *parent = 0); - ~CSourceDialog(){} - void setSourceOptions(map options); - QListWidgetItem *selected_item; +class CSourceDialog : public QDialog +{ + Q_OBJECT +private: + Ui::SourceSelectionDialog _ui; + QListWidgetItem *selected_item; +private Q_SLOTS: + void OkButtonClicked(); +public: + CSourceDialog(QWidget *parent = 0); + ~CSourceDialog(){} + void setSourceOptions(map options); +}; - }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 20d7e57c1..bb4fa27d1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -15,29 +15,20 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -#include "translation_manager_main_window.h" -#include "translation_manager_constants.h" -#include "ftp_selection.h" - // Project system includes #include "../core/icore.h" #include "../core/core_constants.h" #include "../core/imenu_manager.h" #include "../../extension_system/iplugin_spec.h" + // Qt includes #include #include -#include -#include #include #include #include #include #include -#include -#include -#include -#include #include #include #include @@ -46,6 +37,11 @@ #include #include +// Plugin includes +#include "translation_manager_main_window.h" +#include "translation_manager_constants.h" +#include "ftp_selection.h" + namespace Plugin { @@ -449,13 +445,6 @@ void CMainWindow::readSettings() settings->endGroup(); } -void CMainWindow::debug(QString text) -{ - QErrorMessage error_settings; - error_settings.showMessage(text); - error_settings.exec(); -} - bool CMainWindow::verifySettings() { bool count_errors = false; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index ffb87b4b8..63a8934d7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -19,7 +19,7 @@ #ifndef MAIN_WINDOW_H #define MAIN_WINDOW_H -// Project includes +// Project system includes #include "../core/icore_listener.h" // Nel includes @@ -33,14 +33,13 @@ #include #include #include -#include #include #include #include #include #include - +// Plugin includes #include "translation_manager_editor.h" #include "source_selection.h" #include "ui_translation_manager_main_window.h" @@ -62,8 +61,7 @@ public: CMainWindow(QWidget *parent = 0); virtual ~CMainWindow() {} QUndoStack *m_undoStack; -private: - +private: Ui::CMainWindow _ui; // actions QAction *openAct; @@ -90,7 +88,6 @@ private Q_SLOTS: void setActiveSubWindow(QWidget *window); void updateWindowsList(); void mergeSingleFile(); - void debug(QString text); // TODO private: void openWorkFile(QString file); void updateToolbar(QMdiSubWindow *window); From 10d5831ac4ba541ef75e5be99398187899df4685 Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 13 Jul 2011 22:34:00 +0300 Subject: [PATCH 043/215] Changed: #1307 I have clean my branch, removing temp files --HG-- branch : gsoc2011-translationovqt --- .../TranslationManagerPlugin.cbp | 32 ---- .../TranslationManagerPlugin.layout | 4 - .../nbproject/Package-Default.bash | 75 --------- .../nbproject/configurations.xml | 147 ------------------ .../nbproject/private/configurations.xml | 41 ----- .../nbproject/private/private.properties | 0 .../nbproject/private/private.xml | 17 -- .../nbproject/project.properties | 0 .../translation_manager/nbproject/project.xml | 23 --- 9 files changed, 339 deletions(-) delete mode 100644 code/TranslationManagerPlugin/TranslationManagerPlugin.cbp delete mode 100644 code/TranslationManagerPlugin/TranslationManagerPlugin.layout delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/Package-Default.bash delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/configurations.xml delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/configurations.xml delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.properties delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.xml delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.properties delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.xml diff --git a/code/TranslationManagerPlugin/TranslationManagerPlugin.cbp b/code/TranslationManagerPlugin/TranslationManagerPlugin.cbp deleted file mode 100644 index f8c6d49c1..000000000 --- a/code/TranslationManagerPlugin/TranslationManagerPlugin.cbp +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - diff --git a/code/TranslationManagerPlugin/TranslationManagerPlugin.layout b/code/TranslationManagerPlugin/TranslationManagerPlugin.layout deleted file mode 100644 index 23b67ddb1..000000000 --- a/code/TranslationManagerPlugin/TranslationManagerPlugin.layout +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/Package-Default.bash b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/Package-Default.bash deleted file mode 100644 index d50edf611..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/Package-Default.bash +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -x - -# -# Generated - do not edit! -# - -# Macros -TOP=`pwd` -CND_PLATFORM=GNU-Linux-x86 -CND_CONF=Default -CND_DISTDIR=dist -CND_BUILDDIR=build -NBTMPDIR=${CND_BUILDDIR}/${CND_CONF}/${CND_PLATFORM}/tmp-packaging -TMPDIRNAME=tmp-packaging -OUTPUT_PATH=../../../../../../../build/bin/object_viewer_qt -OUTPUT_BASENAME=object_viewer_qt -PACKAGE_TOP_DIR=translationmanager/ - -# Functions -function checkReturnCode -{ - rc=$? - if [ $rc != 0 ] - then - exit $rc - fi -} -function makeDirectory -# $1 directory path -# $2 permission (optional) -{ - mkdir -p "$1" - checkReturnCode - if [ "$2" != "" ] - then - chmod $2 "$1" - checkReturnCode - fi -} -function copyFileToTmpDir -# $1 from-file path -# $2 to-file path -# $3 permission -{ - cp "$1" "$2" - checkReturnCode - if [ "$3" != "" ] - then - chmod $3 "$2" - checkReturnCode - fi -} - -# Setup -cd "${TOP}" -mkdir -p ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package -rm -rf ${NBTMPDIR} -mkdir -p ${NBTMPDIR} - -# Copy files and create directories and links -cd "${TOP}" -makeDirectory "${NBTMPDIR}/translationmanager/bin" -copyFileToTmpDir "${OUTPUT_PATH}" "${NBTMPDIR}/${PACKAGE_TOP_DIR}bin/${OUTPUT_BASENAME}" 0755 - - -# Generate tar file -cd "${TOP}" -rm -f ${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/translationmanager.tar -cd ${NBTMPDIR} -tar -vcf ../../../../${CND_DISTDIR}/${CND_CONF}/${CND_PLATFORM}/package/translationmanager.tar * -checkReturnCode - -# Cleanup -cd "${TOP}" -rm -rf ${NBTMPDIR} diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/configurations.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/configurations.xml deleted file mode 100644 index 66a3d7f05..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/configurations.xml +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - - - - - - - - ../../../../../../../build/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ui_translation_manager_settings_page.h - - - - - - - - - - - - - ../../../../../../include/nel/misc/app_context.h - ../../../../../../include/nel/misc/common.h - ../../../../../../include/nel/misc/debug.h - ../../../../../../include/nel/misc/displayer.h - ../../../../../../include/nel/misc/log.h - ../../../../../../include/nel/misc/mem_displayer.h - ../../../../../../include/nel/misc/mutex.h - ../../../../../../include/nel/misc/string_common.h - ../../../../../../include/nel/misc/time_nl.h - ../../../../../../include/nel/misc/types_nl.h - - - - - - - - ../../extension_system/iplugin.h - ../../extension_system/iplugin_manager.h - ../../extension_system/iplugin_spec.h - - - - ../core/core_constants.h - ../core/core_global.h - ../core/icontext.h - ../core/icore.h - ../core/icore_listener.h - ../core/imenu_manager.h - ../core/ioptions_page.h - - - - - editor_worksheet.cpp - editor_worksheet.h - extract_bot_names.cpp - extract_bot_names.h - extract_new_sheet_names.cpp - extract_new_sheet_names.h - source_selection.cpp - source_selection.h - translation_manager_constants.h - translation_manager_editor.h - translation_manager_main_window.cpp - translation_manager_main_window.h - translation_manager_plugin.cpp - translation_manager_plugin.h - translation_manager_settings_page.cpp - translation_manager_settings_page.h - - - ../../../../../../../build/Makefile - - - ^(nbproject)$ - - . - - ../../../../../../../build/Makefile - - - - LOCAL_SOURCES - default - - - - ../../../../../../../build - ${MAKE} -f Makefile ovqt_plugin_translation_manager - ${MAKE} -f Makefile clean - ../../../../../../../build/bin/object_viewer_qt - - - - - - ../../../../../../include - - - - - - - ../../../../../../../build/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager - ../../../../../../include - - - - - - - ../../../../../../../build/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager - ../../../../../../include - - - - - - - ../../../../../../../build/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager - - - - - - diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/configurations.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/configurations.xml deleted file mode 100644 index c4febd942..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/configurations.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - ../../../../../../../build/Makefile - - - - localhost - 2 - - - - - - - - - - - - - - - - - gdb - - - - "${OUTPUT_PATH}" - - "${OUTPUT_PATH}" - - true - 0 - 0 - - - - - - diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.properties b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.properties deleted file mode 100644 index e69de29bb..000000000 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.xml deleted file mode 100644 index c6e5619d5..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/private/private.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - true - - - 0 - 0 - - - - - - - - - diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.properties b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.properties deleted file mode 100644 index e69de29bb..000000000 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.xml deleted file mode 100644 index 68607a434..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/nbproject/project.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - org.netbeans.modules.cnd.makeproject - - - translation_manager - - cpp - h - UTF-8 - - - . - - - - Default - 0 - - - - - From cfa72440745c01d20615058d49aba55b296dbc08 Mon Sep 17 00:00:00 2001 From: cemycc Date: Fri, 15 Jul 2011 00:38:16 +0300 Subject: [PATCH 044/215] Added #1307 Full support for FTP merge options and icons for FTP window. This is the last commit for the first milestone. --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/CMakeLists.txt | 2 + .../translation_manager/ftp_selection.cpp | 90 +++++++++++++++--- .../translation_manager/ftp_selection.h | 7 +- .../translation_manager/ftp_selection.qrc | 7 ++ .../translation_manager/ftp_selection.ui | 84 ++++++++-------- .../translation_manager/images/cdtoparent.png | Bin 0 -> 139 bytes .../translation_manager/images/dir.png | Bin 0 -> 154 bytes .../translation_manager/images/file.png | Bin 0 -> 129 bytes .../translation_manager/source_selection.h | 4 +- .../translation_manager_main_window.cpp | 73 +++++++++----- .../translation_manager_main_window.h | 2 +- 11 files changed, 188 insertions(+), 81 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.qrc create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/cdtoparent.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/dir.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/file.png diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt index 59f11c4f8..7e7da7e99 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -24,6 +24,8 @@ SET(OVQT_PLUG_TRANSLATION_MANAGER_UIS translation_manager_settings_page.ui source_selection.ui ftp_selection.ui) +SET(OVQT_PLUG_TRANSLATION_MANAGER_RCS ftp_selection.qrc) + SET(QT_USE_QTGUI TRUE) SET(QT_USE_QTOPENGL TRUE) SET(QT_USE_QTNETWORK TRUE) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp index dbc1144a8..68abd456f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp @@ -10,14 +10,21 @@ namespace Plugin _ui.setupUi(this); connect(_ui.connectButton, SIGNAL(clicked()), this, SLOT(ConnectButtonClicked())); connect(_ui.doneButton, SIGNAL(clicked()), this, SLOT(DoneButtonClicked())); - connect(_ui.cancelButton, SIGNAL(clicked()), this, SLOT(CancelButtonClicked())); + connect(_ui.cdToParrent, SIGNAL(clicked()), this, SLOT(cdToParent())); + connect(_ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); - // file list + // file list connect(_ui.fileList, SIGNAL(itemActivated(QTreeWidgetItem*,int)),this, SLOT(processItem(QTreeWidgetItem*,int))); _ui.fileList->setEnabled(false); _ui.fileList->setRootIsDecorated(false); _ui.fileList->setHeaderLabels(QStringList() << tr("Name") << tr("Size") << tr("Owner") << tr("Group") << tr("Time")); _ui.fileList->header()->setStretchLastSection(false); + + // buttons + _ui.cdToParrent->setEnabled(false); + _ui.doneButton->setEnabled(false); + + status = false; } void CFtpSelection::ConnectButtonClicked() @@ -25,7 +32,9 @@ namespace Plugin conn = new QFtp(this); connect(conn, SIGNAL(commandFinished(int,bool)), this, SLOT(FtpCommandFinished(int,bool))); connect(conn, SIGNAL(listInfo(QUrlInfo)), this, SLOT(AddToList(QUrlInfo))); - + #ifndef QT_NO_CURSOR + setCursor(Qt::WaitCursor); + #endif QUrl url(_ui.url->text()); if (!url.isValid() || url.scheme().toLower() != QLatin1String("ftp")) { conn->connectToHost(_ui.url->text(), 21); @@ -44,6 +53,9 @@ namespace Plugin void CFtpSelection::FtpCommandFinished(int, bool error) { + #ifndef QT_NO_CURSOR + setCursor(Qt::ArrowCursor); + #endif if (conn->currentCommand() == QFtp::ConnectToHost) { if (error) @@ -64,6 +76,20 @@ namespace Plugin conn->list(); } + if (conn->currentCommand() == QFtp::Get) + { + if(error) + { + status = false; + file->close(); + file->remove(); + } else { + file->close(); + status = true; + } + _ui.cancelButton->setEnabled(true); + } + if (conn->currentCommand() == QFtp::List) { if (isDirectory.isEmpty()) { @@ -82,7 +108,7 @@ namespace Plugin item->setText(3, urlInfo.group()); item->setText(4, urlInfo.lastModified().toString("MMM dd yyyy")); - QPixmap pixmap(urlInfo.isDir() ? ":/images/dir.png" : ":/images/file.png"); + QPixmap pixmap(urlInfo.isDir() ? ":/translationManager/images/dir.png" : ":/translationManager/images/file.png"); item->setIcon(0, pixmap); isDirectory[urlInfo.name()] = urlInfo.isDir(); @@ -104,18 +130,58 @@ namespace Plugin currentPath += name; conn->cd(name); conn->list(); - //TODO: cursor + #ifndef QT_NO_CURSOR + setCursor(Qt::WaitCursor); + #endif return; - } + } + _ui.doneButton->setEnabled(true); } - + + void CFtpSelection::cdToParent() + { + #ifndef QT_NO_CURSOR + setCursor(Qt::WaitCursor); + #endif + _ui.fileList->clear(); + isDirectory.clear(); + currentPath = currentPath.left(currentPath.lastIndexOf('/')); + if (currentPath.isEmpty()) { + _ui.cdToParrent->setEnabled(false); + conn->cd("/"); + } else { + conn->cd(currentPath); + } + conn->list(); + } + void CFtpSelection::DoneButtonClicked() { - + QString fileName = _ui.fileList->currentItem()->text(0); + + if (QFile::exists(fileName)) { + QMessageBox::information(this, tr("FTP"), + tr("There already exists a file called %1 in " + "the current directory.") + .arg(fileName)); + return; + } + + file = new QFile(fileName); + #ifndef QT_NO_CURSOR + setCursor(Qt::WaitCursor); + #endif + if (!file->open(QIODevice::WriteOnly)) { + QMessageBox::information(this, tr("FTP"), + tr("Unable to save the file %1: %2.") + .arg(fileName).arg(file->errorString())); + delete file; + return; + } + _ui.cancelButton->setEnabled(false); + conn->get(_ui.fileList->currentItem()->text(0), file); + + reject(); } - void CFtpSelection::CancelButtonClicked() - { - - } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h index 195abfc85..64ea136c4 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h @@ -9,11 +9,12 @@ #define FTP_SELECTION_H #include +#include #include #include #include +#include #include -#include #include "ui_ftp_selection.h" @@ -30,13 +31,15 @@ namespace Plugin { QHash isDirectory; QString currentPath; private Q_SLOTS: + void cdToParent(); void processItem(QTreeWidgetItem*,int); - void CancelButtonClicked(); void ConnectButtonClicked(); void DoneButtonClicked(); void FtpCommandFinished(int, bool error); void AddToList(const QUrlInfo &urlInfo); public: + bool status; + QFile *file; CFtpSelection(QWidget* parent = 0); ~CFtpSelection() {} }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.qrc b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.qrc new file mode 100644 index 000000000..bd54366ed --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.qrc @@ -0,0 +1,7 @@ + + + images/cdtoparent.png + images/dir.png + images/file.png + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui index c23a7137b..b8b0bf447 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui @@ -6,8 +6,8 @@ 0 0 - 383 - 547 + 388 + 560 @@ -19,7 +19,7 @@ 10 10 371 - 501 + 541 @@ -28,10 +28,10 @@ - 0 + 10 130 - 371 - 411 + 351 + 361 @@ -40,7 +40,7 @@ - 0 + 10 20 141 21 @@ -53,9 +53,9 @@ - 0 + 10 40 - 361 + 331 311 @@ -69,10 +69,10 @@ - 1 + 11 29 - 361 - 107 + 351 + 101 @@ -98,38 +98,44 @@ + + + :/translationManager/images/cdtoparent.png:/translationManager/images/cdtoparent.png + + + + + + + + + 0 + 500 + 371 + 33 + + + + + + + Done + + + + + + + Cancel + - - - - 10 - 500 - 361 - 33 - - - - - - - Done - - - - - - - Cancel - - - - - - + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/cdtoparent.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/cdtoparent.png new file mode 100644 index 0000000000000000000000000000000000000000..24b618082900a68c531862777d8b95d6e55d4e7f GIT binary patch literal 139 zcmeAS@N?(olHy`uVBq!ia0vp^{2(?58;~rMGjRk`oCO|{#X#BvjNMLV+W{H2o-U3d z8t0P}92)*d7z94}|Npn+a literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/dir.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/dir.png new file mode 100644 index 0000000000000000000000000000000000000000..0ce5ae75fcfd96151c964bf48165b31556295312 GIT binary patch literal 154 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#X#BvjNMLV+W{Fqo-U3d z7QM*{5+(`H&dmIO(a>|&`8k%K|NpON6PcyqT#)Hz#Tve vk7T)V%?=a~xXSt@VFzQthxtA}mJAF%>wkJ2U`u`rG?&5C)z4*}Q$iB}ZY?ls literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/file.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/images/file.png new file mode 100644 index 0000000000000000000000000000000000000000..be6c53089ab9e9eba8477b56acbf44df81220934 GIT binary patch literal 129 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6I14-?i-EKU7`vU!wgWPZJzX3_ zG|nd{D6qWxpX_A9`Xa%8l~4}XKV|KMAqN options); + QListWidgetItem *selected_item; }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index bb4fa27d1..52e423156 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -380,7 +380,26 @@ void CMainWindow::mergeSingleFile() { CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); CSourceDialog *dialog = new CSourceDialog(this); + CFtpSelection* ftp_dialog; map methods; + QString file_name; + + if (_ui.mdiArea->subWindowList().size() == 0) + { + QErrorMessage error; + error.showMessage(QString("Open a work file in editor for merge operation.")); + error.exec(); + return; + } + + if(QString(editor_window->widget()->metaObject()->className()) != "QTableWidget") // Sheet Editor + { + QErrorMessage error; + error.showMessage(QString("Please open or activate the window with a sheet file.")); + error.exec(); + return; + } + // create items QListWidgetItem* local_item = new QListWidgetItem(); local_item->setText("Local directory"); @@ -392,40 +411,44 @@ void CMainWindow::mergeSingleFile() dialog->setSourceOptions(methods); dialog->show(); dialog->exec(); + // get the file for merge if(dialog->selected_item == local_item) // Local directory { - QString file_name; - if (_ui.mdiArea->subWindowList().size() > 0) - { - file_name = QFileDialog::getOpenFileName(this); - } else { - return; - } - - if(QString(editor_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor - { - editor_window->activateWindow(); - CEditorWorksheet* current_window = qobject_cast(editor_window); - if(current_window->windowFilePath() == file_name) - return; - if(current_window->compareWorksheetFile(file_name)) - { - current_window->mergeWorksheetFile(file_name); - } else { - QErrorMessage error; - error.showMessage(QString("The file: %1 has different columns from the current file in editor.").arg(file_name)); - error.exec(); - } - } + file_name = QFileDialog::getOpenFileName(this); } else if(dialog->selected_item == ftp_item) { // Ftp directory CFtpSelection* ftp_dialog = new CFtpSelection(this); ftp_dialog->show(); ftp_dialog->exec(); + if(ftp_dialog->status == true) + { + file_name = ftp_dialog->file->fileName(); + } } else { return; } - - + + editor_window->activateWindow(); + CEditorWorksheet* current_window = qobject_cast(editor_window); + if(current_window->windowFilePath() == file_name) + return; + if(current_window->compareWorksheetFile(file_name)) + { + current_window->mergeWorksheetFile(file_name); + } else { + QErrorMessage error; + error.showMessage(QString("The file: %1 has different columns from the current file in editor.").arg(file_name)); + error.exec(); + } + if(dialog->selected_item == ftp_item) + { + if(!ftp_dialog->file->remove()) + { + QErrorMessage error; + error.showMessage(QString("Please remove the file from ftp server manually. The file is located on the same directory with OVQT application.")); + error.exec(); + } + + } } void CMainWindow::readSettings() diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 63a8934d7..bd21f698d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -70,7 +70,7 @@ private: QMenu *windowMenu; QSignalMapper *windowMapper; // config - map initialize_settings; + QMap initialize_settings; QList filters; QList languages; QString level_design_path; From 875d46f9b5d0bf435bc560152c1d561c3d689c25 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 18 Jul 2011 19:37:59 +0200 Subject: [PATCH 045/215] Changed: #1304: Implementation of the cbClientGroupAbandonMission for the guilds --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.cpp | 17 +++ .../guild_manager/guild.h | 1 + .../mission_client_callbacks.cpp | 127 +++++++++++++----- 3 files changed, 112 insertions(+), 33 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp index 158508202..3a05ef309 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp @@ -35,6 +35,7 @@ #include "primitives_parser.h" #include "modules/shard_unifier_client.h" #include "mission_manager/mission_manager.h" +#include "phrase_manager/phrase_utilities_functions.h" /// todo guild remove entity id translator #include "nel/misc/eid_translator.h" @@ -745,6 +746,22 @@ void CGuild::updateMissionHistories(TAIAlias missionAlias, uint32 result) } } +//---------------------------------------------------------------------------- +void CGuild::sendDynamicMessageToMembers(const string &msgName, const TVectorParamCheck ¶ms, const set &excluded) const +{ + for ( std::map::const_iterator it = getMembersBegin(); + it != getMembersEnd();++it ) + { + CCharacter * user = PlayerManager.getChar( it->first ); + + if ( excluded.find(it->first) == excluded.end()) + { + const uint32 stringId = STRING_MANAGER::sendStringToClient(TheDataset.getDataSetRow(it->first), msgName, params ); + PHRASE_UTILITIES::sendDynamicSystemMessage(TheDataset.getDataSetRow(it->first), stringId); + } + } +} + //---------------------------------------------------------------------------- bool CGuild::processMissionEvent( CMissionEvent & event, TAIAlias alias) { diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index 61dc97dd2..64378b717 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -200,6 +200,7 @@ public: bool processGuildMissionStepEvent(std::list< CMissionEvent* > & eventList, TAIAlias missionAlias, uint32 stepIndex); CMissionGuild* getMissionByAlias( TAIAlias missionAlias ); bool isMissionSuccessfull(TAIAlias alias); + void sendDynamicMessageToMembers(const std::string &msgName, const TVectorParamCheck ¶ms, const std::set &excluded) const; ///\return the mission inline std::vector & getMissions() { diff --git a/code/ryzom/server/src/entities_game_service/mission_client_callbacks.cpp b/code/ryzom/server/src/entities_game_service/mission_client_callbacks.cpp index f0205ed95..52fd214f9 100644 --- a/code/ryzom/server/src/entities_game_service/mission_client_callbacks.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_client_callbacks.cpp @@ -26,6 +26,9 @@ #include "team_manager/team_manager.h" #include "mission_manager/mission_team.h" #include "mission_manager/mission_log.h" +#include "guild_manager/guild_manager.h" +#include "guild_manager/guild.h" +#include "guild_manager/guild_member.h" using namespace std; @@ -222,50 +225,108 @@ void cbClientGroupAbandonMission( NLNET::CMessage& msgin, const std::string &ser CCharacter * user = PlayerManager.getChar( userId ); user->setAfkState(false); - CTeam * team = TeamManager.getRealTeam( user->getTeamId() ); - if ( !team ) - { - MISLOG("user:%s cbClientGroupAbandonMission : Invalid team", userId.toString().c_str()); - return; - } - if ( team->getLeader() != userId ) - { - CCharacter::sendDynamicSystemMessage( user->getEntityRowId(), "REQ_LEADER_TO_ABANDON_MISSION" ); - return; - } - if ( index >= team->getMissions().size() ) + // We check if it's a guild or team mission + if (index < MaxGroupMissionCount) { - MISLOG("user:%s cbClientGroupAbandonMission : Invalid group mission %u ( count %u )", - userId.toString().c_str(), index, team->getMissions().size()); - return; - } - + // Team - CMissionTeam* mission = team->getMissions()[index]; - nlassert(mission); - - if ( mission->getFinished() == false ) - { - CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( mission->getTemplateId() ); - if ( !templ ) + CTeam * team = TeamManager.getRealTeam( user->getTeamId() ); + if ( !team ) { - MISLOG("user:%s cbClientGroupAbandonMission : invalid group mission alias %u", - userId.toString().c_str(), mission->getTemplateId()); + MISLOG("user:%s cbClientGroupAbandonMission : Invalid team", userId.toString().c_str()); return; } - if ( templ->Tags.NonAbandonnable ) + if ( team->getLeader() != userId ) { - MISLOG("user:%s cbClientGroupAbandonMission : group mission alias %u is not abandonnable but user tries to abandon it", - userId.toString().c_str(), mission->getTemplateId()); + CCharacter::sendDynamicSystemMessage( user->getEntityRowId(), "REQ_LEADER_TO_ABANDON_MISSION" ); + return; + } + + if ( index >= team->getMissions().size() ) + { + MISLOG("user:%s cbClientGroupAbandonMission : Invalid group mission %u ( count %u )", + userId.toString().c_str(), index, team->getMissions().size()); return; } - set excluded; - excluded.insert( userId ); - team->sendDynamicMessageToMembers( "ABANDON_GROUP_MISSION",TVectorParamCheck(), excluded ); + + CMissionTeam* mission = team->getMissions()[index]; + nlassert(mission); + + if ( mission->getFinished() == false ) + { + CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( mission->getTemplateId() ); + if ( !templ ) + { + MISLOG("user:%s cbClientGroupAbandonMission : invalid group mission alias %u", + userId.toString().c_str(), mission->getTemplateId()); + return; + } + if ( templ->Tags.NonAbandonnable ) + { + MISLOG("user:%s cbClientGroupAbandonMission : group mission alias %u is not abandonnable but user tries to abandon it", + userId.toString().c_str(), mission->getTemplateId()); + return; + } + set excluded; + excluded.insert( userId ); + + team->sendDynamicMessageToMembers( "ABANDON_GROUP_MISSION",TVectorParamCheck(), excluded ); + } + team->removeMission( index, mr_abandon ); + } + else + { + // Guild + // We set the correct index + index = MaxGroupMissionCount - index; + + CGuild * guild = CGuildManager::getInstance()->getGuildFromId( user->getGuildId() ); + if ( !guild ) + { + MISLOG("user:%s cbClientGroupAbandonMission : Invalid team", userId.toString().c_str()); + return; + } + if ( guild->getLeader()->getIngameEId() != userId ) + { + CCharacter::sendDynamicSystemMessage( user->getEntityRowId(), "REQ_LEADER_TO_ABANDON_MISSION" ); + return; + } + + if ( index >= guild->getMissions().size() ) + { + MISLOG("user:%s cbClientGroupAbandonMission : Invalid group mission %u ( count %u )", + userId.toString().c_str(), index, guild->getMissions().size()); + return; + } + + + CMissionGuild* mission = guild->getMissions()[index]; + nlassert(mission); + + if ( mission->getFinished() == false ) + { + CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( mission->getTemplateId() ); + if ( !templ ) + { + MISLOG("user:%s cbClientGroupAbandonMission : invalid group mission alias %u", + userId.toString().c_str(), mission->getTemplateId()); + return; + } + if ( templ->Tags.NonAbandonnable ) + { + MISLOG("user:%s cbClientGroupAbandonMission : group mission alias %u is not abandonnable but user tries to abandon it", + userId.toString().c_str(), mission->getTemplateId()); + return; + } + set excluded; + excluded.insert( userId ); + + guild->sendDynamicMessageToMembers( "ABANDON_GROUP_MISSION",TVectorParamCheck(), excluded ); + } + guild->removeMission( index, mr_abandon ); } - team->removeMission( index, mr_abandon ); } //---------------------------------------------------------------------------- From 185603b5e659e0e8101d3d692598e0bc92d576cc Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Tue, 19 Jul 2011 18:10:28 +0200 Subject: [PATCH 046/215] Changed: #1304: Debug of the options added in the script (';' replaced by ':' for parameters separation) --HG-- branch : gsoc2011-guildmissions --- .../mission_compiler_lib/step_content.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp index 456f7e286..442ca6fbd 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp @@ -170,7 +170,7 @@ public: string ret; ret = "recv_money : "+_Amount; if (_Guild) - ret += "; guild"; + ret += ": guild"; ret += NL; return ret; } @@ -702,7 +702,7 @@ public: string ret; ret = "recv_fame : "+_Faction+" "+_Fame; if (_Guild) - ret += "; guild"; + ret += ": guild"; ret += NL; return ret; } @@ -800,7 +800,7 @@ public: if (_Group) ret += " : group"; if (_Guild) - ret += "; guild"; + ret += ": guild"; ret += NL; } @@ -874,7 +874,7 @@ public: if (_Group) ret += " : group"; if (_Guild) - ret += "; guild"; + ret += ": guild"; ret += NL; } @@ -968,7 +968,7 @@ public: if (!_BotDestroyer.empty()) ret += " : "+_BotDestroyer; if (_Guild) - ret += "; guild"; + ret += ": guild"; ret += NL; } @@ -1800,7 +1800,7 @@ std::string CContentObjective::genNbGuildMembersNeededOption(CMissionData &md) // If we are in a guild mission we add the 'nb_guild_members_needed' option to the script if (md.isGuildMission()) { - ret = "; nb_guild_members_needed: "; + ret = ": nb_guild_members_needed "; ret += toString(_NbGuildMembersNeeded); } From 2d1ce64c38961bd013d97c57ea52db59e20f9084 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Tue, 19 Jul 2011 18:39:29 +0200 Subject: [PATCH 047/215] Changed: #1304: Parsing the "guild" parameter for the actions that need it. --HG-- branch : gsoc2011-guildmissions --- .../mission_manager/mission_action.cpp | 82 ++++++++++++++++--- 1 file changed, 72 insertions(+), 10 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp index eb627551d..f8a762f95 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp @@ -511,9 +511,9 @@ class CMissionActionRecvItem : public IMissionAction { _SourceLine = line; bool ret = true; - if ( script.size() != 2 && script.size() != 3 ) + if ( script.size() != 2 && script.size() != 3 && script.size() != 4) { - MISLOGSYNTAXERROR(" [] [][:npc_name][:group]"); + MISLOGSYNTAXERROR(" [] [][:npc_name][:group][:guild]"); return false; } vector args; @@ -569,6 +569,17 @@ class CMissionActionRecvItem : public IMissionAction _Group = true; } + // We check for the guild option + _Guild = false; + for (std::vector< std::string >::const_iterator it = script.begin(); it != script.end(); ++it) + { + if (CMissionParser::getNoBlankString(*it) == "guild") + { + _Guild = true; + break; + } + } + if ( _Quantity == 0 ) { @@ -760,6 +771,7 @@ class CMissionActionRecvItem : public IMissionAction uint16 _Quantity; CSheetId _SheetId; bool _Group; + bool _Guild; MISSION_ACTION_GETNEWPTR(CMissionActionRecvItem) }; @@ -772,9 +784,9 @@ class CMissionActionRecvNamedItem : public IMissionAction bool buildAction ( uint32 line, const std::vector< std::string > & script, CMissionGlobalParsingData & globalData, CMissionSpecificParsingData & missionData) { _SourceLine = line; - if ( script.size() != 2 && script.size() != 3 ) + if ( script.size() != 2 && script.size() != 3 && script.size() != 4) { - MISLOGSYNTAXERROR(" [] [:group]"); + MISLOGSYNTAXERROR(" [] [:group] [:guild]"); return false; } vector args; @@ -813,6 +825,17 @@ class CMissionActionRecvNamedItem : public IMissionAction _Group = true; } + // We check for the guild option + _Guild = false; + for (std::vector< std::string >::const_iterator it = script.begin(); it != script.end(); ++it) + { + if (CMissionParser::getNoBlankString(*it) == "guild") + { + _Guild = true; + break; + } + } + if ( _Quantity == 0 ) { MISLOGERROR("quantity = 0"); @@ -945,6 +968,7 @@ class CMissionActionRecvNamedItem : public IMissionAction std::string _NamedItem; uint16 _Quantity; bool _Group; + bool _Guild; MISSION_ACTION_GETNEWPTR(CMissionActionRecvNamedItem) }; @@ -1043,9 +1067,9 @@ class CMissionActionDestroyItem : { // Parse the line _SourceLine = line; - if ( script.size() != 2 && script.size() != 3) + if ( script.size() != 2 && script.size() != 3 && script.size() != 4) { - MISLOGSYNTAXERROR(" [] []:[npc_name]"); + MISLOGSYNTAXERROR(" [] []:[npc_name] [:guild]"); return false; } @@ -1060,6 +1084,17 @@ class CMissionActionDestroyItem : ret= false; } + // We check for the guild option + _Guild = false; + for (std::vector< std::string >::const_iterator it = script.begin(); it != script.end(); ++it) + { + if (CMissionParser::getNoBlankString(*it) == "guild") + { + _Guild = true; + break; + } + } + return ret; } @@ -1104,6 +1139,7 @@ class CMissionActionDestroyItem : } }; TAIAlias _Npc; + bool _Guild; MISSION_ACTION_GETNEWPTR(CMissionActionDestroyItem) }; @@ -1607,9 +1643,9 @@ class CMissionActionRecvMoney : public IMissionAction { bool ret = true; _SourceLine = line; - if ( script.size() != 2 ) + if ( script.size() != 2 && script.size() != 3) { - MISLOGSYNTAXERROR(" OR *[;]"); + MISLOGSYNTAXERROR(" [: guild] OR *[;]"); return false; } @@ -1637,6 +1673,18 @@ class CMissionActionRecvMoney : public IMissionAction ret = false; } } + + // We check for the guild option + _Guild = false; + for (std::vector< std::string >::const_iterator it = script.begin(); it != script.end(); ++it) + { + if (CMissionParser::getNoBlankString(*it) == "guild") + { + _Guild = true; + break; + } + } + return ret; } @@ -1663,6 +1711,7 @@ class CMissionActionRecvMoney : public IMissionAction } }; uint _Amount; + bool _Guild; MISSION_ACTION_GETNEWPTR(CMissionActionRecvMoney) }; @@ -1675,9 +1724,9 @@ class CMissionActionRecvFame : public IMissionAction bool buildAction ( uint32 line, const std::vector< std::string > & script, CMissionGlobalParsingData & globalData, CMissionSpecificParsingData & missionData) { _SourceLine = line; - if ( script.size() != 2 ) + if ( script.size() != 2 && script.size() != 3) { - MISLOGSYNTAXERROR(" "); + MISLOGSYNTAXERROR(" [:guild]"); return false; } vector args; @@ -1700,6 +1749,18 @@ class CMissionActionRecvFame : public IMissionAction MISLOGERROR("fame = 0"); return false; } + + // We check for the guild option + _Guild = false; + for (std::vector< std::string >::const_iterator it = script.begin(); it != script.end(); ++it) + { + if (CMissionParser::getNoBlankString(*it) == "guild") + { + _Guild = true; + break; + } + } + return true; } @@ -1722,6 +1783,7 @@ class CMissionActionRecvFame : public IMissionAction }; uint32 _Faction; sint32 _Value; + bool _Guild; MISSION_ACTION_GETNEWPTR(CMissionActionRecvFame) }; From af4d2d62362b9b30ba4f4a3000f5c5a95e9090b0 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Tue, 19 Jul 2011 19:10:12 +0200 Subject: [PATCH 048/215] Changed: #1304: Implementation of the "guild" parameter for the "recv_money" action --HG-- branch : gsoc2011-guildmissions --- .../mission_manager/mission_action.cpp | 64 ++++++++++++++++--- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp index f8a762f95..c039b6b5f 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp @@ -1695,19 +1695,63 @@ class CMissionActionRecvMoney : public IMissionAction std::vector entities; instance->getEntities(entities); - uint amount = _Amount / (uint)entities.size(); - if ( amount == 0 || _Amount % entities.size() ) - amount++; - for ( uint i = 0; i < entities.size(); i++ ) + + // If the guild parameter is not set we just divide the money and give it to each entity + if (!_Guild) { - CCharacter * user = PlayerManager.getChar( entities[i] ); - if ( user ) + + uint amount = _Amount / (uint)entities.size(); + if ( amount == 0 || _Amount % entities.size() ) + amount++; + for ( uint i = 0; i < entities.size(); i++ ) { - user->giveMoney( _Amount ); - SM_STATIC_PARAMS_1(params, STRING_MANAGER::integer); - params[0].Int = _Amount; - PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_RECV_MONEY",params); + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) + { + user->giveMoney( _Amount ); + SM_STATIC_PARAMS_1(params, STRING_MANAGER::integer); + params[0].Int = _Amount; + PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_RECV_MONEY",params); + } } + + } + // Else we give the money to the guild + else + { + if (entities.size() == 0) + return; + + CCharacter * user = PlayerManager.getChar( entities[0] ); + if (!user) + { + LOGMISSIONACTION("recv_money : Invalid user"); + return; + } + + CGuild * guild = CGuildManager::getInstance()->getGuildFromId(user->getGuildId()); + if (guild) + { + guild->addMoney(_Amount); + } + else + { + LOGMISSIONACTION("recv_money : Invalid guild id '" + NLMISC::toString(user->getGuildId()) + "'"); + return; + } + + // tell everyone some money has been given to the guild + for ( uint i = 0; i < entities.size(); i++ ) + { + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) + { + SM_STATIC_PARAMS_1(params, STRING_MANAGER::integer); + params[0].Int = _Amount; + PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_GUILD_RECV_MONEY",params); + } + } + } }; uint _Amount; From cb6b4ac91708487a5776e8b6e66e545db8ddd041 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Wed, 20 Jul 2011 18:41:30 +0200 Subject: [PATCH 049/215] Changed: #1304: Implementation of the "guild" parameter for the "recv_fame" action --HG-- branch : gsoc2011-guildmissions --- .../mission_manager/mission_action.cpp | 58 ++++++++++++++++--- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp index c039b6b5f..a73e2d714 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp @@ -1813,15 +1813,57 @@ class CMissionActionRecvFame : public IMissionAction LOGMISSIONACTION("recv_fame"); std::vector entities; instance->getEntities(entities); - for ( uint i = 0; i < entities.size(); i++ ) - { - CEntityId eid = TheDataset.getEntityId(entities[i]); - CFameInterface::getInstance().addFameIndexed(eid, _Faction, _Value, true); - // Make the client refresh the icons on mission giver NPCs, at once - CCharacter *character = PlayerManager.getChar(entities[i]); - if (character) - character->sendEventForMissionAvailabilityCheck(); + // If there is no "guild" parameter we give the fame to every user + if (!_Guild) + { + + for ( uint i = 0; i < entities.size(); i++ ) + { + CEntityId eid = TheDataset.getEntityId(entities[i]); + CFameInterface::getInstance().addFameIndexed(eid, _Faction, _Value, true); + + // Make the client refresh the icons on mission giver NPCs, at once + CCharacter *character = PlayerManager.getChar(entities[i]); + if (character) + character->sendEventForMissionAvailabilityCheck(); + } + + } + // Else we just give it to the guild + else + { + + if (entities.size() == 0) + return; + + CCharacter * user = PlayerManager.getChar( entities[0] ); + if (!user) + { + LOGMISSIONACTION("recv_fame : Invalid user"); + return; + } + + CGuild * guild = CGuildManager::getInstance()->getGuildFromId(user->getGuildId()); + if (guild) + { + CFameInterface::getInstance().addFameIndexed(guild->getEId(), _Faction, _Value, true); + } + else + { + LOGMISSIONACTION("recv_fame : Invalid guild id '" + NLMISC::toString(user->getGuildId()) + "'"); + return; + } + + // tell everyone some money has been given to the guild + for ( uint i = 0; i < entities.size(); i++ ) + { + // Make the client refresh the icons on mission giver NPCs, at once + CCharacter *character = PlayerManager.getChar(entities[i]); + if (character) + character->sendEventForMissionAvailabilityCheck(); + } + } }; From a431e625390c2fe85bbba41e754e9a3c4f2e7728 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 20 Jul 2011 12:54:30 -0500 Subject: [PATCH 050/215] Update: Completed settings page, loading and saving of settings and preferences (checked publish servers.) Changed namespace from 'Plugin' to 'MissionCompiler'. --HG-- branch : branch-mission-compiler-qt --- .../mission_compiler_main_window.cpp | 88 +++++++++++++++++-- .../mission_compiler_main_window.h | 4 +- .../mission_compiler_plugin.cpp | 4 +- .../mission_compiler_plugin.h | 4 +- .../mission_compiler_plugin_constants.h | 17 ++++ .../mission_compiler_settings_page.cpp | 74 ++++++++++------ .../mission_compiler_settings_page.h | 4 +- .../mission_compiler/server_entry_dialog.cpp | 4 +- .../mission_compiler/server_entry_dialog.h | 4 +- 9 files changed, 155 insertions(+), 48 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin_constants.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index 9bab135d4..56cc791c7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -2,6 +2,7 @@ #include "ui_mission_compiler_main_window.h" #include "validation_file.h" #include "mission_compiler.h" +#include "mission_compiler_plugin_constants.h" #include #include @@ -11,6 +12,8 @@ #include #include #include +#include +#include #include "../core/icore.h" #include "../core/imenu_manager.h" @@ -23,6 +26,8 @@ #include #include +using namespace MissionCompiler::Constants; + MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MissionCompilerMainWindow) @@ -70,6 +75,9 @@ MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : connect(ui->filterEdit, SIGNAL(textEdited(const QString&)), this, SLOT(handleFilterChanged(const QString&))); connect(ui->resetFiltersButton, SIGNAL(clicked()), this, SLOT(handleResetFiltersButton())); + // Connect for settings changes. + connect(Core::ICore::instance(), SIGNAL(changeSettings()), this, SLOT(handleChangedSettings())); + // Set the default data dir to the primitives path. QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup(Core::Constants::DATA_PATH_SECTION); @@ -365,26 +373,88 @@ void MissionCompilerMainWindow::updateCompileLog() } void MissionCompilerMainWindow::loadConfig() { - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup("MissionCompiler"); - - //QColor color; - //color = settings->value("BackgroundColor", QColor(80, 80, 80)).value(); - //m_nelWidget->setBackgroundColor(NLMISC::CRGBA(color.red(), color.green(), color.blue(), color.alpha())); + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(MISSION_COMPILER_SECTION); + + // Retrieve the local text path. + QString localPath = settings->value(SETTING_LOCAL_TEXT_PATH).toString(); + QListWidgetItem *item = new QListWidgetItem("Local"); + item->setForeground(Qt::blue); + item->setCheckState(Qt::Unchecked); + ui->publishServersList->addItem(item); + + QStringList items = settings->value(SETTING_SERVERS_TABLE_ITEMS).toStringList(); + int column = 0; + int row = 0; + Q_FOREACH(QString var, items) + { + // Check to see if we're starting a new row. + if(column > 2) + { + column = 0; + row++; + } + if(column == 0) + { + item = new QListWidgetItem(var); + item->setCheckState(Qt::Unchecked); + ui->publishServersList->addItem(item); + } + + column++; + } + + // Reapply the checkboxes for servers we had checked previously. + QStringList servers = settings->value(SETTING_PUBLISH_SERVER_CHECKS).toStringList(); + applyCheckboxes(servers); + + settings->endGroup(); } void MissionCompilerMainWindow::saveConfig() { QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup("MissionCompiler" ); + settings->beginGroup(MISSION_COMPILER_SECTION); - //QColor color(m_nelWidget->backgroundColor().R, m_nelWidget->backgroundColor().G, m_nelWidget->backgroundColor().B, m_nelWidget->backgroundColor().A); - //settings->setValue("BackgroundColor", color); + QStringList servers; + for(int row = 0; row < ui->publishServersList->count(); row++) + { + QListWidgetItem *item = ui->publishServersList->item(row); + if(item->checkState() == Qt::Checked) + servers << item->text(); + } + + settings->setValue(SETTING_PUBLISH_SERVER_CHECKS, servers); settings->endGroup(); settings->sync(); } +void MissionCompilerMainWindow::handleChangedSettings() +{ + QStringList servers; + for(int row = 0; row < ui->publishServersList->count(); row++) + { + QListWidgetItem *item = ui->publishServersList->item(row); + if(item->checkState() == Qt::Checked) + servers << item->text(); + } + ui->publishServersList->clear(); + loadConfig(); + + applyCheckboxes(servers); +} + +void MissionCompilerMainWindow::applyCheckboxes(const QStringList &servers) +{ + Q_FOREACH(QString server, servers) + { + QList items = ui->publishServersList->findItems(server, Qt::MatchExactly); + items.at(0)->setCheckState(Qt::Checked); + } +} + MissionCompilerMainWindow::~MissionCompilerMainWindow() { + saveConfig(); delete ui; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h index 7ea2e1f81..1451b2613 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h @@ -47,6 +47,7 @@ public Q_SLOTS: void handleDataDirButton(); void handleDataDirChanged(const QString &text); void handleResetFiltersButton(); + void handleChangedSettings(); private: Ui::MissionCompilerMainWindow *ui; @@ -56,7 +57,8 @@ private: bool parsePrimForMissions(NLLIGO::IPrimitive const *prim, TMissionContainer &missions); void compileMission(bool publish=false); void moveSelectedItem(const QModelIndex &index, QStringListModel *from, QStringListModel *to); - + void applyCheckboxes(const QStringList &servers); + QMenu *_toolModeMenu; QUndoStack *m_undoStack; QStringListModel *m_allPrimitivesModel; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp index d67128a50..3b9eb3ead 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp @@ -19,7 +19,7 @@ #include "mission_compiler_settings_page.h" -namespace Plugin +namespace MissionCompiler { MissionCompilerPlugin::~MissionCompilerPlugin() @@ -128,4 +128,4 @@ ExtensionSystem::IPluginSpec *MissionCompilerPlugin::pluginByName(const QString } -Q_EXPORT_PLUGIN(Plugin::MissionCompilerPlugin) \ No newline at end of file +Q_EXPORT_PLUGIN(MissionCompiler::MissionCompilerPlugin) \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h index 773c984ea..7f64465e7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h @@ -24,7 +24,7 @@ namespace ExtensionSystem class IPluginSpec; } -namespace Plugin +namespace MissionCompiler { class MissionCompilerPlugin : public QObject, public ExtensionSystem::IPlugin @@ -96,6 +96,6 @@ public: MissionCompilerMainWindow *m_missionCompilerMainWindow; }; -} // namespace Plugin +} // namespace MissionCompiler #endif // MISSION_COMPILER_PLUGIN_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin_constants.h new file mode 100644 index 000000000..bfa71ceb1 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin_constants.h @@ -0,0 +1,17 @@ +#ifndef MISSION_COMPILER_PLUGIN_CONSTANTS_H +#define MISSION_COMPILER_PLUGIN_CONSTANTS_H + +namespace MissionCompiler +{ +namespace Constants +{ +//settings +const char * const MISSION_COMPILER_SECTION = "MissionCompiler"; +const char * const SETTING_LOCAL_TEXT_PATH = "LocalTextPath"; +const char * const SETTING_SERVERS_TABLE_ITEMS = "ServersTableItems"; +const char * const SETTING_PUBLISH_SERVER_CHECKS = "PublishServerChecks"; + +} // namespace Constants +} // namespace MissionCompiler + +#endif // MISSION_COMPILER_PLUGIN_CONSTANTS_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp index 028ac51db..2f75aa4ab 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.cpp @@ -17,6 +17,7 @@ // Project includes #include "mission_compiler_settings_page.h" +#include "mission_compiler_plugin_constants.h" #include "../core/core_constants.h" #include "../core/icore.h" @@ -31,7 +32,7 @@ #include #include -namespace Plugin +namespace MissionCompiler { QString lastDir = "."; @@ -149,37 +150,54 @@ void MissionCompilerSettingsPage::delServer() void MissionCompilerSettingsPage::readSettings() { - //QStringList paths; - //QSettings *settings = Core::ICore::instance()->settings(); - //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - //if (m_recurse) - // paths = settings->value(Core::Constants::RECURSIVE_SEARCH_PATHS).toStringList(); - //else - // paths = settings->value(Core::Constants::SEARCH_PATHS).toStringList(); - //settings->endGroup(); - //Q_FOREACH(QString path, paths) - //{ - // QListWidgetItem *newItem = new QListWidgetItem; - // newItem->setText(path); - // newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); - // m_ui.serversTreeWidget->addItem(newItem); - //} + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(Constants::MISSION_COMPILER_SECTION); + + // Retrieve the local text path. + m_ui.localPathEdit->setText(settings->value(Constants::SETTING_LOCAL_TEXT_PATH).toString()); + + QStringList items = settings->value(Constants::SETTING_SERVERS_TABLE_ITEMS).toStringList(); + int column = 0; + int row = 0; + m_ui.serversTableWidget->insertRow(row); + Q_FOREACH(QString var, items) + { + // Check to see if we're starting a new row. + if(column > 2) + { + column = 0; + row++; + m_ui.serversTableWidget->insertRow(row); + } + + QTableWidgetItem *item = new QTableWidgetItem(var); + m_ui.serversTableWidget->setItem(row, column, item); + + column++; + } + settings->endGroup(); } void MissionCompilerSettingsPage::writeSettings() { - //QStringList paths; - //for (int i = 0; i < m_ui.serversTreeWidget->count(); ++i) - // paths << m_ui.serversTreeWidget->item(i)->text(); + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(Constants::MISSION_COMPILER_SECTION); + + // Save the local text path. + settings->setValue(Constants::SETTING_LOCAL_TEXT_PATH, m_ui.localPathEdit->text()); - //QSettings *settings = Core::ICore::instance()->settings(); - //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - //if (m_recurse) - // settings->setValue(Core::Constants::RECURSIVE_SEARCH_PATHS, paths); - //else - // settings->setValue(Core::Constants::SEARCH_PATHS, paths); - //settings->endGroup(); - //settings->sync(); + QStringList items; + for(int row = 0; row < m_ui.serversTableWidget->rowCount(); row++) + { + for(int column = 0; column < m_ui.serversTableWidget->columnCount(); column++) + { + items << m_ui.serversTableWidget->item(row, column)->text(); + } + } + + settings->setValue(Constants::SETTING_SERVERS_TABLE_ITEMS, items); + settings->endGroup(); + settings->sync(); } -} /* namespace Plugin */ \ No newline at end of file +} /* namespace MissionCompiler */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h index 32b59e050..71996e442 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_settings_page.h @@ -27,7 +27,7 @@ class QWidget; -namespace Plugin +namespace MissionCompiler { /** @class MissionCompilerSettingsPage @@ -63,6 +63,6 @@ private: Ui::MissionCompilerSettingsPage m_ui; }; -} // namespace Plugin +} // namespace MissionCompiler #endif // MISSION_COMPILER_SETTINGS_PAGE_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.cpp index b930eb5ec..a9ef08f50 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.cpp @@ -25,7 +25,7 @@ // Qt includes #include -namespace Plugin +namespace MissionCompiler { ServerEntryDialog::ServerEntryDialog(QWidget *parent) @@ -86,4 +86,4 @@ void ServerEntryDialog::lookupPrimPath() QString path = QFileDialog::getExistingDirectory(this, "", curPath); m_ui->serverPrimPathEdit->setText(path); } -} /* namespace Plugin */ \ No newline at end of file +} /* namespace MissionCompiler */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.h index e2a1d5016..98a086cd0 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/server_entry_dialog.h @@ -25,7 +25,7 @@ namespace Ui { class ServerEntryDialog; } -namespace Plugin +namespace MissionCompiler { /** @class ServerEntryDialog @@ -54,6 +54,6 @@ private: Ui::ServerEntryDialog *m_ui; }; -} // namespace Plugin +} // namespace MissionCompiler #endif // SERVER_ENTRY_DIALOG_H From 5ac224b5af63e47312affc7f94a39901a1fce836 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Wed, 20 Jul 2011 20:33:52 +0200 Subject: [PATCH 051/215] Changed: #1304: Implementation of the "guild" parameter for the "recv_item" action --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.cpp | 6 +- .../guild_manager/guild.h | 2 +- .../mission_manager/mission_action.cpp | 326 ++++++++++++------ 3 files changed, 220 insertions(+), 114 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp index 3a05ef309..df9082c6b 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp @@ -904,10 +904,12 @@ bool CGuild::canAccessToGuildInventory( CCharacter * user ) } //---------------------------------------------------------------------------- -void CGuild::putItem( CGameItemPtr item ) +bool CGuild::putItem( CGameItemPtr item ) { - if (_Inventory->insertItem(item, INVENTORIES::INSERT_IN_FIRST_FREE_SLOT, true) != CInventoryBase::ior_ok) + CInventoryBase::TInventoryOpResult res = _Inventory->insertItem(item, INVENTORIES::INSERT_IN_FIRST_FREE_SLOT, true); + if (res != CInventoryBase::ior_ok) item.deleteItem(); + return res == CInventoryBase::ior_ok; } //---------------------------------------------------------------------------- diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index 64378b717..449debe53 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -235,7 +235,7 @@ public: return _Inventory->getItem(slot); } /// add an item in the guild inventory (item can be deleted if not inserted : do not use it anymore in any case!) - void putItem( CGameItemPtr item ); + bool putItem( CGameItemPtr item ); /// return the inventory (const) const NLMISC::CSmartPtr& getInventory() const { return _Inventory; } diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp index a73e2d714..61646a74b 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp @@ -617,7 +617,7 @@ class CMissionActionRecvItem : public IMissionAction } } } - else if ( !_Group ) + else if ( !_Group && !_Guild) { CCharacter * user = PlayerManager.getChar( entities[0] ); CTeam * team = TeamManager.getRealTeam(user->getTeamId()); @@ -628,141 +628,245 @@ class CMissionActionRecvItem : public IMissionAction } } - - // check free room space in inventory - // NB : in case of group, fail happens only if none in the group have enough free space - sint16 neededSlotCount = 0; - uint32 neededBulk = 0; - CSheetId sheet = ( _SheetId != CSheetId::Unknown )?_SheetId:_Item.getSheetId(); - CGameItemPtr itemTmp = GameItemManager.createItem(sheet, _Quality, true, true); - if (itemTmp != NULL) + // If the case we want to give the item to the guild + if (_Guild) { - neededSlotCount = (sint16) ceil( (float)_Quantity / itemTmp->getMaxStackSize() ); - neededBulk = _Quantity * itemTmp->getStackBulk(); - itemTmp.deleteItem(); + if (entities.size() == 0) + return; + + CCharacter * user = PlayerManager.getChar( entities[0] ); + if (!user) + { + LOGMISSIONACTION("recv_fame : Invalid user"); + return; + } + + CGuild * guild = CGuildManager::getInstance()->getGuildFromId(user->getGuildId()); + if (!guild) + { + LOGMISSIONACTION("recv_fame : Invalid guild id '" + NLMISC::toString(user->getGuildId()) + "'"); + return; + } + + SM_STATIC_PARAMS_3(params, STRING_MANAGER::item, STRING_MANAGER::integer, STRING_MANAGER::integer); + if ( _SheetId != CSheetId::Unknown ) + { + const CStaticItem * form = CSheets::getForm( _SheetId ); + if ( !form ) + { + LOGMISSIONACTION("sheetId '" + _SheetId.toString() + "' is unknown"); + return; + } + if (form->Family != ITEMFAMILY::MISSION_ITEM) + return; + + uint quantity = _Quantity; + while (quantity > 0) + { + CGameItemPtr item = user->createItem(_Quality, quantity, _SheetId); + if (item == NULL) + break; + const uint32 stackSize = item->getStackSize(); + + if (!guild->putItem(item)) + { + CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( instance->getTemplateId() ); + if ( templ ) + { + if ( templ->Tags.FailIfInventoryIsFull ) + { + instance->setProcessingState(CMission::ActionFailed); + return; + } + } + } + // from here item maybe NULL (because of autostack) + + quantity -= stackSize; + } + params[2].Int = _Quality; + } + else + { + const CStaticItem * form = CSheets::getForm( _Item.getSheetId() ); + if ( !form ) + { + LOGMISSIONACTION("sheetId '" + _Item.getSheetId().toString() + "' is unknown"); + return; + } + uint quantity = _Quantity; + while (quantity > 0) + { + CGameItemPtr item = _Item.createItem(quantity); + if (item == NULL) + break; + + const uint32 stackSize = item->getStackSize(); + if (!guild->putItem(item)) + { + CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( instance->getTemplateId() ); + if ( templ ) + { + if ( templ->Tags.FailIfInventoryIsFull ) + { + instance->setProcessingState(CMission::ActionFailed); + return; + } + } + } + // from here item maybe NULL (because of autostack) + + quantity -= stackSize; + } + params[2].Int = _Item.getQuality(); + } + + params[0].SheetId = _SheetId; + params[1].Int = _Quantity; + + for ( uint i = 0; i < entities.size(); i++ ) + { + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) + PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_GUILD_RECV_ITEM", params); + } } else { - LOGMISSIONACTION("can't get static item from sheet " + sheet.toString()); - return; - } - - - bool fail = true; - for ( uint i = 0; i < entities.size(); i++ ) - { - CCharacter * user = PlayerManager.getChar( entities[i] ); - if ( user ) + // check free room space in inventory + // NB : in case of group, fail happens only if none in the group have enough free space + sint16 neededSlotCount = 0; + uint32 neededBulk = 0; + CSheetId sheet = ( _SheetId != CSheetId::Unknown )?_SheetId:_Item.getSheetId(); + CGameItemPtr itemTmp = GameItemManager.createItem(sheet, _Quality, true, true); + if (itemTmp != NULL) { - CInventoryPtr invBag = user->getInventory( INVENTORIES::bag ); - sint16 freeSlotcount = invBag->getFreeSlotCount(); - uint32 maxBulk = invBag->getMaxBulk(); - - CInventoryPtr invTemp = user->getInventory( INVENTORIES::temporary ); - if( invTemp ) - { - freeSlotcount -= invTemp->getUsedSlotCount(); - maxBulk -= invTemp->getInventoryBulk(); - } - - if( (neededSlotCount <= freeSlotcount) && ( neededBulk + invBag->getInventoryBulk() <= maxBulk) ) - { - fail = false; - break; - } + neededSlotCount = (sint16) ceil( (float)_Quantity / itemTmp->getMaxStackSize() ); + neededBulk = _Quantity * itemTmp->getStackBulk(); + itemTmp.deleteItem(); } - } - if( fail ) - { - CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( instance->getTemplateId() ); - if ( templ ) + else { - if ( templ->Tags.FailIfInventoryIsFull ) - { - instance->setProcessingState(CMission::ActionFailed); - return; - } + LOGMISSIONACTION("can't get static item from sheet " + sheet.toString()); + return; } - } - for ( uint i = 0; i < entities.size(); i++ ) - { - CCharacter * user = PlayerManager.getChar( entities[i] ); - if ( user ) + bool fail = true; + for ( uint i = 0; i < entities.size(); i++ ) { - SM_STATIC_PARAMS_3(params, STRING_MANAGER::item, STRING_MANAGER::integer, STRING_MANAGER::integer); - if ( _SheetId != CSheetId::Unknown ) + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) { - const CStaticItem * form = CSheets::getForm( _SheetId ); - if ( !form ) + CInventoryPtr invBag = user->getInventory( INVENTORIES::bag ); + sint16 freeSlotcount = invBag->getFreeSlotCount(); + uint32 maxBulk = invBag->getMaxBulk(); + + CInventoryPtr invTemp = user->getInventory( INVENTORIES::temporary ); + if( invTemp ) { - LOGMISSIONACTION("sheetId '" + _SheetId.toString() + "' is unknown"); + freeSlotcount -= invTemp->getUsedSlotCount(); + maxBulk -= invTemp->getInventoryBulk(); + } + + if( (neededSlotCount <= freeSlotcount) && ( neededBulk + invBag->getInventoryBulk() <= maxBulk) ) + { + fail = false; + break; + } + } + } + if( fail ) + { + CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( instance->getTemplateId() ); + if ( templ ) + { + if ( templ->Tags.FailIfInventoryIsFull ) + { + instance->setProcessingState(CMission::ActionFailed); return; } - if (form->Family != ITEMFAMILY::MISSION_ITEM && !user->enterTempInventoryMode(TEMP_INV_MODE::MissionReward)) - continue; + } + } - uint quantity = _Quantity; - while (quantity > 0) + for ( uint i = 0; i < entities.size(); i++ ) + { + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) + { + SM_STATIC_PARAMS_3(params, STRING_MANAGER::item, STRING_MANAGER::integer, STRING_MANAGER::integer); + if ( _SheetId != CSheetId::Unknown ) { - CGameItemPtr item = user->createItem(_Quality, quantity, _SheetId); - if (item == NULL) - break; - const uint32 stackSize = item->getStackSize(); - - if( form->Family != ITEMFAMILY::MISSION_ITEM ) + const CStaticItem * form = CSheets::getForm( _SheetId ); + if ( !form ) { + LOGMISSIONACTION("sheetId '" + _SheetId.toString() + "' is unknown"); + return; + } + if (form->Family != ITEMFAMILY::MISSION_ITEM && !user->enterTempInventoryMode(TEMP_INV_MODE::MissionReward)) + continue; + + uint quantity = _Quantity; + while (quantity > 0) + { + CGameItemPtr item = user->createItem(_Quality, quantity, _SheetId); + if (item == NULL) + break; + const uint32 stackSize = item->getStackSize(); + + if( form->Family != ITEMFAMILY::MISSION_ITEM ) + { + if (!user->addItemToInventory(INVENTORIES::temporary, item)) + { + item.deleteItem(); + break; + } + } + else + { + if (!user->addItemToInventory(INVENTORIES::bag, item)) + { + item.deleteItem(); + break; + } + } + // from here item maybe NULL (because of autostack) + + quantity -= stackSize; + } + params[2].Int = _Quality; + } + else + { + const CStaticItem * form = CSheets::getForm( _Item.getSheetId() ); + if ( !form ) + { + LOGMISSIONACTION("sheetId '" + _Item.getSheetId().toString() + "' is unknown"); + return; + } + uint quantity = _Quantity; + while (quantity > 0) + { + CGameItemPtr item = _Item.createItem(quantity); + if (item == NULL) + break; + + const uint32 stackSize = item->getStackSize(); if (!user->addItemToInventory(INVENTORIES::temporary, item)) { item.deleteItem(); break; } - } - else - { - if (!user->addItemToInventory(INVENTORIES::bag, item)) - { - item.deleteItem(); - break; - } - } - // from here item maybe NULL (because of autostack) + // from here item maybe NULL (because of autostack) - quantity -= stackSize; + quantity -= stackSize; + } + params[2].Int = _Item.getQuality(); } - params[2].Int = _Quality; + + params[0].SheetId = _SheetId; + params[1].Int = _Quantity; + PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_RECV_ITEM", params); } - else - { - const CStaticItem * form = CSheets::getForm( _Item.getSheetId() ); - if ( !form ) - { - LOGMISSIONACTION("sheetId '" + _Item.getSheetId().toString() + "' is unknown"); - return; - } - uint quantity = _Quantity; - while (quantity > 0) - { - CGameItemPtr item = _Item.createItem(quantity); - if (item == NULL) - break; - - const uint32 stackSize = item->getStackSize(); - if (!user->addItemToInventory(INVENTORIES::temporary, item)) - { - item.deleteItem(); - break; - } - // from here item maybe NULL (because of autostack) - - quantity -= stackSize; - } - params[2].Int = _Item.getQuality(); - } - - params[0].SheetId = _SheetId; - params[1].Int = _Quantity; - PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_RECV_ITEM", params); } } }; From 757f393db1c38474b8e6fb2e8e0d2a318faaf7e1 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Wed, 20 Jul 2011 21:08:53 +0200 Subject: [PATCH 052/215] Changed: #1304: Implementation of the "guild" parameter for the "recv_named_item" action --HG-- branch : gsoc2011-guildmissions --- .../mission_manager/mission_action.cpp | 171 ++++++++++++------ 1 file changed, 114 insertions(+), 57 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp index 61646a74b..5630913d0 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp @@ -975,7 +975,7 @@ class CMissionActionRecvNamedItem : public IMissionAction } } } - else if ( !_Group ) + else if ( !_Group && !_Guild) { CCharacter * user = PlayerManager.getChar( entities[0] ); CTeam * team = TeamManager.getRealTeam(user->getTeamId()); @@ -986,41 +986,34 @@ class CMissionActionRecvNamedItem : public IMissionAction } } - // check free room space in inventory - // NB : in case of group, fail happens only if noone in the group have enough free space - CGameItemPtr itemTmp = CNamedItems::getInstance().createNamedItem(_NamedItem, _Quantity); - if( itemTmp != NULL ) + // If the case we want to give the item to the guild + if (_Guild) { - sint16 neededSlotCount = (sint16) ceil( (float)_Quantity / itemTmp->getMaxStackSize() ); - uint32 neededBulk = _Quantity * itemTmp->getStackBulk(); - itemTmp.deleteItem(); - - bool fail = true; - for ( uint i = 0; i < entities.size(); i++ ) - { - CCharacter * user = PlayerManager.getChar( entities[i] ); - if ( user ) - { - CInventoryPtr invBag = user->getInventory( INVENTORIES::bag ); - sint16 freeSlotcount = invBag->getFreeSlotCount(); - uint32 maxBulk = invBag->getMaxBulk(); + if (entities.size() == 0) + return; - CInventoryPtr invTemp = user->getInventory( INVENTORIES::temporary ); - if( invTemp ) - { - freeSlotcount -= invTemp->getUsedSlotCount(); - maxBulk -= invTemp->getInventoryBulk(); - } - - if( (neededSlotCount <= freeSlotcount) && ( neededBulk + invBag->getInventoryBulk() <= maxBulk) ) - { - fail = false; - break; - } - } - + CCharacter * user = PlayerManager.getChar( entities[0] ); + if (!user) + { + LOGMISSIONACTION("recv_fame : Invalid user"); + return; } - if( fail ) + + CGuild * guild = CGuildManager::getInstance()->getGuildFromId(user->getGuildId()); + if (!guild) + { + LOGMISSIONACTION("recv_fame : Invalid guild id '" + NLMISC::toString(user->getGuildId()) + "'"); + return; + } + + // add the item to inventory + CGameItemPtr item = CNamedItems::getInstance().createNamedItem(_NamedItem, _Quantity); + if (item == NULL) + { + LOGMISSIONACTION("named item '" + _NamedItem + "' is unknown"); + return; + } + if (!guild->putItem(item)) { CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( instance->getTemplateId() ); if ( templ ) @@ -1032,39 +1025,103 @@ class CMissionActionRecvNamedItem : public IMissionAction } } } + else + { + for ( uint i = 0; i < entities.size(); i++ ) + { + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) + { + SM_STATIC_PARAMS_2(params, STRING_MANAGER::dyn_string_id, STRING_MANAGER::integer); + params[0].StringId = item->sendNameId(user); + params[1].Int = _Quantity; + PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_GUILD_RECV_NAMED_ITEM", params); + } + } + } } else { - LOGMISSIONACTION("named item '" + _NamedItem + "' is unknown"); - return; - } - - // apply the action to all entities - for ( uint i = 0; i < entities.size(); i++ ) - { - CCharacter * user = PlayerManager.getChar( entities[i] ); - if ( user ) + // check free room space in inventory + // NB : in case of group, fail happens only if noone in the group have enough free space + CGameItemPtr itemTmp = CNamedItems::getInstance().createNamedItem(_NamedItem, _Quantity); + if( itemTmp != NULL ) { - if (!user->enterTempInventoryMode(TEMP_INV_MODE::MissionReward)) - continue; + sint16 neededSlotCount = (sint16) ceil( (float)_Quantity / itemTmp->getMaxStackSize() ); + uint32 neededBulk = _Quantity * itemTmp->getStackBulk(); + itemTmp.deleteItem(); + + bool fail = true; + for ( uint i = 0; i < entities.size(); i++ ) + { + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) + { + CInventoryPtr invBag = user->getInventory( INVENTORIES::bag ); + sint16 freeSlotcount = invBag->getFreeSlotCount(); + uint32 maxBulk = invBag->getMaxBulk(); - // add the item to inventory - CGameItemPtr item = CNamedItems::getInstance().createNamedItem(_NamedItem, _Quantity); - if (item == NULL) - { - LOGMISSIONACTION("named item '" + _NamedItem + "' is unknown"); - return; + CInventoryPtr invTemp = user->getInventory( INVENTORIES::temporary ); + if( invTemp ) + { + freeSlotcount -= invTemp->getUsedSlotCount(); + maxBulk -= invTemp->getInventoryBulk(); + } + + if( (neededSlotCount <= freeSlotcount) && ( neededBulk + invBag->getInventoryBulk() <= maxBulk) ) + { + fail = false; + break; + } + } + } - if(!user->addItemToInventory(INVENTORIES::temporary, item)) + if( fail ) { - item.deleteItem(); + CMissionTemplate * templ = CMissionManager::getInstance()->getTemplate( instance->getTemplateId() ); + if ( templ ) + { + if ( templ->Tags.FailIfInventoryIsFull ) + { + instance->setProcessingState(CMission::ActionFailed); + return; + } + } } - else + } + else + { + LOGMISSIONACTION("named item '" + _NamedItem + "' is unknown"); + return; + } + + // apply the action to all entities + for ( uint i = 0; i < entities.size(); i++ ) + { + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) { - SM_STATIC_PARAMS_2(params, STRING_MANAGER::dyn_string_id, STRING_MANAGER::integer); - params[0].StringId = item->sendNameId(user); - params[1].Int = _Quantity; - PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_RECV_NAMED_ITEM", params); + if (!user->enterTempInventoryMode(TEMP_INV_MODE::MissionReward)) + continue; + + // add the item to inventory + CGameItemPtr item = CNamedItems::getInstance().createNamedItem(_NamedItem, _Quantity); + if (item == NULL) + { + LOGMISSIONACTION("named item '" + _NamedItem + "' is unknown"); + return; + } + if(!user->addItemToInventory(INVENTORIES::temporary, item)) + { + item.deleteItem(); + } + else + { + SM_STATIC_PARAMS_2(params, STRING_MANAGER::dyn_string_id, STRING_MANAGER::integer); + params[0].StringId = item->sendNameId(user); + params[1].Int = _Quantity; + PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_RECV_NAMED_ITEM", params); + } } } } From 5762e00236bb5e5c01d3545f7d69c4799c22759b Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Wed, 20 Jul 2011 22:09:01 +0200 Subject: [PATCH 053/215] Changed: #1304: Implementation of the "guild" parameter for the "destroy_item" action --HG-- branch : gsoc2011-guildmissions --- .../guild_manager/guild.cpp | 82 ++++++++++++++++ .../guild_manager/guild.h | 16 ++++ .../mission_manager/mission_action.cpp | 93 ++++++++++++++----- 3 files changed, 169 insertions(+), 22 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp index df9082c6b..2460eb530 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp @@ -1030,6 +1030,88 @@ void CGuild::takeItem( CCharacter * user, uint32 slot, uint32 quantity, uint16 s } } +//---------------------------------------------------------------------------- +uint CGuild::selectItems(NLMISC::CSheetId itemSheetId, uint32 quality, std::vector *itemList) +{ + // For all items + uint quantitySelected= 0; + for (uint32 i = 0; i < _Inventory->getSlotCount(); i++) + { + CGameItemPtr item = _Inventory->getItem(i); + if (item == NULL) + continue; + + // if match, append to the list + if (item->getSheetId()==itemSheetId && item->quality()>=quality) + { + quantitySelected+= item->getStackSize(); + if(itemList) + { + CItemSlotId entry; + entry.Slot= i; + entry.Quality= item->quality(); + itemList->push_back(entry); + } + } + } + + return quantitySelected; +} + +//---------------------------------------------------------------------------- +uint CGuild::destroyItems(const std::vector &itemSlotIns, uint32 maxQuantity) +{ + // none to destroy actually? + if(maxQuantity==0 || itemSlotIns.empty()) + return 0; + + // If has to destroy only some of them, must sort to take first the ones of lowest quality + const std::vector *itemSlots= NULL; + std::vector itemSlotSorted; + if(maxQuantity!=uint32(-1)) + { + itemSlotSorted= itemSlotIns; + std::sort(itemSlotSorted.begin(), itemSlotSorted.end()); + itemSlots= &itemSlotSorted; + } + else + { + // just point to the original one + itemSlots= &itemSlotIns; + } + + // destroy items up to the maxquantity wanted + uint index= 0; + uint totalDestroyed= 0; + while(maxQuantity>0 && indexgetStackSize()); + + CGameItemPtr item = _Inventory->removeItem(itemSlot.Slot, quantityToDestroy); + item.deleteItem(); + + // decrease if not infinity + if(maxQuantity!=-1) + maxQuantity-= quantityToDestroy; + + // increase count + totalDestroyed+= quantityToDestroy; + } + + // next slot to destroy + index++; + } + + return totalDestroyed; +} + //---------------------------------------------------------------------------- void CGuild::takeMoney( CCharacter * user, uint64 money, uint16 session ) { diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h index 449debe53..671f7c17b 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.h +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.h @@ -237,6 +237,22 @@ public: /// add an item in the guild inventory (item can be deleted if not inserted : do not use it anymore in any case!) bool putItem( CGameItemPtr item ); + class CItemSlotId + { + public: + uint32 Slot; + uint32 Quality; + bool operator<(const CItemSlotId &o) const + { + return Quality *itemList= NULL); + /// destroy a list of items (up to maxQuantity to destroy) + uint destroyItems(const std::vector &itemSlots, uint32 maxQuantity=-1); + /// return the inventory (const) const NLMISC::CSmartPtr& getInventory() const { return _Inventory; } /// store for a character and return the current info version for an item of the guild inventory diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp index 5630913d0..bfb7c8d5a 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp @@ -1270,32 +1270,81 @@ class CMissionActionDestroyItem : instance->getEntities(entities); if ( entities.empty() ) return; - for ( uint i = 0; i < entities.size(); i++ ) + + // If the "guild" parameter is not set, we destroy the items for the users + if (!_Guild) { - CCharacter * user = PlayerManager.getChar( entities[i] ); - if ( user ) + + for ( uint i = 0; i < entities.size(); i++ ) { - // Select the items in Bag AND mektoub that match the request - vector itemList; - user->selectItems(INVENTORIES::bag, _SheetId, _Quality, &itemList); - for(uint pa=0;paselectItems(INVENTORIES::TInventory(INVENTORIES::pet_animal + pa), _SheetId, _Quality, &itemList); + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) + { + // Select the items in Bag AND mektoub that match the request + vector itemList; + user->selectItems(INVENTORIES::bag, _SheetId, _Quality, &itemList); + for(uint pa=0;paselectItems(INVENTORIES::TInventory(INVENTORIES::pet_animal + pa), _SheetId, _Quality, &itemList); - // Destroy them, up to quantity wanted - // NB: don't care if destroying an item owned by a mektoub is strange because mektoub not near! - uint quantityReallyDestroyed; - quantityReallyDestroyed= user->destroyItems(itemList, _Quantity); + // Destroy them, up to quantity wanted + // NB: don't care if destroying an item owned by a mektoub is strange because mektoub not near! + uint quantityReallyDestroyed; + quantityReallyDestroyed= user->destroyItems(itemList, _Quantity); - // Send message - SM_STATIC_PARAMS_4(params, STRING_MANAGER::bot, STRING_MANAGER::item, STRING_MANAGER::integer, STRING_MANAGER::integer); - TAIAlias botAlias= _Npc; - if(botAlias==CAIAliasTranslator::Invalid) - botAlias= instance->getGiver(); - params[0].setEIdAIAlias(CAIAliasTranslator::getInstance()->getEntityId( botAlias ), botAlias); - params[1].SheetId = _SheetId; - params[2].Int = quantityReallyDestroyed; - params[3].Int = _Quality; - PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_DESTROY_ITEM", params); + // Send message + SM_STATIC_PARAMS_4(params, STRING_MANAGER::bot, STRING_MANAGER::item, STRING_MANAGER::integer, STRING_MANAGER::integer); + TAIAlias botAlias= _Npc; + if(botAlias==CAIAliasTranslator::Invalid) + botAlias= instance->getGiver(); + params[0].setEIdAIAlias(CAIAliasTranslator::getInstance()->getEntityId( botAlias ), botAlias); + params[1].SheetId = _SheetId; + params[2].Int = quantityReallyDestroyed; + params[3].Int = _Quality; + PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_DESTROY_ITEM", params); + } + } + + } + // We destroy the item in the guild + else + { + CCharacter * user = PlayerManager.getChar( entities[0] ); + if (!user) + { + LOGMISSIONACTION("recv_fame : Invalid user"); + return; + } + + CGuild * guild = CGuildManager::getInstance()->getGuildFromId(user->getGuildId()); + if (!guild) + { + LOGMISSIONACTION("recv_fame : Invalid guild id '" + NLMISC::toString(user->getGuildId()) + "'"); + return; + } + + vector itemList; + guild->selectItems(_SheetId, _Quality, &itemList); + + // Destroy them, up to quantity wanted + uint quantityReallyDestroyed; + quantityReallyDestroyed = guild->destroyItems(itemList, _Quantity); + + // Send message + for ( uint i = 0; i < entities.size(); i++ ) + { + CCharacter * user = PlayerManager.getChar( entities[i] ); + if ( user ) + { + SM_STATIC_PARAMS_4(params, STRING_MANAGER::bot, STRING_MANAGER::item, STRING_MANAGER::integer, STRING_MANAGER::integer); + TAIAlias botAlias= _Npc; + if(botAlias==CAIAliasTranslator::Invalid) + botAlias= instance->getGiver(); + params[0].setEIdAIAlias(CAIAliasTranslator::getInstance()->getEntityId( botAlias ), botAlias); + params[1].SheetId = _SheetId; + params[2].Int = quantityReallyDestroyed; + params[3].Int = _Quality; + PHRASE_UTILITIES::sendDynamicSystemMessage(user->getEntityRowId(),"MIS_DESTROY_ITEM", params); + } } } }; From a5c0026d23c974a86765ae6ad10b454462eedf44 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 20 Jul 2011 15:39:23 -0500 Subject: [PATCH 054/215] Update: Fixed a specific crash when only one server was checked and configured. Added mission publication. --HG-- branch : branch-mission-compiler-qt --- .../mission_compiler_main_window.cpp | 68 ++++++++++++++++--- .../mission_compiler_main_window.h | 1 + 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index 56cc791c7..bb4e783a7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -245,17 +245,63 @@ void MissionCompilerMainWindow::compileMission(bool publish) nbMission += mc.getMissionsCount(); // publish files to selected servers - //if (publish) - //for (uint i=0 ; isettings(); + settings->beginGroup(MISSION_COMPILER_SECTION); - // compileLog += toString("\r\nPublishing to %s ...\r\n", ServerName[i].c_str()); - // for (uint j=0 ; jvalue(SETTING_LOCAL_TEXT_PATH).toString(); + settings->endGroup(); + QStringList checkedServers; + for(int i = 0; ipublishServersList->count(); i++) + { + + // Retrieve each checked server. + QListWidgetItem *item = ui->publishServersList->item(i); + if(item->checkState() == Qt::Checked) + checkedServers << item->text(); + } + + Q_FOREACH(QString checkedServer, checkedServers) + { + m_compileLog.append("Processing publication configuration for '"+checkedServer+"'\n"); + QStringList items = settings->value(SETTING_SERVERS_TABLE_ITEMS).toStringList(); + int column = 0; + int row = 0; + QString servName; + QString primPath; + QString textPath; + Q_FOREACH(QString var, items) + { + // Check to see if we're starting a new row. + if(column > 2) + { + column = 0; + row++; + } + if(column == 0) + servName = var; + else if(column == 1) + textPath = var; + else if(column == 2) + { + primPath = var; + + m_compileLog.append("Publishing to "+servName+" ...\n"); + for (uint j=0 ; j items = ui->publishServersList->findItems(server, Qt::MatchExactly); + if(items.size() != 1) + continue; items.at(0)->setCheckState(Qt::Checked); } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h index 1451b2613..1dffea313 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.h @@ -59,6 +59,7 @@ private: void moveSelectedItem(const QModelIndex &index, QStringListModel *from, QStringListModel *to); void applyCheckboxes(const QStringList &servers); + QMenu *_toolModeMenu; QUndoStack *m_undoStack; QStringListModel *m_allPrimitivesModel; From 12fe8693d5e7628bd7d4099fe31a8f3bb9433b68 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Sat, 23 Jul 2011 15:51:46 +0200 Subject: [PATCH 055/215] Changed: #1304: Revert modifications for the objectives in the world_editor_classes.xml (i changed the way i will do it) --HG-- branch : gsoc2011-guildmissions --- .../world_editor_classes.xml | 71 +------------------ 1 file changed, 1 insertion(+), 70 deletions(-) diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml index 238158bbf..7faaf1fcf 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml @@ -688,8 +688,6 @@ - - @@ -838,14 +836,6 @@ - - - - - - - - @@ -1108,8 +1098,6 @@ - - @@ -1122,10 +1110,6 @@ - - - - @@ -1135,45 +1119,28 @@ - - - - - - - - - + - - - - - - - - - @@ -1182,27 +1149,18 @@ - - - - - - - - - @@ -1212,9 +1170,6 @@ - - - @@ -1223,9 +1178,6 @@ - - - @@ -1233,9 +1185,6 @@ - - - @@ -1243,35 +1192,23 @@ - - - - - - - - - - - - @@ -1304,18 +1241,12 @@ - - - - From 9f05d66a81435ff102002333f92e196498b710e4 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Sat, 23 Jul 2011 15:56:32 +0200 Subject: [PATCH 056/215] Changed: #1304: Revert modifications for the objectives in the mission compiler (i changed the way i will do it) --HG-- branch : gsoc2011-guildmissions --- .../leveldesign/mission_compiler_lib/step.h | 4 +- .../mission_compiler_lib/step_content.cpp | 42 +++++++++---------- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h b/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h index 9d2a053df..fdb1db4be 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step.h @@ -194,9 +194,9 @@ protected: bool _HideObj; // Option nb_guild_members_needed, available for each objective - int _NbGuildMembersNeeded; + /*int _NbGuildMembersNeeded; - std::string genNbGuildMembersNeededOption(CMissionData &md); + std::string genNbGuildMembersNeededOption(CMissionData &md);*/ public: void init(CMissionData &md, NLLIGO::IPrimitive *prim); diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp index 442ca6fbd..1ff9b3b18 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp @@ -1762,7 +1762,7 @@ void CContentObjective::init(CMissionData &md, IPrimitive *prim) _RoleplayPhrase.initPhrase(md, prim, _RoleplayObj, numEntry, params); // check for the 'nb_guild_members_needed' option and see if it's correct for this mission - string nbGuildMembersNeeded = md.getProperty(prim, "nb_guild_members_needed", false, true); + /*string nbGuildMembersNeeded = md.getProperty(prim, "nb_guild_members_needed", false, true); if (nbGuildMembersNeeded.empty()) nbGuildMembersNeeded = "1"; if (!fromString(nbGuildMembersNeeded.c_str(), _NbGuildMembersNeeded)) @@ -1773,7 +1773,7 @@ void CContentObjective::init(CMissionData &md, IPrimitive *prim) { string err = toString("primitive(%s): nb_guild_members_needed != 1 for non guild mission.", prim->getName().c_str()); throw EParseException(prim, err.c_str()); - } + }*/ } // --------------------------------------------------------------------------- @@ -1794,7 +1794,7 @@ string CContentObjective::genCode(CMissionData &md) } // --------------------------------------------------------------------------- -std::string CContentObjective::genNbGuildMembersNeededOption(CMissionData &md) +/*std::string CContentObjective::genNbGuildMembersNeededOption(CMissionData &md) { string ret = ""; // If we are in a guild mission we add the 'nb_guild_members_needed' option to the script @@ -1805,7 +1805,7 @@ std::string CContentObjective::genNbGuildMembersNeededOption(CMissionData &md) } return ret; -} +}*/ // --------------------------------------------------------------------------- string CContentObjective::genPhrase() @@ -2109,7 +2109,7 @@ public: ret += " : "+_Place; // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2166,7 +2166,7 @@ public: if (!_Phrase.isEmpty()) ret += " : "+_Phrase.genScript(md); // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2242,7 +2242,7 @@ public: ret += ": "+_Place; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2317,7 +2317,7 @@ public: ret += "; "; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2444,7 +2444,7 @@ public: ret += "; "; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2519,7 +2519,7 @@ public: ret += "; "; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2651,7 +2651,7 @@ public: if (!_Place.empty()) ret += " : " + _Place; // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2737,7 +2737,7 @@ public: ret += " : "+_Npc; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2820,7 +2820,7 @@ public: ret += " : "+_Npc; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2933,7 +2933,7 @@ public: }; ret += " : "+_Npc; // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -2971,7 +2971,7 @@ public: ret += "give_money : "+_Amount+" : "+_Npc; // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -3024,7 +3024,7 @@ public: } } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -3061,7 +3061,7 @@ public: if (_SaveAll) ret += " : save_all"; // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -3135,7 +3135,7 @@ public: ret += "; "; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -3174,7 +3174,7 @@ public: ret += "; "; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -3213,7 +3213,7 @@ public: ret += " "; } // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; @@ -3430,7 +3430,7 @@ public: ret += "ring_scenario : "; ret += _ScenarioTag; // Add the 'nb_guild_members_needed' parameter if needed - ret += CContentObjective::genNbGuildMembersNeededOption(md); + //ret += CContentObjective::genNbGuildMembersNeededOption(md); ret += NL; return ret; From 78dbefb759dc0a78b6ce361a9423ec6fabfc97df Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Sat, 23 Jul 2011 18:14:31 +0200 Subject: [PATCH 057/215] Changed: #1304: Checks in the mission compiler if the do_mission objective is correct (for a guild mission) when we add a number (the number of members from the guild needed to complete the mission) after a mission name --HG-- branch : gsoc2011-guildmissions --- .../mission_compiler_lib/step_content.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp index 1ff9b3b18..dcd8bb9b3 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp @@ -3153,9 +3153,12 @@ class CContentMission: public CContentObjective } public: + CContentMission(): _Prim(0) {} + void init(CMissionData &md, IPrimitive *prim) { _Missions = md.getPropertyArray(prim, "mission_names", true, false); + _Prim = prim; CContentObjective::init(md, prim); } @@ -3172,6 +3175,14 @@ public: ret += _Missions[i]; if (i < _Missions.size()-1) ret += "; "; + + // We check to see if we specified a number after the mission name. If so, we check if it's a guild mission + std::size_t pos = _Missions[i].find_first_of(" \t"); + if (pos != std::string::npos && !md.isGuildMission()) + { + string err = toString("primitive(%s): CContentMission: Number of members needed to complete the mission specified but the mission is not a guild mission.", _Prim->getName().c_str()); + throw EParseException(_Prim, err.c_str()); + } } // Add the 'nb_guild_members_needed' parameter if needed //ret += CContentObjective::genNbGuildMembersNeededOption(md); @@ -3179,6 +3190,8 @@ public: return ret; } + + IPrimitive *_Prim; }; REGISTER_STEP_CONTENT(CContentMission, "do_mission"); From 6462021f14f696ba11fda2609d9d19653ba7549b Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 25 Jul 2011 12:27:19 +0200 Subject: [PATCH 058/215] Changed: #1304: Update of CMissionStepDoMissions to support missions done several times (by different guild members) --HG-- branch : gsoc2011-guildmissions --- .../mission_manager/mission_step_misc.cpp | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp index ff8496eba..2078c6c6a 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp @@ -1141,7 +1141,13 @@ MISSION_REGISTER_STEP(CMissionStepCast,"cast") // ---------------------------------------------------------------------------- class CMissionStepDoMissions : public IMissionStepTemplate { - std::vector< std::string > _Missions; + struct MissionNb + { + std::string Mission; + uint32 NbNeedCompletion; + }; + + std::vector< MissionNb > _Missions; virtual bool buildStep( uint32 line, const std::vector< std::string > & script, CMissionGlobalParsingData & globalData, CMissionSpecificParsingData & missionData ) { @@ -1158,7 +1164,13 @@ class CMissionStepDoMissions : public IMissionStepTemplate _Missions.resize(subs.size()); for ( uint i = 0; i < subs.size(); i++ ) { - _Missions[i] = CMissionParser::getNoBlankString( subs[i] ); + std::vector< std::string > params; + NLMISC::splitString( subs[i]," \t", params ); + _Missions[i].Mission = CMissionParser::getNoBlankString( params[0] ); + if (params.size() > 1) + NLMISC::fromString(params[1], _Missions[i].NbNeedCompletion); + else + _Missions[i].NbNeedCompletion = 1; } return true; } @@ -1168,7 +1180,7 @@ class CMissionStepDoMissions : public IMissionStepTemplate if ( event.Type == CMissionEvent::MissionDone ) { CMissionEventMissionDone & eventSpe = (CMissionEventMissionDone&)event; - TAIAlias alias = CAIAliasTranslator::getInstance()->getMissionUniqueIdFromName( _Missions[subStepIndex] ); + TAIAlias alias = CAIAliasTranslator::getInstance()->getMissionUniqueIdFromName( _Missions[subStepIndex].Mission ); if ( eventSpe.Mission == alias ) { LOGMISSIONSTEPSUCCESS("mission"); @@ -1181,6 +1193,12 @@ class CMissionStepDoMissions : public IMissionStepTemplate void getInitState( std::vector& ret ) { ret.resize( _Missions.size(), 1 ); + uint32 i = 0; + for (std::vector::const_iterator it = _Missions.begin(); it != _Missions.end(); ++it) + { + ret[i] = it->NbNeedCompletion; + i++; + } } virtual void getTextParams( uint & nbSubSteps,const std::string* & textPtr,TVectorParamCheck& retParams, const std::vector& subStepStates) From 1f057afe9193afe1129f983df56ec4951e4adb5d Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Mon, 25 Jul 2011 14:09:13 +0200 Subject: [PATCH 059/215] Changed: #1304: Message concerning the number of times a mission needs to be completed --HG-- branch : gsoc2011-guildmissions --- .../mission_manager/mission_step_misc.cpp | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp index 2078c6c6a..dc0be33d5 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp @@ -1203,7 +1203,26 @@ class CMissionStepDoMissions : public IMissionStepTemplate virtual void getTextParams( uint & nbSubSteps,const std::string* & textPtr,TVectorParamCheck& retParams, const std::vector& subStepStates) { - static const std::string stepText = "ERROR_UNSPECIFIED_MISSION_TEXT"; + /*static const std::string stepText = "ERROR_UNSPECIFIED_MISSION_TEXT"; + textPtr = &stepText;*/ + + // Because we can specify the number of times we want a mission to be completed, we specify the parameters + static const std::string stepText = "MIS_DO_MISSION_"; + nlassert( _Missions.size() == subStepStates.size() ); + for ( uint i = 0; i < subStepStates.size(); i++ ) + { + if( subStepStates[i] != 0 ) + { + nbSubSteps++; + retParams.push_back(STRING_MANAGER::TParam()); + retParams.back().Type = STRING_MANAGER::integer; + retParams.back().Int = subStepStates[i]; + + retParams.push_back(STRING_MANAGER::TParam()); + retParams.back().Type = STRING_MANAGER::literal; + retParams.back().Literal = _Missions[i].Mission; + } + } textPtr = &stepText; } bool checkTextConsistency() From 6fb8d197cf1ecb24c914d4e406aabffa948c3eb1 Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 27 Jul 2011 01:01:00 +0300 Subject: [PATCH 060/215] Changed: #1307 Added the base files for phrase editor and new method to save the editor type --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/CMakeLists.txt | 1 + .../translation_manager/editor_phrase.cpp.txt | 53 ++++++++++++++++++ .../translation_manager/editor_phrase.h.txt | 48 ++++++++++++++++ .../translation_manager/editor_worksheet.cpp | 1 + .../translation_manager_constants.h | 3 + .../translation_manager_editor.h | 6 +- .../translation_manager_main_window.cpp | 56 +++++++++++++------ 7 files changed, 150 insertions(+), 18 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp.txt create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h.txt diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt index 7e7da7e99..0f520c9d8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -16,6 +16,7 @@ SET(OVQT_PLUG_TRANSLATION_MANAGER_HDR translation_manager_plugin.h source_selection.h ftp_selection.h editor_worksheet.h + editor_phrase.h extract_new_sheet_names.h extract_bot_names.h) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp.txt new file mode 100644 index 000000000..83d360d8a --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp.txt @@ -0,0 +1,53 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Qt includes +#include +#include +#include +#include + +// Project includes +#include "editor_phrase.h" +#include "translation_manager_constants.h" + +using namespace std; + +namespace Plugin { + +void CEditorPhrase::open(QString filename) +{ + +} + +void CEditorPhrase::activateWindow() +{ + +} + +void CEditorPhrase::save() +{ + +} + +void CEditorPhrase::saveAs(QString filename) +{ + +} + + +} \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h.txt new file mode 100644 index 000000000..539314eaf --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h.txt @@ -0,0 +1,48 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef EDITOR_PHRASE_H +#define EDITOR_PHRASE_H + +// Qt includes +#include +#include +#include +#include +#include +#include + +// Project includes +#include "translation_manager_editor.h" + +namespace Plugin { + +class CEditorPhrase : public CEditor +{ + Q_OBJECT +public: + CEditorPhrase(QMdiArea* parent) : CEditor(parent) {} + CEditorPhrase() : CEditor() {} + void open(QString filename); + void save(); + void saveAs(QString filename); + void activateWindow(); +}; + +} + +#endif /* EDITOR_PHRASE_H */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index 5323ded1a..2c7261805 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -93,6 +93,7 @@ void CEditorWorksheet::open(QString filename) setCurrentFile(filename); setAttribute(Qt::WA_DeleteOnClose); setWidget(table_editor); + editor_type = Constants::ED_SHEET; table_editor->resizeColumnsToContents(); table_editor->resizeRowsToContents(); // set editor signals diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h index b6bdc8315..3cb66181b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h @@ -12,6 +12,9 @@ namespace Plugin { namespace Constants { + const int ED_SHEET = 1; + const int ED_PHRASE = 2; + const char * const WK_BOTNAMES = "bot_names_wk.txt"; const char * const WK_ITEM = "item_words_wk.txt"; const char * const WK_CREATURE = "creature_words_wk.txt"; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h index ff947b1d1..767dd90de 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h @@ -31,7 +31,7 @@ Q_OBJECT protected: QUndoStack* current_stack; QString current_file; - int editor_type; + int editor_type; public: CEditor(QMdiArea* parent) : QMdiSubWindow(parent) {} CEditor() : QMdiSubWindow() {} @@ -40,6 +40,10 @@ public: virtual void saveAs(QString filename) =0; virtual void activateWindow() =0; public: + int eType() + { + return editor_type; + } QString subWindowFilePath() { return current_file; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 52e423156..e6fa9508a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -206,6 +206,10 @@ void CMainWindow::open() editor->activateWindow(); return; } + #ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(Qt::WaitCursor); + #endif + // sheet editor if(isWorksheetEditor(file_name)) { CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); @@ -213,6 +217,9 @@ void CMainWindow::open() new_window->open(file_name); new_window->activateWindow(); } + #ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); + #endif } } @@ -241,11 +248,13 @@ void CMainWindow::save() if(_ui.mdiArea->subWindowList().size() > 0) { CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - - if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor - { - current_window->save(); - } + #ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(Qt::WaitCursor); + #endif + current_window->save(); + #ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); + #endif } } @@ -258,13 +267,15 @@ void CMainWindow::saveAs() } if (!file_name.isEmpty()) - { - CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - if(QString(current_window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor - { - current_window->saveAs(file_name); - } - + { + CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + #ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(Qt::WaitCursor); + #endif + current_window->saveAs(file_name); + #ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); + #endif } } @@ -341,14 +352,19 @@ void CMainWindow::extractWords(QString typeq) builderP.PrimFilter.push_back("indoors_*.primitive"); isSheet = false; } - + #ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(Qt::WaitCursor); + #endif if(isSheet) { editor_window->extractWords(editor_window->windowFilePath(), column_name, builderS); } else { initializeSettings(false); editor_window->extractWords(editor_window->windowFilePath(), column_name, builderP); - } + } + #ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); + #endif } } @@ -371,8 +387,14 @@ void CMainWindow::extractBotNames() QString file_path = editor_window->windowFilePath(); } else return; } + #ifndef QT_NO_CURSOR + QApplication::setOverrideCursor(Qt::WaitCursor); + #endif initializeSettings(true); - editor_window->extractBotNames(convertQStringList(filters), level_design_path.toStdString(), ligoConfig); + editor_window->extractBotNames(convertQStringList(filters), level_design_path.toStdString(), ligoConfig); + #ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); + #endif } } @@ -392,7 +414,7 @@ void CMainWindow::mergeSingleFile() return; } - if(QString(editor_window->widget()->metaObject()->className()) != "QTableWidget") // Sheet Editor + if(editor_window->eType() != Constants::ED_SHEET) // Sheet Editor { QErrorMessage error; error.showMessage(QString("Please open or activate the window with a sheet file.")); @@ -500,7 +522,7 @@ CEditorWorksheet *CMainWindow::getEditorByWorksheetType(const QString &type) Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) { CEditor *currentEditor = qobject_cast(subWindow); - if(QString(currentEditor->widget()->metaObject()->className()) == "QTableWidget") + if(currentEditor->eType() == Constants::ED_SHEET) { CEditorWorksheet *editor = qobject_cast(currentEditor); if(type != NULL) { From bd2768ec89784b908fc66de065a08c4b2047c8b6 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 3 Aug 2011 07:51:10 -0500 Subject: [PATCH 062/215] Added: 3rd party library - Qt Property Browser 2.5 --HG-- branch : branch_ovqt_3rdparty --- code/CMakeModules/nel.cmake | 4 +- .../src/3rdparty/CMakeLists.txt | 1 + .../3rdparty/qtpropertybrowser/CMakeLists.txt | 89 + .../qtpropertybrowser/LGPL_EXCEPTION.txt | 10 + .../3rdparty/qtpropertybrowser/LICENSE.GPL3 | 674 ++ .../3rdparty/qtpropertybrowser/LICENSE.LGPL | 504 ++ .../QtAbstractEditorFactoryBase | 1 + .../QtAbstractPropertyBrowser | 1 + .../QtAbstractPropertyManager | 1 + .../qtpropertybrowser/QtBoolPropertyManager | 1 + .../3rdparty/qtpropertybrowser/QtBrowserItem | 1 + .../qtpropertybrowser/QtButtonPropertyBrowser | 1 + .../qtpropertybrowser/QtCharEditorFactory | 1 + .../qtpropertybrowser/QtCharPropertyManager | 1 + .../qtpropertybrowser/QtCheckBoxFactory | 1 + .../qtpropertybrowser/QtColorEditorFactory | 1 + .../qtpropertybrowser/QtColorPropertyManager | 1 + .../qtpropertybrowser/QtCursorEditorFactory | 1 + .../qtpropertybrowser/QtCursorPropertyManager | 1 + .../qtpropertybrowser/QtDateEditFactory | 1 + .../qtpropertybrowser/QtDatePropertyManager | 1 + .../qtpropertybrowser/QtDateTimeEditFactory | 1 + .../QtDateTimePropertyManager | 1 + .../qtpropertybrowser/QtDoublePropertyManager | 1 + .../qtpropertybrowser/QtDoubleSpinBoxFactory | 1 + .../qtpropertybrowser/QtEnumEditorFactory | 1 + .../qtpropertybrowser/QtEnumPropertyManager | 1 + .../qtpropertybrowser/QtFlagPropertyManager | 1 + .../qtpropertybrowser/QtFontEditorFactory | 1 + .../qtpropertybrowser/QtFontPropertyManager | 1 + .../QtGroupBoxPropertyBrowser | 1 + .../qtpropertybrowser/QtGroupPropertyManager | 1 + .../qtpropertybrowser/QtIntPropertyManager | 1 + .../QtKeySequenceEditorFactory | 1 + .../QtKeySequencePropertyManager | 1 + .../qtpropertybrowser/QtLineEditFactory | 1 + .../qtpropertybrowser/QtLocalePropertyManager | 1 + .../qtpropertybrowser/QtPointFPropertyManager | 1 + .../qtpropertybrowser/QtPointPropertyManager | 1 + .../src/3rdparty/qtpropertybrowser/QtProperty | 1 + .../qtpropertybrowser/QtRectFPropertyManager | 1 + .../qtpropertybrowser/QtRectPropertyManager | 1 + .../qtpropertybrowser/QtScrollBarFactory | 1 + .../qtpropertybrowser/QtSizeFPropertyManager | 1 + .../QtSizePolicyPropertyManager | 1 + .../qtpropertybrowser/QtSizePropertyManager | 1 + .../qtpropertybrowser/QtSliderFactory | 1 + .../qtpropertybrowser/QtSpinBoxFactory | 1 + .../qtpropertybrowser/QtStringPropertyManager | 1 + .../qtpropertybrowser/QtTimeEditFactory | 1 + .../qtpropertybrowser/QtTimePropertyManager | 1 + .../qtpropertybrowser/QtTreePropertyBrowser | 1 + .../qtpropertybrowser/QtVariantEditorFactory | 1 + .../qtpropertybrowser/QtVariantProperty | 1 + .../QtVariantPropertyManager | 1 + .../src/3rdparty/qtpropertybrowser/README.TXT | 19 + .../qtpropertybrowser/images/cursor-arrow.png | Bin 0 -> 171 bytes .../qtpropertybrowser/images/cursor-busy.png | Bin 0 -> 201 bytes .../images/cursor-closedhand.png | Bin 0 -> 147 bytes .../qtpropertybrowser/images/cursor-cross.png | Bin 0 -> 130 bytes .../images/cursor-forbidden.png | Bin 0 -> 199 bytes .../qtpropertybrowser/images/cursor-hand.png | Bin 0 -> 159 bytes .../images/cursor-hsplit.png | Bin 0 -> 155 bytes .../qtpropertybrowser/images/cursor-ibeam.png | Bin 0 -> 124 bytes .../images/cursor-openhand.png | Bin 0 -> 160 bytes .../images/cursor-sizeall.png | Bin 0 -> 174 bytes .../qtpropertybrowser/images/cursor-sizeb.png | Bin 0 -> 161 bytes .../qtpropertybrowser/images/cursor-sizef.png | Bin 0 -> 161 bytes .../qtpropertybrowser/images/cursor-sizeh.png | Bin 0 -> 145 bytes .../qtpropertybrowser/images/cursor-sizev.png | Bin 0 -> 141 bytes .../images/cursor-uparrow.png | Bin 0 -> 132 bytes .../images/cursor-vsplit.png | Bin 0 -> 161 bytes .../qtpropertybrowser/images/cursor-wait.png | Bin 0 -> 172 bytes .../images/cursor-whatsthis.png | Bin 0 -> 191 bytes .../qtbuttonpropertybrowser.cpp | 676 ++ .../qtbuttonpropertybrowser.h | 135 + .../qtpropertybrowser/qteditorfactory.cpp | 2609 +++++++ .../qtpropertybrowser/qteditorfactory.h | 448 ++ .../qtgroupboxpropertybrowser.cpp | 578 ++ .../qtgroupboxpropertybrowser.h | 126 + .../qtpropertybrowser/qtpropertybrowser.cpp | 2053 ++++++ .../qtpropertybrowser/qtpropertybrowser.h | 379 + .../qtpropertybrowser/qtpropertybrowser.pri | 30 + .../qtpropertybrowser/qtpropertybrowser.qrc | 23 + .../qtpropertybrowserutils.cpp | 477 ++ .../qtpropertybrowserutils_p.h | 207 + .../qtpropertybrowser/qtpropertymanager.cpp | 6465 +++++++++++++++++ .../qtpropertybrowser/qtpropertymanager.h | 796 ++ .../qttreepropertybrowser.cpp | 1095 +++ .../qtpropertybrowser/qttreepropertybrowser.h | 184 + .../qtpropertybrowser/qtvariantproperty.cpp | 2329 ++++++ .../qtpropertybrowser/qtvariantproperty.h | 232 + .../3d/object_viewer_qt/src/CMakeLists.txt | 2 + 93 files changed, 20192 insertions(+), 2 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/CMakeLists.txt create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LGPL_EXCEPTION.txt create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LICENSE.GPL3 create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LICENSE.LGPL create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractEditorFactoryBase create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractPropertyBrowser create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtBoolPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtBrowserItem create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtButtonPropertyBrowser create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCharEditorFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCharPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCheckBoxFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtColorEditorFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtColorPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCursorEditorFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCursorPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateEditFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDatePropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateTimeEditFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateTimePropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDoublePropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDoubleSpinBoxFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtEnumEditorFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtEnumPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFlagPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFontEditorFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFontPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtGroupBoxPropertyBrowser create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtGroupPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtIntPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtKeySequenceEditorFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtKeySequencePropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtLineEditFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtLocalePropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtPointFPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtPointPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtProperty create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtRectFPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtRectPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtScrollBarFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizeFPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizePolicyPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizePropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSliderFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSpinBoxFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtStringPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTimeEditFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTimePropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTreePropertyBrowser create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantEditorFactory create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantProperty create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantPropertyManager create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/README.TXT create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-arrow.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-busy.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-closedhand.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-cross.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-forbidden.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-hand.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-hsplit.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-ibeam.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-openhand.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizeall.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizeb.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizef.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizeh.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizev.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-uparrow.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-vsplit.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-wait.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-whatsthis.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtbuttonpropertybrowser.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtbuttonpropertybrowser.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qteditorfactory.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qteditorfactory.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtgroupboxpropertybrowser.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtgroupboxpropertybrowser.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.pri create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.qrc create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowserutils.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowserutils_p.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertymanager.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertymanager.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qttreepropertybrowser.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qttreepropertybrowser.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtvariantproperty.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtvariantproperty.h diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index d2a5cb014..14a78d721 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -529,8 +529,8 @@ MACRO(SETUP_EXTERNAL) SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") ELSE(WITH_STATIC_EXTERNAL) SET(CMAKE_FIND_LIBRARY_SUFFIXES ".so") - ENDIF(WITH_STATIC_EXTERNAL AND NOT APPLE) - ENDIF(CMAKE_FIND_LIBRARY_SUFFIXES) + ENDIF(WITH_STATIC_EXTERNAL) + ENDIF(CMAKE_FIND_LIBRARY_SUFFIXES AND NOT APPLE) ENDIF(WIN32) IF(WITH_STLPORT) diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/CMakeLists.txt new file mode 100644 index 000000000..2caaa1be7 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/CMakeLists.txt @@ -0,0 +1 @@ +ADD_SUBDIRECTORY(qtpropertybrowser) diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt new file mode 100644 index 000000000..8fb0881af --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt @@ -0,0 +1,89 @@ +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${QT_INCLUDES}) +INCLUDE(${QT_USE_FILE}) + +FILE(GLOB SRC *.cpp *.h) + +SET(QT_PROPERTY_EDITOR_HDR qtpropertybrowser.h + qtpropertymanager.h + qteditorfactory.h + qtvariantproperty.h + qttreepropertybrowser.h + qtbuttonpropertybrowser.h + qtgroupboxpropertybrowser.h + qtpropertybrowserutils_p.h) + +SET(QT_PROPERTY_EDITOR_RCS qtpropertybrowser.qrc) + +SET(QT_USE_QTGUI TRUE) + +QT4_ADD_RESOURCES(QT_PROPERTY_EDITOR_RC_SRCS ${QT_PROPERTY_EDITOR_RCS}) +QT4_WRAP_CPP(QT_PROPERTY_EDITOR_MOC_SRC ${QT_PROPERTY_EDITOR_HDR}) + +SOURCE_GROUP(QtResources FILES ${QT_PROPERTY_EDITOR_RCS}) +SOURCE_GROUP(QtGeneratedMocSrc FILES ${QT_PROPERTY_EDITOR_MOC_SRC}) +SOURCE_GROUP("Qt Property Editor Source" FILES ${SRC}) + +qt4_generate_moc(qtpropertymanager.cpp ${CMAKE_CURRENT_BINARY_DIR}/qtpropertymanager.moc) +qt4_generate_moc(qteditorfactory.cpp ${CMAKE_CURRENT_BINARY_DIR}/qteditorfactory.moc) +qt4_generate_moc(qttreepropertybrowser.cpp ${CMAKE_CURRENT_BINARY_DIR}/qttreepropertybrowser.moc) + +# Need to remove these so that they are not linked as they are inline included. +LIST(REMOVE_ITEM QT_PROPERTY_EDITOR_MOC_SRC ${CMAKE_CURRENT_BINARY_DIR}/moc_qtbuttonpropertybrowser.cxx + ${CMAKE_CURRENT_BINARY_DIR}/moc_qteditorfactory.cxx + ${CMAKE_CURRENT_BINARY_DIR}/moc_qtgroupboxpropertybrowser.cxx + ${CMAKE_CURRENT_BINARY_DIR}/moc_qtpropertybrowser.cxx + ${CMAKE_CURRENT_BINARY_DIR}/moc_qtpropertymanager.cxx + ${CMAKE_CURRENT_BINARY_DIR}/moc_qttreepropertybrowser.cxx + ${CMAKE_CURRENT_BINARY_DIR}/moc_qtvariantproperty.cxx) + +#set( +# qtpropertyeditor_HEADERS_ONLY_MOC +# ${CMAKE_CURRENT_BINARY_DIR}/moc_qtpropertybrowser.cpp +# ${CMAKE_CURRENT_BINARY_DIR}/moc_qtpropertymanager.cpp +# ${CMAKE_CURRENT_BINARY_DIR}/moc_qteditorfactory.cpp +# ${CMAKE_CURRENT_BINARY_DIR}/moc_qtvariantproperty.cpp +# ${CMAKE_CURRENT_BINARY_DIR}/moc_qttreepropertybrowser.cpp +# ${CMAKE_CURRENT_BINARY_DIR}/moc_qtbuttonpropertybrowser.cpp +# ${CMAKE_CURRENT_BINARY_DIR}/moc_qtgroupboxpropertybrowser.cpp +# ${CMAKE_CURRENT_BINARY_DIR}/qtpropertymanager.moc +# ${CMAKE_CURRENT_BINARY_DIR}/qteditorfactory.moc +# ${CMAKE_CURRENT_BINARY_DIR}/qttreepropertybrowser.moc +#) +# +#set_source_files_properties( +# ${qtpropertyeditor_HEADERS_ONLY_MOC} +# PROPERTIES +# HEADER_FILE_ONLY true +#) +# + +#set( +# qtpropertyeditor_HEADERS_MOC +# ${CMAKE_CURRENT_BINARY_DIR}/moc_qtpropertybrowserutils_p.cpp +#) + +#set( +# qtpropertyeditor_MOC +# ${qtpropertyeditor_HEADERS_MOC} +# ${qtpropertyeditor_HEADERS_ONLY_MOC} +#) + +ADD_LIBRARY(qt_property_browser SHARED ${SRC} + ${QT_PROPERTY_EDITOR_MOC_SRC} + ${QT_PROPERTY_EDITOR_RC_SRCS} + ${CMAKE_CURRENT_BINARY_DIR}/qtpropertymanager.moc + ${CMAKE_CURRENT_BINARY_DIR}/qttreepropertybrowser.moc + ${CMAKE_CURRENT_BINARY_DIR}/qteditorfactory.moc) + +TARGET_LINK_LIBRARIES(qt_property_browser ${QT_LIBRARIES}) + +ADD_DEFINITIONS(${QT_DEFINITIONS}) +ADD_DEFINITIONS(-DQT_DLL) +#ADD_DEFINITIONS(-DQT_QTPROPERTYBROWSER_EXPORT) +ADD_DEFINITIONS(-DQT_PLUGIN) +#ADD_DEFINITIONS(-DQT_NO_DEBUG) +ADD_DEFINITIONS(-DQT_SHARED) + +NL_DEFAULT_PROPS(qt_property_browser "3rdParty: Qt Property Browser 2.5") +NL_ADD_RUNTIME_FLAGS(qt_property_browser) +NL_ADD_LIB_SUFFIX(qt_property_browser) diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LGPL_EXCEPTION.txt b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LGPL_EXCEPTION.txt new file mode 100644 index 000000000..0b56ff1ef --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LGPL_EXCEPTION.txt @@ -0,0 +1,10 @@ +Nokia Qt LGPL Exception version 1.0 + +As a special exception to the GNU Lesser General Public License +version 2.1, the object code form of a "work that uses the Library" +may incorporate material from a header file that is part of the +Library. You may distribute such object code under terms of your +choice, provided that the incorporated material (i) does not exceed +more than 5% of the total size of the Library; and (ii) is limited to +numerical parameters, data structure layouts, accessors, macros, +inline functions and templates. diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LICENSE.GPL3 b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LICENSE.GPL3 new file mode 100644 index 000000000..94a9ed024 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LICENSE.GPL3 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LICENSE.LGPL b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LICENSE.LGPL new file mode 100644 index 000000000..5ab7695ab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/LICENSE.LGPL @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractEditorFactoryBase b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractEditorFactoryBase new file mode 100644 index 000000000..ab4e7104a --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractEditorFactoryBase @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractPropertyBrowser b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractPropertyBrowser new file mode 100644 index 000000000..ab4e7104a --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractPropertyBrowser @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractPropertyManager new file mode 100644 index 000000000..ab4e7104a --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtAbstractPropertyManager @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtBoolPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtBoolPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtBoolPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtBrowserItem b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtBrowserItem new file mode 100644 index 000000000..ab4e7104a --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtBrowserItem @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtButtonPropertyBrowser b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtButtonPropertyBrowser new file mode 100644 index 000000000..56e089704 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtButtonPropertyBrowser @@ -0,0 +1 @@ +#include "qtbuttonpropertybrowser.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCharEditorFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCharEditorFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCharEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCharPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCharPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCharPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCheckBoxFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCheckBoxFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCheckBoxFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtColorEditorFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtColorEditorFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtColorEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtColorPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtColorPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtColorPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCursorEditorFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCursorEditorFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCursorEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCursorPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCursorPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtCursorPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateEditFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateEditFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateEditFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDatePropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDatePropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDatePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateTimeEditFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateTimeEditFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateTimeEditFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateTimePropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateTimePropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDateTimePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDoublePropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDoublePropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDoublePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDoubleSpinBoxFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDoubleSpinBoxFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtDoubleSpinBoxFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtEnumEditorFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtEnumEditorFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtEnumEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtEnumPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtEnumPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtEnumPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFlagPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFlagPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFlagPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFontEditorFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFontEditorFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFontEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFontPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFontPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtFontPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtGroupBoxPropertyBrowser b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtGroupBoxPropertyBrowser new file mode 100644 index 000000000..27964c080 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtGroupBoxPropertyBrowser @@ -0,0 +1 @@ +#include "qtgroupboxpropertybrowser.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtGroupPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtGroupPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtGroupPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtIntPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtIntPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtIntPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtKeySequenceEditorFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtKeySequenceEditorFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtKeySequenceEditorFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtKeySequencePropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtKeySequencePropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtKeySequencePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtLineEditFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtLineEditFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtLineEditFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtLocalePropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtLocalePropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtLocalePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtPointFPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtPointFPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtPointFPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtPointPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtPointPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtPointPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtProperty b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtProperty new file mode 100644 index 000000000..ab4e7104a --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtProperty @@ -0,0 +1 @@ +#include "qtpropertybrowser.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtRectFPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtRectFPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtRectFPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtRectPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtRectPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtRectPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtScrollBarFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtScrollBarFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtScrollBarFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizeFPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizeFPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizeFPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizePolicyPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizePolicyPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizePolicyPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizePropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizePropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSizePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSliderFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSliderFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSliderFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSpinBoxFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSpinBoxFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtSpinBoxFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtStringPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtStringPropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtStringPropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTimeEditFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTimeEditFactory new file mode 100644 index 000000000..75f35adab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTimeEditFactory @@ -0,0 +1 @@ +#include "qteditorfactory.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTimePropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTimePropertyManager new file mode 100644 index 000000000..1842e431d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTimePropertyManager @@ -0,0 +1 @@ +#include "qtpropertymanager.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTreePropertyBrowser b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTreePropertyBrowser new file mode 100644 index 000000000..aab106c75 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtTreePropertyBrowser @@ -0,0 +1 @@ +#include "qttreepropertybrowser.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantEditorFactory b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantEditorFactory new file mode 100644 index 000000000..8118190d5 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantEditorFactory @@ -0,0 +1 @@ +#include "qtvariantproperty.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantProperty b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantProperty new file mode 100644 index 000000000..8118190d5 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantProperty @@ -0,0 +1 @@ +#include "qtvariantproperty.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantPropertyManager b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantPropertyManager new file mode 100644 index 000000000..8118190d5 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/QtVariantPropertyManager @@ -0,0 +1 @@ +#include "qtvariantproperty.h" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/README.TXT b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/README.TXT new file mode 100644 index 000000000..d9452cfc1 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/README.TXT @@ -0,0 +1,19 @@ +Property Browser v2.5 + +A property browser framework enabling the user to edit a set of +properties. + +The framework provides a browser widget that displays the given +properties with labels and corresponding editing widgets (e.g. +line edits or comboboxes). The various types of editing widgets +are provided by the framework's editor factories: For each +property type, the framework provides a property manager (e.g. +QtIntPropertyManager and QtStringPropertyManager) which can be +associated with the preferred editor factory (e.g. +QtSpinBoxFactory and QtLineEditFactory). The framework also +provides a variant based property type with corresponding variant +manager and factory. Finally, the framework provides three +ready-made implementations of the browser widget: +QtTreePropertyBrowser, QtButtonPropertyBrowser and +QtGroupBoxPropertyBrowser. + diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-arrow.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..a69ef4eb6158503c7c67c916aea86e65fdc81f72 GIT binary patch literal 171 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz2@N{tusfe>Zw~@EOfQR+sk_Oou&-fjcEVpj<&$!X3+c~E>J1*JV{%`IM zZgwlFFx}G*?uS>UO1?P#t>x;u4TbmfTg6u$kN3O$qTon=;uum9xAyEt-c|zv=78lpdFvQ2-{H`#GwYbpmwD+D`;ru|4Sly6 z!u|DfH10id;EhPQ?!aC0$4imznq|kzoDQ|k!q4v~%5Ey$Ds|~#XU>6Rfw7|8GuZ2t ub|G literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-closedhand.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-closedhand.png new file mode 100644 index 0000000000000000000000000000000000000000..b78dd1dac5a827fa698f1993718f22c282019505 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6I14-?iy0WWJ3*My{N(AiKtWee z7sn8d^J_1j=3-FbaJl&N|NMQrDg{-+lDl3uEOL6iyxxIhJsV5IbqBU04WsP|EmPEI sG+%tqc!TF=!@MiLH_pW!e7ccq3Qxekuk)up2O7xW>FVdQ&MBb@057mJumAu6 literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-cross.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-cross.png new file mode 100644 index 0000000000000000000000000000000000000000..fe38e744805f61abefcc555e07d399725c3fd7e6 GIT binary patch literal 130 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz=_jGX#;h346aDZ1vLF6&ZrWY3kt_3XM>{%UFuy$6%gUPvacWfgV8N9#n V_FPj?S`E~~;OXk;vd$@?2>`R9Dxv@Y literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-forbidden.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-forbidden.png new file mode 100644 index 0000000000000000000000000000000000000000..2b08c4e2a3cafc992459a8f484e6c8e6c3e74857 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMr#O@pN$vsfa5*w~^P`kb@;)w}Ny_&ztDY@kvlRIOU`3qAsV; zTo+`Y{*UQrTXjS>sKxPHd$OYcrG25=M&E9}?0tT1Yy7oqDMB$hU$uYoMqOpDnka4~ qygb6(Z%*F(qx}EZO#W7L6_Nk?#pi+cGI+ZBxvX&}>iqs75rOW+zd6_S+ yS|~6}xHpZ9x7T5rLt)D22WQ%mvv4FO#t=)GwlEX literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-ibeam.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-ibeam.png new file mode 100644 index 0000000000000000000000000000000000000000..097fc5fa7287da71ffd907b3a11adbda4516aca6 GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqgTe~DWM4f DkdQT7 literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizeall.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizeall.png new file mode 100644 index 0000000000000000000000000000000000000000..69f13eb347a6c299e06844729a14f657b282fe8f GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq(Z!J(8lD*)YR5__{zaA2hTRnmr4+Dcjj*u+}&-pB%i42~uelF{r5}E+x CJu-a& literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizef.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizef.png new file mode 100644 index 0000000000000000000000000000000000000000..3b127a05d34b48a2f4f9b9cc77b681c6b43afcdd GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqD?iBlbDB7>)^pUXO@geCxo C3NS_h literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizeh.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizeh.png new file mode 100644 index 0000000000000000000000000000000000000000..a9f40cbc3d77c566c11c32c0631b4f94f44dd441 GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42Qq&V7RQzr9bPir8&?922WQ%mvv4FO#sHOC(Zx> literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizev.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-sizev.png new file mode 100644 index 0000000000000000000000000000000000000000..1edbab27a5b05555aaf515931f69ad6bf7e417f0 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK)l42QqM`U1 literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-wait.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-wait.png new file mode 100644 index 0000000000000000000000000000000000000000..69056c479e9b2f009e366dfd71999a7c74f97620 GIT binary patch literal 172 zcmeAS@N?(olHy`uVBq!ia0vp^k|4~)3?z59=Y9ZEoB=)|uK&RR$m{!Z_zjR^ED7=p zW^j0RBMrz2^mK6y;h346;J_ZVI&3Y=<%J7po~YS&;ev>WRgqc?pLgqp;N^OZ7kj)9 zt2OYRn)8&w&_Fn;AZ-e>OWiG&j$ap41wJoZG>h5TTvwNMesusN!}2q-U)D!*O8^aL N@O1TaS?83{1OR$JJ0<`C literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-whatsthis.png b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/images/cursor-whatsthis.png new file mode 100644 index 0000000000000000000000000000000000000000..b47601c3780eec780fdae43bab7481bbfebdddae GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnH3?%tPCZz)@&H$ef*Z*JuL59RLGuTpf8VsdEq+OT~Tw87C;38om{D5bL fNHFu^21W*zFY +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtButtonPropertyBrowserPrivate +{ + QtButtonPropertyBrowser *q_ptr; + Q_DECLARE_PUBLIC(QtButtonPropertyBrowser) +public: + + void init(QWidget *parent); + + void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); + void propertyRemoved(QtBrowserItem *index); + void propertyChanged(QtBrowserItem *index); + QWidget *createEditor(QtProperty *property, QWidget *parent) const + { return q_ptr->createEditor(property, parent); } + + void slotEditorDestroyed(); + void slotUpdate(); + void slotToggled(bool checked); + + struct WidgetItem + { + WidgetItem() : widget(0), label(0), widgetLabel(0), + button(0), container(0), layout(0), /*line(0), */parent(0), expanded(false) { } + QWidget *widget; // can be null + QLabel *label; // main label with property name + QLabel *widgetLabel; // label substitute showing the current value if there is no widget + QToolButton *button; // expandable button for items with children + QWidget *container; // container which is expanded when the button is clicked + QGridLayout *layout; // layout in container + WidgetItem *parent; + QList children; + bool expanded; + }; +private: + void updateLater(); + void updateItem(WidgetItem *item); + void insertRow(QGridLayout *layout, int row) const; + void removeRow(QGridLayout *layout, int row) const; + int gridRow(WidgetItem *item) const; + int gridSpan(WidgetItem *item) const; + void setExpanded(WidgetItem *item, bool expanded); + QToolButton *createButton(QWidget *panret = 0) const; + + QMap m_indexToItem; + QMap m_itemToIndex; + QMap m_widgetToItem; + QMap m_buttonToItem; + QGridLayout *m_mainLayout; + QList m_children; + QList m_recreateQueue; +}; + +QToolButton *QtButtonPropertyBrowserPrivate::createButton(QWidget *parent) const +{ + QToolButton *button = new QToolButton(parent); + button->setCheckable(true); + button->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed)); + button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + button->setArrowType(Qt::DownArrow); + button->setIconSize(QSize(3, 16)); + /* + QIcon icon; + icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowDown), QIcon::Normal, QIcon::Off); + icon.addPixmap(q_ptr->style()->standardPixmap(QStyle::SP_ArrowUp), QIcon::Normal, QIcon::On); + button->setIcon(icon); + */ + return button; +} + +int QtButtonPropertyBrowserPrivate::gridRow(WidgetItem *item) const +{ + QList siblings; + if (item->parent) + siblings = item->parent->children; + else + siblings = m_children; + + int row = 0; + QListIterator it(siblings); + while (it.hasNext()) { + WidgetItem *sibling = it.next(); + if (sibling == item) + return row; + row += gridSpan(sibling); + } + return -1; +} + +int QtButtonPropertyBrowserPrivate::gridSpan(WidgetItem *item) const +{ + if (item->container && item->expanded) + return 2; + return 1; +} + +void QtButtonPropertyBrowserPrivate::init(QWidget *parent) +{ + m_mainLayout = new QGridLayout(); + parent->setLayout(m_mainLayout); + QLayoutItem *item = new QSpacerItem(0, 0, + QSizePolicy::Fixed, QSizePolicy::Expanding); + m_mainLayout->addItem(item, 0, 0); +} + +void QtButtonPropertyBrowserPrivate::slotEditorDestroyed() +{ + QWidget *editor = qobject_cast(q_ptr->sender()); + if (!editor) + return; + if (!m_widgetToItem.contains(editor)) + return; + m_widgetToItem[editor]->widget = 0; + m_widgetToItem.remove(editor); +} + +void QtButtonPropertyBrowserPrivate::slotUpdate() +{ + QListIterator itItem(m_recreateQueue); + while (itItem.hasNext()) { + WidgetItem *item = itItem.next(); + + WidgetItem *parent = item->parent; + QWidget *w = 0; + QGridLayout *l = 0; + const int oldRow = gridRow(item); + if (parent) { + w = parent->container; + l = parent->layout; + } else { + w = q_ptr; + l = m_mainLayout; + } + + int span = 1; + if (!item->widget && !item->widgetLabel) + span = 2; + item->label = new QLabel(w); + item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + l->addWidget(item->label, oldRow, 0, 1, span); + + updateItem(item); + } + m_recreateQueue.clear(); +} + +void QtButtonPropertyBrowserPrivate::setExpanded(WidgetItem *item, bool expanded) +{ + if (item->expanded == expanded) + return; + + if (!item->container) + return; + + item->expanded = expanded; + const int row = gridRow(item); + WidgetItem *parent = item->parent; + QGridLayout *l = 0; + if (parent) + l = parent->layout; + else + l = m_mainLayout; + + if (expanded) { + insertRow(l, row + 1); + l->addWidget(item->container, row + 1, 0, 1, 2); + item->container->show(); + } else { + l->removeWidget(item->container); + item->container->hide(); + removeRow(l, row + 1); + } + + item->button->setChecked(expanded); + item->button->setArrowType(expanded ? Qt::UpArrow : Qt::DownArrow); +} + +void QtButtonPropertyBrowserPrivate::slotToggled(bool checked) +{ + WidgetItem *item = m_buttonToItem.value(q_ptr->sender()); + if (!item) + return; + + setExpanded(item, checked); + + if (checked) + emit q_ptr->expanded(m_itemToIndex.value(item)); + else + emit q_ptr->collapsed(m_itemToIndex.value(item)); +} + +void QtButtonPropertyBrowserPrivate::updateLater() +{ + QTimer::singleShot(0, q_ptr, SLOT(slotUpdate())); +} + +void QtButtonPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) +{ + WidgetItem *afterItem = m_indexToItem.value(afterIndex); + WidgetItem *parentItem = m_indexToItem.value(index->parent()); + + WidgetItem *newItem = new WidgetItem(); + newItem->parent = parentItem; + + QGridLayout *layout = 0; + QWidget *parentWidget = 0; + int row = -1; + if (!afterItem) { + row = 0; + if (parentItem) + parentItem->children.insert(0, newItem); + else + m_children.insert(0, newItem); + } else { + row = gridRow(afterItem) + gridSpan(afterItem); + if (parentItem) + parentItem->children.insert(parentItem->children.indexOf(afterItem) + 1, newItem); + else + m_children.insert(m_children.indexOf(afterItem) + 1, newItem); + } + + if (!parentItem) { + layout = m_mainLayout; + parentWidget = q_ptr; + } else { + if (!parentItem->container) { + m_recreateQueue.removeAll(parentItem); + WidgetItem *grandParent = parentItem->parent; + QWidget *w = 0; + QGridLayout *l = 0; + const int oldRow = gridRow(parentItem); + if (grandParent) { + w = grandParent->container; + l = grandParent->layout; + } else { + w = q_ptr; + l = m_mainLayout; + } + QFrame *container = new QFrame(); + container->setFrameShape(QFrame::Panel); + container->setFrameShadow(QFrame::Raised); + parentItem->container = container; + parentItem->button = createButton(); + m_buttonToItem[parentItem->button] = parentItem; + q_ptr->connect(parentItem->button, SIGNAL(toggled(bool)), q_ptr, SLOT(slotToggled(bool))); + parentItem->layout = new QGridLayout(); + container->setLayout(parentItem->layout); + if (parentItem->label) { + l->removeWidget(parentItem->label); + delete parentItem->label; + parentItem->label = 0; + } + int span = 1; + if (!parentItem->widget && !parentItem->widgetLabel) + span = 2; + l->addWidget(parentItem->button, oldRow, 0, 1, span); + updateItem(parentItem); + } + layout = parentItem->layout; + parentWidget = parentItem->container; + } + + newItem->label = new QLabel(parentWidget); + newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + newItem->widget = createEditor(index->property(), parentWidget); + if (newItem->widget) { + QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed())); + m_widgetToItem[newItem->widget] = newItem; + } else if (index->property()->hasValue()) { + newItem->widgetLabel = new QLabel(parentWidget); + newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed)); + } + + insertRow(layout, row); + int span = 1; + if (newItem->widget) + layout->addWidget(newItem->widget, row, 1); + else if (newItem->widgetLabel) + layout->addWidget(newItem->widgetLabel, row, 1); + else + span = 2; + layout->addWidget(newItem->label, row, 0, span, 1); + + m_itemToIndex[newItem] = index; + m_indexToItem[index] = newItem; + + updateItem(newItem); +} + +void QtButtonPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) +{ + WidgetItem *item = m_indexToItem.value(index); + + m_indexToItem.remove(index); + m_itemToIndex.remove(item); + + WidgetItem *parentItem = item->parent; + + const int row = gridRow(item); + + if (parentItem) + parentItem->children.removeAt(parentItem->children.indexOf(item)); + else + m_children.removeAt(m_children.indexOf(item)); + + const int colSpan = gridSpan(item); + + m_buttonToItem.remove(item->button); + + if (item->widget) + delete item->widget; + if (item->label) + delete item->label; + if (item->widgetLabel) + delete item->widgetLabel; + if (item->button) + delete item->button; + if (item->container) + delete item->container; + + if (!parentItem) { + removeRow(m_mainLayout, row); + if (colSpan > 1) + removeRow(m_mainLayout, row); + } else if (parentItem->children.count() != 0) { + removeRow(parentItem->layout, row); + if (colSpan > 1) + removeRow(parentItem->layout, row); + } else { + const WidgetItem *grandParent = parentItem->parent; + QGridLayout *l = 0; + if (grandParent) { + l = grandParent->layout; + } else { + l = m_mainLayout; + } + + const int parentRow = gridRow(parentItem); + const int parentSpan = gridSpan(parentItem); + + l->removeWidget(parentItem->button); + l->removeWidget(parentItem->container); + delete parentItem->button; + delete parentItem->container; + parentItem->button = 0; + parentItem->container = 0; + parentItem->layout = 0; + if (!m_recreateQueue.contains(parentItem)) + m_recreateQueue.append(parentItem); + if (parentSpan > 1) + removeRow(l, parentRow + 1); + + updateLater(); + } + m_recreateQueue.removeAll(item); + + delete item; +} + +void QtButtonPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const +{ + QMap itemToPos; + int idx = 0; + while (idx < layout->count()) { + int r, c, rs, cs; + layout->getItemPosition(idx, &r, &c, &rs, &cs); + if (r >= row) { + itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs); + } else { + idx++; + } + } + + const QMap::ConstIterator icend = itemToPos.constEnd(); + for(QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { + const QRect r = it.value(); + layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); + } +} + +void QtButtonPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const +{ + QMap itemToPos; + int idx = 0; + while (idx < layout->count()) { + int r, c, rs, cs; + layout->getItemPosition(idx, &r, &c, &rs, &cs); + if (r > row) { + itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs); + } else { + idx++; + } + } + + const QMap::ConstIterator icend = itemToPos.constEnd(); + for(QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { + const QRect r = it.value(); + layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); + } +} + +void QtButtonPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) +{ + WidgetItem *item = m_indexToItem.value(index); + + updateItem(item); +} + +void QtButtonPropertyBrowserPrivate::updateItem(WidgetItem *item) +{ + QtProperty *property = m_itemToIndex[item]->property(); + if (item->button) { + QFont font = item->button->font(); + font.setUnderline(property->isModified()); + item->button->setFont(font); + item->button->setText(property->propertyName()); + item->button->setToolTip(property->toolTip()); + item->button->setStatusTip(property->statusTip()); + item->button->setWhatsThis(property->whatsThis()); + item->button->setEnabled(property->isEnabled()); + } + if (item->label) { + QFont font = item->label->font(); + font.setUnderline(property->isModified()); + item->label->setFont(font); + item->label->setText(property->propertyName()); + item->label->setToolTip(property->toolTip()); + item->label->setStatusTip(property->statusTip()); + item->label->setWhatsThis(property->whatsThis()); + item->label->setEnabled(property->isEnabled()); + } + if (item->widgetLabel) { + QFont font = item->widgetLabel->font(); + font.setUnderline(false); + item->widgetLabel->setFont(font); + item->widgetLabel->setText(property->valueText()); + item->widgetLabel->setToolTip(property->valueText()); + item->widgetLabel->setEnabled(property->isEnabled()); + } + if (item->widget) { + QFont font = item->widget->font(); + font.setUnderline(false); + item->widget->setFont(font); + item->widget->setEnabled(property->isEnabled()); + item->widget->setToolTip(property->valueText()); + } +} + + + +/*! + \class QtButtonPropertyBrowser + + \brief The QtButtonPropertyBrowser class provides a drop down QToolButton + based property browser. + + A property browser is a widget that enables the user to edit a + given set of properties. Each property is represented by a label + specifying the property's name, and an editing widget (e.g. a line + edit or a combobox) holding its value. A property can have zero or + more subproperties. + + QtButtonPropertyBrowser provides drop down button for all nested + properties, i.e. subproperties are enclosed by a container associated with + the drop down button. The parent property's name is displayed as button text. For example: + + \image qtbuttonpropertybrowser.png + + Use the QtAbstractPropertyBrowser API to add, insert and remove + properties from an instance of the QtButtonPropertyBrowser + class. The properties themselves are created and managed by + implementations of the QtAbstractPropertyManager class. + + \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser +*/ + +/*! + \fn void QtButtonPropertyBrowser::collapsed(QtBrowserItem *item) + + This signal is emitted when the \a item is collapsed. + + \sa expanded(), setExpanded() +*/ + +/*! + \fn void QtButtonPropertyBrowser::expanded(QtBrowserItem *item) + + This signal is emitted when the \a item is expanded. + + \sa collapsed(), setExpanded() +*/ + +/*! + Creates a property browser with the given \a parent. +*/ +QtButtonPropertyBrowser::QtButtonPropertyBrowser(QWidget *parent) + : QtAbstractPropertyBrowser(parent) +{ + d_ptr = new QtButtonPropertyBrowserPrivate; + d_ptr->q_ptr = this; + + d_ptr->init(this); +} + +/*! + Destroys this property browser. + + Note that the properties that were inserted into this browser are + \e not destroyed since they may still be used in other + browsers. The properties are owned by the manager that created + them. + + \sa QtProperty, QtAbstractPropertyManager +*/ +QtButtonPropertyBrowser::~QtButtonPropertyBrowser() +{ + const QMap::ConstIterator icend = d_ptr->m_itemToIndex.constEnd(); + for (QMap::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it) + delete it.key(); + delete d_ptr; +} + +/*! + \reimp +*/ +void QtButtonPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) +{ + d_ptr->propertyInserted(item, afterItem); +} + +/*! + \reimp +*/ +void QtButtonPropertyBrowser::itemRemoved(QtBrowserItem *item) +{ + d_ptr->propertyRemoved(item); +} + +/*! + \reimp +*/ +void QtButtonPropertyBrowser::itemChanged(QtBrowserItem *item) +{ + d_ptr->propertyChanged(item); +} + +/*! + Sets the \a item to either collapse or expanded, depending on the value of \a expanded. + + \sa isExpanded(), expanded(), collapsed() +*/ + +void QtButtonPropertyBrowser::setExpanded(QtBrowserItem *item, bool expanded) +{ + QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item); + if (itm) + d_ptr->setExpanded(itm, expanded); +} + +/*! + Returns true if the \a item is expanded; otherwise returns false. + + \sa setExpanded() +*/ + +bool QtButtonPropertyBrowser::isExpanded(QtBrowserItem *item) const +{ + QtButtonPropertyBrowserPrivate::WidgetItem *itm = d_ptr->m_indexToItem.value(item); + if (itm) + return itm->expanded; + return false; +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtbuttonpropertybrowser.cxx" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtbuttonpropertybrowser.h b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtbuttonpropertybrowser.h new file mode 100644 index 000000000..518e047e8 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtbuttonpropertybrowser.h @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTBUTTONPROPERTYBROWSER_H +#define QTBUTTONPROPERTYBROWSER_H + +#include "qtpropertybrowser.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtButtonPropertyBrowserPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtButtonPropertyBrowser : public QtAbstractPropertyBrowser +{ + Q_OBJECT +public: + + QtButtonPropertyBrowser(QWidget *parent = 0); + ~QtButtonPropertyBrowser(); + + void setExpanded(QtBrowserItem *item, bool expanded); + bool isExpanded(QtBrowserItem *item) const; + +Q_SIGNALS: + + void collapsed(QtBrowserItem *item); + void expanded(QtBrowserItem *item); + +protected: + virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); + virtual void itemRemoved(QtBrowserItem *item); + virtual void itemChanged(QtBrowserItem *item); + +private: + + QtButtonPropertyBrowserPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtButtonPropertyBrowser) + Q_DISABLE_COPY(QtButtonPropertyBrowser) + Q_PRIVATE_SLOT(d_func(), void slotUpdate()) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed()) + Q_PRIVATE_SLOT(d_func(), void slotToggled(bool)) + +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qteditorfactory.cpp b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qteditorfactory.cpp new file mode 100644 index 000000000..e619cf8cb --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qteditorfactory.cpp @@ -0,0 +1,2609 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qteditorfactory.h" +#include "qtpropertybrowserutils_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_CC_MSVC) +# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ +#endif + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +// Set a hard coded left margin to account for the indentation +// of the tree view icon when switching to an editor + +static inline void setupTreeViewEditorMargin(QLayout *lt) +{ + enum { DecorationMargin = 4 }; + if (QApplication::layoutDirection() == Qt::LeftToRight) + lt->setContentsMargins(DecorationMargin, 0, 0, 0); + else + lt->setContentsMargins(0, 0, DecorationMargin, 0); +} + +// ---------- EditorFactoryPrivate : +// Base class for editor factory private classes. Manages mapping of properties to editors and vice versa. + +template +class EditorFactoryPrivate +{ +public: + + typedef QList EditorList; + typedef QMap PropertyToEditorListMap; + typedef QMap EditorToPropertyMap; + + Editor *createEditor(QtProperty *property, QWidget *parent); + void initializeEditor(QtProperty *property, Editor *e); + void slotEditorDestroyed(QObject *object); + + PropertyToEditorListMap m_createdEditors; + EditorToPropertyMap m_editorToProperty; +}; + +template +Editor *EditorFactoryPrivate::createEditor(QtProperty *property, QWidget *parent) +{ + Editor *editor = new Editor(parent); + initializeEditor(property, editor); + return editor; +} + +template +void EditorFactoryPrivate::initializeEditor(QtProperty *property, Editor *editor) +{ + Q_TYPENAME PropertyToEditorListMap::iterator it = m_createdEditors.find(property); + if (it == m_createdEditors.end()) + it = m_createdEditors.insert(property, EditorList()); + it.value().append(editor); + m_editorToProperty.insert(editor, property); +} + +template +void EditorFactoryPrivate::slotEditorDestroyed(QObject *object) +{ + const Q_TYPENAME EditorToPropertyMap::iterator ecend = m_editorToProperty.end(); + for (Q_TYPENAME EditorToPropertyMap::iterator itEditor = m_editorToProperty.begin(); itEditor != ecend; ++itEditor) { + if (itEditor.key() == object) { + Editor *editor = itEditor.key(); + QtProperty *property = itEditor.value(); + const Q_TYPENAME PropertyToEditorListMap::iterator pit = m_createdEditors.find(property); + if (pit != m_createdEditors.end()) { + pit.value().removeAll(editor); + if (pit.value().empty()) + m_createdEditors.erase(pit); + } + m_editorToProperty.erase(itEditor); + return; + } + } +} + +// ------------ QtSpinBoxFactory + +class QtSpinBoxFactoryPrivate : public EditorFactoryPrivate +{ + QtSpinBoxFactory *q_ptr; + Q_DECLARE_PUBLIC(QtSpinBoxFactory) +public: + + void slotPropertyChanged(QtProperty *property, int value); + void slotRangeChanged(QtProperty *property, int min, int max); + void slotSingleStepChanged(QtProperty *property, int step); + void slotSetValue(int value); +}; + +void QtSpinBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSpinBox *editor = itEditor.next(); + if (editor->value() != value) { + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } + } +} + +void QtSpinBoxFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setRange(min, max); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtSpinBoxFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setSingleStep(step); + editor->blockSignals(false); + } +} + +void QtSpinBoxFactoryPrivate::slotSetValue(int value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) { + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } + } +} + +/*! + \class QtSpinBoxFactory + + \brief The QtSpinBoxFactory class provides QSpinBox widgets for + properties created by QtIntPropertyManager objects. + + \sa QtAbstractEditorFactory, QtIntPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtSpinBoxFactory::QtSpinBoxFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtSpinBoxFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtSpinBoxFactory::~QtSpinBoxFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtSpinBoxFactory::connectPropertyManager(QtIntPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtSpinBoxFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QSpinBox *editor = d_ptr->createEditor(property, parent); + editor->setSingleStep(manager->singleStep(property)); + editor->setRange(manager->minimum(property), manager->maximum(property)); + editor->setValue(manager->value(property)); + editor->setKeyboardTracking(false); + + connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtSpinBoxFactory::disconnectPropertyManager(QtIntPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +// QtSliderFactory + +class QtSliderFactoryPrivate : public EditorFactoryPrivate +{ + QtSliderFactory *q_ptr; + Q_DECLARE_PUBLIC(QtSliderFactory) +public: + void slotPropertyChanged(QtProperty *property, int value); + void slotRangeChanged(QtProperty *property, int min, int max); + void slotSingleStepChanged(QtProperty *property, int step); + void slotSetValue(int value); +}; + +void QtSliderFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSlider *editor = itEditor.next(); + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } +} + +void QtSliderFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSlider *editor = itEditor.next(); + editor->blockSignals(true); + editor->setRange(min, max); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtSliderFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QSlider *editor = itEditor.next(); + editor->blockSignals(true); + editor->setSingleStep(step); + editor->blockSignals(false); + } +} + +void QtSliderFactoryPrivate::slotSetValue(int value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor ) { + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } + } +} + +/*! + \class QtSliderFactory + + \brief The QtSliderFactory class provides QSlider widgets for + properties created by QtIntPropertyManager objects. + + \sa QtAbstractEditorFactory, QtIntPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtSliderFactory::QtSliderFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtSliderFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtSliderFactory::~QtSliderFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtSliderFactory::connectPropertyManager(QtIntPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtSliderFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QSlider *editor = new QSlider(Qt::Horizontal, parent); + d_ptr->initializeEditor(property, editor); + editor->setSingleStep(manager->singleStep(property)); + editor->setRange(manager->minimum(property), manager->maximum(property)); + editor->setValue(manager->value(property)); + + connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtSliderFactory::disconnectPropertyManager(QtIntPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +// QtSliderFactory + +class QtScrollBarFactoryPrivate : public EditorFactoryPrivate +{ + QtScrollBarFactory *q_ptr; + Q_DECLARE_PUBLIC(QtScrollBarFactory) +public: + void slotPropertyChanged(QtProperty *property, int value); + void slotRangeChanged(QtProperty *property, int min, int max); + void slotSingleStepChanged(QtProperty *property, int step); + void slotSetValue(int value); +}; + +void QtScrollBarFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor( m_createdEditors[property]); + while (itEditor.hasNext()) { + QScrollBar *editor = itEditor.next(); + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } +} + +void QtScrollBarFactoryPrivate::slotRangeChanged(QtProperty *property, int min, int max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor( m_createdEditors[property]); + while (itEditor.hasNext()) { + QScrollBar *editor = itEditor.next(); + editor->blockSignals(true); + editor->setRange(min, max); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtScrollBarFactoryPrivate::slotSingleStepChanged(QtProperty *property, int step) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QScrollBar *editor = itEditor.next(); + editor->blockSignals(true); + editor->setSingleStep(step); + editor->blockSignals(false); + } +} + +void QtScrollBarFactoryPrivate::slotSetValue(int value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtIntPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtScrollBarFactory + + \brief The QtScrollBarFactory class provides QScrollBar widgets for + properties created by QtIntPropertyManager objects. + + \sa QtAbstractEditorFactory, QtIntPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtScrollBarFactory::QtScrollBarFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtScrollBarFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtScrollBarFactory::~QtScrollBarFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtScrollBarFactory::connectPropertyManager(QtIntPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtScrollBarFactory::createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QScrollBar *editor = new QScrollBar(Qt::Horizontal, parent); + d_ptr->initializeEditor(property, editor); + editor->setSingleStep(manager->singleStep(property)); + editor->setRange(manager->minimum(property), manager->maximum(property)); + editor->setValue(manager->value(property)); + connect(editor, SIGNAL(valueChanged(int)), this, SLOT(slotSetValue(int))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtScrollBarFactory::disconnectPropertyManager(QtIntPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); +} + +// QtCheckBoxFactory + +class QtCheckBoxFactoryPrivate : public EditorFactoryPrivate +{ + QtCheckBoxFactory *q_ptr; + Q_DECLARE_PUBLIC(QtCheckBoxFactory) +public: + void slotPropertyChanged(QtProperty *property, bool value); + void slotSetValue(bool value); +}; + +void QtCheckBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, bool value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QtBoolEdit *editor = itEditor.next(); + editor->blockCheckBoxSignals(true); + editor->setChecked(value); + editor->blockCheckBoxSignals(false); + } +} + +void QtCheckBoxFactoryPrivate::slotSetValue(bool value) +{ + QObject *object = q_ptr->sender(); + + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtBoolPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtCheckBoxFactory + + \brief The QtCheckBoxFactory class provides QCheckBox widgets for + properties created by QtBoolPropertyManager objects. + + \sa QtAbstractEditorFactory, QtBoolPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtCheckBoxFactory::QtCheckBoxFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtCheckBoxFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtCheckBoxFactory::~QtCheckBoxFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCheckBoxFactory::connectPropertyManager(QtBoolPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotPropertyChanged(QtProperty *, bool))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtCheckBoxFactory::createEditor(QtBoolPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QtBoolEdit *editor = d_ptr->createEditor(property, parent); + editor->setChecked(manager->value(property)); + + connect(editor, SIGNAL(toggled(bool)), this, SLOT(slotSetValue(bool))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCheckBoxFactory::disconnectPropertyManager(QtBoolPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotPropertyChanged(QtProperty *, bool))); +} + +// QtDoubleSpinBoxFactory + +class QtDoubleSpinBoxFactoryPrivate : public EditorFactoryPrivate +{ + QtDoubleSpinBoxFactory *q_ptr; + Q_DECLARE_PUBLIC(QtDoubleSpinBoxFactory) +public: + + void slotPropertyChanged(QtProperty *property, double value); + void slotRangeChanged(QtProperty *property, double min, double max); + void slotSingleStepChanged(QtProperty *property, double step); + void slotDecimalsChanged(QtProperty *property, int prec); + void slotSetValue(double value); +}; + +void QtDoubleSpinBoxFactoryPrivate::slotPropertyChanged(QtProperty *property, double value) +{ + QList editors = m_createdEditors[property]; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + if (editor->value() != value) { + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } + } +} + +void QtDoubleSpinBoxFactoryPrivate::slotRangeChanged(QtProperty *property, + double min, double max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtDoublePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QList editors = m_createdEditors[property]; + QListIterator itEditor(editors); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setRange(min, max); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtDoubleSpinBoxFactoryPrivate::slotSingleStepChanged(QtProperty *property, double step) +{ + if (!m_createdEditors.contains(property)) + return; + + QtDoublePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QList editors = m_createdEditors[property]; + QListIterator itEditor(editors); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setSingleStep(step); + editor->blockSignals(false); + } +} + +void QtDoubleSpinBoxFactoryPrivate::slotDecimalsChanged(QtProperty *property, int prec) +{ + if (!m_createdEditors.contains(property)) + return; + + QtDoublePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QList editors = m_createdEditors[property]; + QListIterator itEditor(editors); + while (itEditor.hasNext()) { + QDoubleSpinBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setDecimals(prec); + editor->setValue(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtDoubleSpinBoxFactoryPrivate::slotSetValue(double value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator itcend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != itcend; ++itEditor) { + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtDoublePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } + } +} + +/*! \class QtDoubleSpinBoxFactory + + \brief The QtDoubleSpinBoxFactory class provides QDoubleSpinBox + widgets for properties created by QtDoublePropertyManager objects. + + \sa QtAbstractEditorFactory, QtDoublePropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtDoubleSpinBoxFactory::QtDoubleSpinBoxFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtDoubleSpinBoxFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtDoubleSpinBoxFactory::~QtDoubleSpinBoxFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDoubleSpinBoxFactory::connectPropertyManager(QtDoublePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotPropertyChanged(QtProperty *, double))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + connect(manager, SIGNAL(singleStepChanged(QtProperty *, double)), + this, SLOT(slotSingleStepChanged(QtProperty *, double))); + connect(manager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtDoubleSpinBoxFactory::createEditor(QtDoublePropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QDoubleSpinBox *editor = d_ptr->createEditor(property, parent); + editor->setSingleStep(manager->singleStep(property)); + editor->setDecimals(6); + editor->setRange(manager->minimum(property), manager->maximum(property)); + editor->setValue(manager->value(property)); + editor->setKeyboardTracking(false); + + connect(editor, SIGNAL(valueChanged(double)), this, SLOT(slotSetValue(double))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDoubleSpinBoxFactory::disconnectPropertyManager(QtDoublePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotPropertyChanged(QtProperty *, double))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + disconnect(manager, SIGNAL(singleStepChanged(QtProperty *, double)), + this, SLOT(slotSingleStepChanged(QtProperty *, double))); + disconnect(manager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); +} + +// QtLineEditFactory + +class QtLineEditFactoryPrivate : public EditorFactoryPrivate +{ + QtLineEditFactory *q_ptr; + Q_DECLARE_PUBLIC(QtLineEditFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QString &value); + void slotRegExpChanged(QtProperty *property, const QRegExp ®Exp); + void slotSetValue(const QString &value); + void slotEditingFinished(); +}; + +void QtLineEditFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QString &value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor( m_createdEditors[property]); + while (itEditor.hasNext()) { + QLineEdit *editor = itEditor.next(); + if (editor->text() != value) + editor->setText(value); + } +} + +void QtLineEditFactoryPrivate::slotRegExpChanged(QtProperty *property, + const QRegExp ®Exp) +{ + if (!m_createdEditors.contains(property)) + return; + + QtStringPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QLineEdit *editor = itEditor.next(); + editor->blockSignals(true); + const QValidator *oldValidator = editor->validator(); + QValidator *newValidator = 0; + if (regExp.isValid()) { + newValidator = new QRegExpValidator(regExp, editor); + } + editor->setValidator(newValidator); + if (oldValidator) + delete oldValidator; + editor->blockSignals(false); + } +} + +void QtLineEditFactoryPrivate::slotSetValue(const QString &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtStringPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +void QtLineEditFactoryPrivate::slotEditingFinished() +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtStringPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + QString value = static_cast(itEditor.key())->text(); + manager->setValue(property, value); + return; + } +} + +/*! + \class QtLineEditFactory + + \brief The QtLineEditFactory class provides QLineEdit widgets for + properties created by QtStringPropertyManager objects. + + \sa QtAbstractEditorFactory, QtStringPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtLineEditFactory::QtLineEditFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtLineEditFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtLineEditFactory::~QtLineEditFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtLineEditFactory::connectPropertyManager(QtStringPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QString &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QString &))); + connect(manager, SIGNAL(regExpChanged(QtProperty *, const QRegExp &)), + this, SLOT(slotRegExpChanged(QtProperty *, const QRegExp &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtLineEditFactory::createEditor(QtStringPropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + + QLineEdit *editor = d_ptr->createEditor(property, parent); + QRegExp regExp = manager->regExp(property); + if (regExp.isValid()) { + QValidator *validator = new QRegExpValidator(regExp, editor); + editor->setValidator(validator); + } + editor->setText(manager->value(property)); + + connect(editor, SIGNAL(editingFinished()), + this, SLOT(slotEditingFinished())); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtLineEditFactory::disconnectPropertyManager(QtStringPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QString &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QString &))); + disconnect(manager, SIGNAL(regExpChanged(QtProperty *, const QRegExp &)), + this, SLOT(slotRegExpChanged(QtProperty *, const QRegExp &))); +} + +// QtDateEditFactory + +class QtDateEditFactoryPrivate : public EditorFactoryPrivate +{ + QtDateEditFactory *q_ptr; + Q_DECLARE_PUBLIC(QtDateEditFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QDate &value); + void slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max); + void slotSetValue(const QDate &value); +}; + +void QtDateEditFactoryPrivate::slotPropertyChanged(QtProperty *property, const QDate &value) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QDateEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setDate(value); + editor->blockSignals(false); + } +} + +void QtDateEditFactoryPrivate::slotRangeChanged(QtProperty *property, + const QDate &min, const QDate &max) +{ + if (!m_createdEditors.contains(property)) + return; + + QtDatePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QDateEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setDateRange(min, max); + editor->setDate(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtDateEditFactoryPrivate::slotSetValue(const QDate &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtDatePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtDateEditFactory + + \brief The QtDateEditFactory class provides QDateEdit widgets for + properties created by QtDatePropertyManager objects. + + \sa QtAbstractEditorFactory, QtDatePropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtDateEditFactory::QtDateEditFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtDateEditFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtDateEditFactory::~QtDateEditFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDateEditFactory::connectPropertyManager(QtDatePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QDate &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QDate &))); + connect(manager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), + this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtDateEditFactory::createEditor(QtDatePropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QDateEdit *editor = d_ptr->createEditor(property, parent); + editor->setCalendarPopup(true); + editor->setDateRange(manager->minimum(property), manager->maximum(property)); + editor->setDate(manager->value(property)); + + connect(editor, SIGNAL(dateChanged(const QDate &)), + this, SLOT(slotSetValue(const QDate &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDateEditFactory::disconnectPropertyManager(QtDatePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QDate &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QDate &))); + disconnect(manager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), + this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); +} + +// QtTimeEditFactory + +class QtTimeEditFactoryPrivate : public EditorFactoryPrivate +{ + QtTimeEditFactory *q_ptr; + Q_DECLARE_PUBLIC(QtTimeEditFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QTime &value); + void slotSetValue(const QTime &value); +}; + +void QtTimeEditFactoryPrivate::slotPropertyChanged(QtProperty *property, const QTime &value) +{ + if (!m_createdEditors.contains(property)) + return; + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QTimeEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setTime(value); + editor->blockSignals(false); + } +} + +void QtTimeEditFactoryPrivate::slotSetValue(const QTime &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtTimePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtTimeEditFactory + + \brief The QtTimeEditFactory class provides QTimeEdit widgets for + properties created by QtTimePropertyManager objects. + + \sa QtAbstractEditorFactory, QtTimePropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtTimeEditFactory::QtTimeEditFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtTimeEditFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtTimeEditFactory::~QtTimeEditFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtTimeEditFactory::connectPropertyManager(QtTimePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QTime &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QTime &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtTimeEditFactory::createEditor(QtTimePropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QTimeEdit *editor = d_ptr->createEditor(property, parent); + editor->setTime(manager->value(property)); + + connect(editor, SIGNAL(timeChanged(const QTime &)), + this, SLOT(slotSetValue(const QTime &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtTimeEditFactory::disconnectPropertyManager(QtTimePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QTime &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QTime &))); +} + +// QtDateTimeEditFactory + +class QtDateTimeEditFactoryPrivate : public EditorFactoryPrivate +{ + QtDateTimeEditFactory *q_ptr; + Q_DECLARE_PUBLIC(QtDateTimeEditFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QDateTime &value); + void slotSetValue(const QDateTime &value); + +}; + +void QtDateTimeEditFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QDateTime &value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QDateTimeEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setDateTime(value); + editor->blockSignals(false); + } +} + +void QtDateTimeEditFactoryPrivate::slotSetValue(const QDateTime &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtDateTimePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtDateTimeEditFactory + + \brief The QtDateTimeEditFactory class provides QDateTimeEdit + widgets for properties created by QtDateTimePropertyManager objects. + + \sa QtAbstractEditorFactory, QtDateTimePropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtDateTimeEditFactory::QtDateTimeEditFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtDateTimeEditFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtDateTimeEditFactory::~QtDateTimeEditFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDateTimeEditFactory::connectPropertyManager(QtDateTimePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QDateTime &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtDateTimeEditFactory::createEditor(QtDateTimePropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QDateTimeEdit *editor = d_ptr->createEditor(property, parent); + editor->setDateTime(manager->value(property)); + + connect(editor, SIGNAL(dateTimeChanged(const QDateTime &)), + this, SLOT(slotSetValue(const QDateTime &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtDateTimeEditFactory::disconnectPropertyManager(QtDateTimePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QDateTime &))); +} + +// QtKeySequenceEditorFactory + +class QtKeySequenceEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtKeySequenceEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtKeySequenceEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QKeySequence &value); + void slotSetValue(const QKeySequence &value); +}; + +void QtKeySequenceEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QKeySequence &value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QtKeySequenceEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setKeySequence(value); + editor->blockSignals(false); + } +} + +void QtKeySequenceEditorFactoryPrivate::slotSetValue(const QKeySequence &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtKeySequencePropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtKeySequenceEditorFactory + + \brief The QtKeySequenceEditorFactory class provides editor + widgets for properties created by QtKeySequencePropertyManager objects. + + \sa QtAbstractEditorFactory +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtKeySequenceEditorFactory::QtKeySequenceEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtKeySequenceEditorFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtKeySequenceEditorFactory::~QtKeySequenceEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtKeySequenceEditorFactory::connectPropertyManager(QtKeySequencePropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QKeySequence &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtKeySequenceEditorFactory::createEditor(QtKeySequencePropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QtKeySequenceEdit *editor = d_ptr->createEditor(property, parent); + editor->setKeySequence(manager->value(property)); + + connect(editor, SIGNAL(keySequenceChanged(const QKeySequence &)), + this, SLOT(slotSetValue(const QKeySequence &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtKeySequenceEditorFactory::disconnectPropertyManager(QtKeySequencePropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QKeySequence &))); +} + +// QtCharEdit + +class QtCharEdit : public QWidget +{ + Q_OBJECT +public: + QtCharEdit(QWidget *parent = 0); + + QChar value() const; + bool eventFilter(QObject *o, QEvent *e); +public Q_SLOTS: + void setValue(const QChar &value); +Q_SIGNALS: + void valueChanged(const QChar &value); +protected: + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + bool event(QEvent *e); +private slots: + void slotClearChar(); +private: + void handleKeyEvent(QKeyEvent *e); + + QChar m_value; + QLineEdit *m_lineEdit; +}; + +QtCharEdit::QtCharEdit(QWidget *parent) + : QWidget(parent), m_lineEdit(new QLineEdit(this)) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(m_lineEdit); + layout->setMargin(0); + m_lineEdit->installEventFilter(this); + m_lineEdit->setReadOnly(true); + m_lineEdit->setFocusProxy(this); + setFocusPolicy(m_lineEdit->focusPolicy()); + setAttribute(Qt::WA_InputMethodEnabled); +} + +bool QtCharEdit::eventFilter(QObject *o, QEvent *e) +{ + if (o == m_lineEdit && e->type() == QEvent::ContextMenu) { + QContextMenuEvent *c = static_cast(e); + QMenu *menu = m_lineEdit->createStandardContextMenu(); + QList actions = menu->actions(); + QListIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + action->setShortcut(QKeySequence()); + QString actionString = action->text(); + const int pos = actionString.lastIndexOf(QLatin1Char('\t')); + if (pos > 0) + actionString = actionString.remove(pos, actionString.length() - pos); + action->setText(actionString); + } + QAction *actionBefore = 0; + if (actions.count() > 0) + actionBefore = actions[0]; + QAction *clearAction = new QAction(tr("Clear Char"), menu); + menu->insertAction(actionBefore, clearAction); + menu->insertSeparator(actionBefore); + clearAction->setEnabled(!m_value.isNull()); + connect(clearAction, SIGNAL(triggered()), this, SLOT(slotClearChar())); + menu->exec(c->globalPos()); + delete menu; + e->accept(); + return true; + } + + return QWidget::eventFilter(o, e); +} + +void QtCharEdit::slotClearChar() +{ + if (m_value.isNull()) + return; + setValue(QChar()); + emit valueChanged(m_value); +} + +void QtCharEdit::handleKeyEvent(QKeyEvent *e) +{ + const int key = e->key(); + switch (key) { + case Qt::Key_Control: + case Qt::Key_Shift: + case Qt::Key_Meta: + case Qt::Key_Alt: + case Qt::Key_Super_L: + case Qt::Key_Return: + return; + default: + break; + } + + const QString text = e->text(); + if (text.count() != 1) + return; + + const QChar c = text.at(0); + if (!c.isPrint()) + return; + + if (m_value == c) + return; + + m_value = c; + const QString str = m_value.isNull() ? QString() : QString(m_value); + m_lineEdit->setText(str); + e->accept(); + emit valueChanged(m_value); +} + +void QtCharEdit::setValue(const QChar &value) +{ + if (value == m_value) + return; + + m_value = value; + QString str = value.isNull() ? QString() : QString(value); + m_lineEdit->setText(str); +} + +QChar QtCharEdit::value() const +{ + return m_value; +} + +void QtCharEdit::focusInEvent(QFocusEvent *e) +{ + m_lineEdit->event(e); + m_lineEdit->selectAll(); + QWidget::focusInEvent(e); +} + +void QtCharEdit::focusOutEvent(QFocusEvent *e) +{ + m_lineEdit->event(e); + QWidget::focusOutEvent(e); +} + +void QtCharEdit::keyPressEvent(QKeyEvent *e) +{ + handleKeyEvent(e); + e->accept(); +} + +void QtCharEdit::keyReleaseEvent(QKeyEvent *e) +{ + m_lineEdit->event(e); +} + +bool QtCharEdit::event(QEvent *e) +{ + switch(e->type()) { + case QEvent::Shortcut: + case QEvent::ShortcutOverride: + case QEvent::KeyRelease: + e->accept(); + return true; + default: + break; + } + return QWidget::event(e); +} + +// QtCharEditorFactory + +class QtCharEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtCharEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtCharEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QChar &value); + void slotSetValue(const QChar &value); + +}; + +void QtCharEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QChar &value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QtCharEdit *editor = itEditor.next(); + editor->blockSignals(true); + editor->setValue(value); + editor->blockSignals(false); + } +} + +void QtCharEditorFactoryPrivate::slotSetValue(const QChar &value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtCharPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtCharEditorFactory + + \brief The QtCharEditorFactory class provides editor + widgets for properties created by QtCharPropertyManager objects. + + \sa QtAbstractEditorFactory +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtCharEditorFactory::QtCharEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtCharEditorFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtCharEditorFactory::~QtCharEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCharEditorFactory::connectPropertyManager(QtCharPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QChar &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QChar &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtCharEditorFactory::createEditor(QtCharPropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QtCharEdit *editor = d_ptr->createEditor(property, parent); + editor->setValue(manager->value(property)); + + connect(editor, SIGNAL(valueChanged(const QChar &)), + this, SLOT(slotSetValue(const QChar &))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCharEditorFactory::disconnectPropertyManager(QtCharPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QChar &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QChar &))); +} + +// QtEnumEditorFactory + +class QtEnumEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtEnumEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtEnumEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, int value); + void slotEnumNamesChanged(QtProperty *property, const QStringList &); + void slotEnumIconsChanged(QtProperty *property, const QMap &); + void slotSetValue(int value); +}; + +void QtEnumEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, int value) +{ + if (!m_createdEditors.contains(property)) + return; + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QComboBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->setCurrentIndex(value); + editor->blockSignals(false); + } +} + +void QtEnumEditorFactoryPrivate::slotEnumNamesChanged(QtProperty *property, + const QStringList &enumNames) +{ + if (!m_createdEditors.contains(property)) + return; + + QtEnumPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + QMap enumIcons = manager->enumIcons(property); + + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QComboBox *editor = itEditor.next(); + editor->blockSignals(true); + editor->clear(); + editor->addItems(enumNames); + const int nameCount = enumNames.count(); + for (int i = 0; i < nameCount; i++) + editor->setItemIcon(i, enumIcons.value(i)); + editor->setCurrentIndex(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtEnumEditorFactoryPrivate::slotEnumIconsChanged(QtProperty *property, + const QMap &enumIcons) +{ + if (!m_createdEditors.contains(property)) + return; + + QtEnumPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + + const QStringList enumNames = manager->enumNames(property); + QListIterator itEditor(m_createdEditors[property]); + while (itEditor.hasNext()) { + QComboBox *editor = itEditor.next(); + editor->blockSignals(true); + const int nameCount = enumNames.count(); + for (int i = 0; i < nameCount; i++) + editor->setItemIcon(i, enumIcons.value(i)); + editor->setCurrentIndex(manager->value(property)); + editor->blockSignals(false); + } +} + +void QtEnumEditorFactoryPrivate::slotSetValue(int value) +{ + QObject *object = q_ptr->sender(); + const QMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtEnumPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtEnumEditorFactory + + \brief The QtEnumEditorFactory class provides QComboBox widgets for + properties created by QtEnumPropertyManager objects. + + \sa QtAbstractEditorFactory, QtEnumPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtEnumEditorFactory::QtEnumEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtEnumEditorFactoryPrivate(); + d_ptr->q_ptr = this; + +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtEnumEditorFactory::~QtEnumEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtEnumEditorFactory::connectPropertyManager(QtEnumPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + connect(manager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtEnumEditorFactory::createEditor(QtEnumPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QComboBox *editor = d_ptr->createEditor(property, parent); + editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed); + editor->view()->setTextElideMode(Qt::ElideRight); + QStringList enumNames = manager->enumNames(property); + editor->addItems(enumNames); + QMap enumIcons = manager->enumIcons(property); + const int enumNamesCount = enumNames.count(); + for (int i = 0; i < enumNamesCount; i++) + editor->setItemIcon(i, enumIcons.value(i)); + editor->setCurrentIndex(manager->value(property)); + + connect(editor, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSetValue(int))); + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtEnumEditorFactory::disconnectPropertyManager(QtEnumPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotPropertyChanged(QtProperty *, int))); + disconnect(manager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); +} + +// QtCursorEditorFactory + +Q_GLOBAL_STATIC(QtCursorDatabase, cursorDatabase) + +class QtCursorEditorFactoryPrivate +{ + QtCursorEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtCursorEditorFactory) +public: + QtCursorEditorFactoryPrivate(); + + void slotPropertyChanged(QtProperty *property, const QCursor &cursor); + void slotEnumChanged(QtProperty *property, int value); + void slotEditorDestroyed(QObject *object); + + QtEnumEditorFactory *m_enumEditorFactory; + QtEnumPropertyManager *m_enumPropertyManager; + + QMap m_propertyToEnum; + QMap m_enumToProperty; + QMap > m_enumToEditors; + QMap m_editorToEnum; + bool m_updatingEnum; +}; + +QtCursorEditorFactoryPrivate::QtCursorEditorFactoryPrivate() + : m_updatingEnum(false) +{ + +} + +void QtCursorEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, const QCursor &cursor) +{ + // update enum property + QtProperty *enumProp = m_propertyToEnum.value(property); + if (!enumProp) + return; + + m_updatingEnum = true; + m_enumPropertyManager->setValue(enumProp, cursorDatabase()->cursorToValue(cursor)); + m_updatingEnum = false; +} + +void QtCursorEditorFactoryPrivate::slotEnumChanged(QtProperty *property, int value) +{ + if (m_updatingEnum) + return; + // update cursor property + QtProperty *prop = m_enumToProperty.value(property); + if (!prop) + return; + QtCursorPropertyManager *cursorManager = q_ptr->propertyManager(prop); + if (!cursorManager) + return; +#ifndef QT_NO_CURSOR + cursorManager->setValue(prop, QCursor(cursorDatabase()->valueToCursor(value))); +#endif +} + +void QtCursorEditorFactoryPrivate::slotEditorDestroyed(QObject *object) +{ + // remove from m_editorToEnum map; + // remove from m_enumToEditors map; + // if m_enumToEditors doesn't contains more editors delete enum property; + const QMap::ConstIterator ecend = m_editorToEnum.constEnd(); + for (QMap::ConstIterator itEditor = m_editorToEnum.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QWidget *editor = itEditor.key(); + QtProperty *enumProp = itEditor.value(); + m_editorToEnum.remove(editor); + m_enumToEditors[enumProp].removeAll(editor); + if (m_enumToEditors[enumProp].isEmpty()) { + m_enumToEditors.remove(enumProp); + QtProperty *property = m_enumToProperty.value(enumProp); + m_enumToProperty.remove(enumProp); + m_propertyToEnum.remove(property); + delete enumProp; + } + return; + } +} + +/*! + \class QtCursorEditorFactory + + \brief The QtCursorEditorFactory class provides QComboBox widgets for + properties created by QtCursorPropertyManager objects. + + \sa QtAbstractEditorFactory, QtCursorPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtCursorEditorFactory::QtCursorEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtCursorEditorFactoryPrivate(); + d_ptr->q_ptr = this; + + d_ptr->m_enumEditorFactory = new QtEnumEditorFactory(this); + d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); + connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotEnumChanged(QtProperty *, int))); + d_ptr->m_enumEditorFactory->addPropertyManager(d_ptr->m_enumPropertyManager); +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtCursorEditorFactory::~QtCursorEditorFactory() +{ + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCursorEditorFactory::connectPropertyManager(QtCursorPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QCursor &))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtCursorEditorFactory::createEditor(QtCursorPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + QtProperty *enumProp = 0; + if (d_ptr->m_propertyToEnum.contains(property)) { + enumProp = d_ptr->m_propertyToEnum[property]; + } else { + enumProp = d_ptr->m_enumPropertyManager->addProperty(property->propertyName()); + d_ptr->m_enumPropertyManager->setEnumNames(enumProp, cursorDatabase()->cursorShapeNames()); + d_ptr->m_enumPropertyManager->setEnumIcons(enumProp, cursorDatabase()->cursorShapeIcons()); +#ifndef QT_NO_CURSOR + d_ptr->m_enumPropertyManager->setValue(enumProp, cursorDatabase()->cursorToValue(manager->value(property))); +#endif + d_ptr->m_propertyToEnum[property] = enumProp; + d_ptr->m_enumToProperty[enumProp] = property; + } + QtAbstractEditorFactoryBase *af = d_ptr->m_enumEditorFactory; + QWidget *editor = af->createEditor(enumProp, parent); + d_ptr->m_enumToEditors[enumProp].append(editor); + d_ptr->m_editorToEnum[editor] = enumProp; + connect(editor, SIGNAL(destroyed(QObject *)), + this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtCursorEditorFactory::disconnectPropertyManager(QtCursorPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), + this, SLOT(slotPropertyChanged(QtProperty *, const QCursor &))); +} + +// QtColorEditWidget + +class QtColorEditWidget : public QWidget { + Q_OBJECT + +public: + QtColorEditWidget(QWidget *parent); + + bool eventFilter(QObject *obj, QEvent *ev); + +public Q_SLOTS: + void setValue(const QColor &value); + +private Q_SLOTS: + void buttonClicked(); + +Q_SIGNALS: + void valueChanged(const QColor &value); + +private: + QColor m_color; + QLabel *m_pixmapLabel; + QLabel *m_label; + QToolButton *m_button; +}; + +QtColorEditWidget::QtColorEditWidget(QWidget *parent) : + QWidget(parent), + m_pixmapLabel(new QLabel), + m_label(new QLabel), + m_button(new QToolButton) +{ + QHBoxLayout *lt = new QHBoxLayout(this); + setupTreeViewEditorMargin(lt); + lt->setSpacing(0); + lt->addWidget(m_pixmapLabel); + lt->addWidget(m_label); + lt->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); + + m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + m_button->setFixedWidth(20); + setFocusProxy(m_button); + setFocusPolicy(m_button->focusPolicy()); + m_button->setText(tr("...")); + m_button->installEventFilter(this); + connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClicked())); + lt->addWidget(m_button); + m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::brushValuePixmap(QBrush(m_color))); + m_label->setText(QtPropertyBrowserUtils::colorValueText(m_color)); +} + +void QtColorEditWidget::setValue(const QColor &c) +{ + if (m_color != c) { + m_color = c; + m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::brushValuePixmap(QBrush(c))); + m_label->setText(QtPropertyBrowserUtils::colorValueText(c)); + } +} + +void QtColorEditWidget::buttonClicked() +{ + bool ok = false; + QRgb oldRgba = m_color.rgba(); + QRgb newRgba = QColorDialog::getRgba(oldRgba, &ok, this); + if (ok && newRgba != oldRgba) { + setValue(QColor::fromRgba(newRgba)); + emit valueChanged(m_color); + } +} + +bool QtColorEditWidget::eventFilter(QObject *obj, QEvent *ev) +{ + if (obj == m_button) { + switch (ev->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { // Prevent the QToolButton from handling Enter/Escape meant control the delegate + switch (static_cast(ev)->key()) { + case Qt::Key_Escape: + case Qt::Key_Enter: + case Qt::Key_Return: + ev->ignore(); + return true; + default: + break; + } + } + break; + default: + break; + } + } + return QWidget::eventFilter(obj, ev); +} + +// QtColorEditorFactoryPrivate + +class QtColorEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtColorEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtColorEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QColor &value); + void slotSetValue(const QColor &value); +}; + +void QtColorEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QColor &value) +{ + const PropertyToEditorListMap::iterator it = m_createdEditors.find(property); + if (it == m_createdEditors.end()) + return; + QListIterator itEditor(it.value()); + + while (itEditor.hasNext()) + itEditor.next()->setValue(value); +} + +void QtColorEditorFactoryPrivate::slotSetValue(const QColor &value) +{ + QObject *object = q_ptr->sender(); + const EditorToPropertyMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (EditorToPropertyMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtColorPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtColorEditorFactory + + \brief The QtColorEditorFactory class provides color editing for + properties created by QtColorPropertyManager objects. + + \sa QtAbstractEditorFactory, QtColorPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtColorEditorFactory::QtColorEditorFactory(QObject *parent) : + QtAbstractEditorFactory(parent), + d_ptr(new QtColorEditorFactoryPrivate()) +{ + d_ptr->q_ptr = this; +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtColorEditorFactory::~QtColorEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtColorEditorFactory::connectPropertyManager(QtColorPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty*,QColor)), + this, SLOT(slotPropertyChanged(QtProperty*,QColor))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtColorEditorFactory::createEditor(QtColorPropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QtColorEditWidget *editor = d_ptr->createEditor(property, parent); + editor->setValue(manager->value(property)); + connect(editor, SIGNAL(valueChanged(QColor)), this, SLOT(slotSetValue(QColor))); + connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtColorEditorFactory::disconnectPropertyManager(QtColorPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty*,QColor)), this, SLOT(slotPropertyChanged(QtProperty*,QColor))); +} + +// QtFontEditWidget + +class QtFontEditWidget : public QWidget { + Q_OBJECT + +public: + QtFontEditWidget(QWidget *parent); + + bool eventFilter(QObject *obj, QEvent *ev); + +public Q_SLOTS: + void setValue(const QFont &value); + +private Q_SLOTS: + void buttonClicked(); + +Q_SIGNALS: + void valueChanged(const QFont &value); + +private: + QFont m_font; + QLabel *m_pixmapLabel; + QLabel *m_label; + QToolButton *m_button; +}; + +QtFontEditWidget::QtFontEditWidget(QWidget *parent) : + QWidget(parent), + m_pixmapLabel(new QLabel), + m_label(new QLabel), + m_button(new QToolButton) +{ + QHBoxLayout *lt = new QHBoxLayout(this); + setupTreeViewEditorMargin(lt); + lt->setSpacing(0); + lt->addWidget(m_pixmapLabel); + lt->addWidget(m_label); + lt->addItem(new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Ignored)); + + m_button->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored); + m_button->setFixedWidth(20); + setFocusProxy(m_button); + setFocusPolicy(m_button->focusPolicy()); + m_button->setText(tr("...")); + m_button->installEventFilter(this); + connect(m_button, SIGNAL(clicked()), this, SLOT(buttonClicked())); + lt->addWidget(m_button); + m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::fontValuePixmap(m_font)); + m_label->setText(QtPropertyBrowserUtils::fontValueText(m_font)); +} + +void QtFontEditWidget::setValue(const QFont &f) +{ + if (m_font != f) { + m_font = f; + m_pixmapLabel->setPixmap(QtPropertyBrowserUtils::fontValuePixmap(f)); + m_label->setText(QtPropertyBrowserUtils::fontValueText(f)); + } +} + +void QtFontEditWidget::buttonClicked() +{ + bool ok = false; + QFont newFont = QFontDialog::getFont(&ok, m_font, this, tr("Select Font")); + if (ok && newFont != m_font) { + QFont f = m_font; + // prevent mask for unchanged attributes, don't change other attributes (like kerning, etc...) + if (m_font.family() != newFont.family()) + f.setFamily(newFont.family()); + if (m_font.pointSize() != newFont.pointSize()) + f.setPointSize(newFont.pointSize()); + if (m_font.bold() != newFont.bold()) + f.setBold(newFont.bold()); + if (m_font.italic() != newFont.italic()) + f.setItalic(newFont.italic()); + if (m_font.underline() != newFont.underline()) + f.setUnderline(newFont.underline()); + if (m_font.strikeOut() != newFont.strikeOut()) + f.setStrikeOut(newFont.strikeOut()); + setValue(f); + emit valueChanged(m_font); + } +} + +bool QtFontEditWidget::eventFilter(QObject *obj, QEvent *ev) +{ + if (obj == m_button) { + switch (ev->type()) { + case QEvent::KeyPress: + case QEvent::KeyRelease: { // Prevent the QToolButton from handling Enter/Escape meant control the delegate + switch (static_cast(ev)->key()) { + case Qt::Key_Escape: + case Qt::Key_Enter: + case Qt::Key_Return: + ev->ignore(); + return true; + default: + break; + } + } + break; + default: + break; + } + } + return QWidget::eventFilter(obj, ev); +} + +// QtFontEditorFactoryPrivate + +class QtFontEditorFactoryPrivate : public EditorFactoryPrivate +{ + QtFontEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtFontEditorFactory) +public: + + void slotPropertyChanged(QtProperty *property, const QFont &value); + void slotSetValue(const QFont &value); +}; + +void QtFontEditorFactoryPrivate::slotPropertyChanged(QtProperty *property, + const QFont &value) +{ + const PropertyToEditorListMap::iterator it = m_createdEditors.find(property); + if (it == m_createdEditors.end()) + return; + QListIterator itEditor(it.value()); + + while (itEditor.hasNext()) + itEditor.next()->setValue(value); +} + +void QtFontEditorFactoryPrivate::slotSetValue(const QFont &value) +{ + QObject *object = q_ptr->sender(); + const EditorToPropertyMap::ConstIterator ecend = m_editorToProperty.constEnd(); + for (EditorToPropertyMap::ConstIterator itEditor = m_editorToProperty.constBegin(); itEditor != ecend; ++itEditor) + if (itEditor.key() == object) { + QtProperty *property = itEditor.value(); + QtFontPropertyManager *manager = q_ptr->propertyManager(property); + if (!manager) + return; + manager->setValue(property, value); + return; + } +} + +/*! + \class QtFontEditorFactory + + \brief The QtFontEditorFactory class provides font editing for + properties created by QtFontPropertyManager objects. + + \sa QtAbstractEditorFactory, QtFontPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtFontEditorFactory::QtFontEditorFactory(QObject *parent) : + QtAbstractEditorFactory(parent), + d_ptr(new QtFontEditorFactoryPrivate()) +{ + d_ptr->q_ptr = this; +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtFontEditorFactory::~QtFontEditorFactory() +{ + qDeleteAll(d_ptr->m_editorToProperty.keys()); + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtFontEditorFactory::connectPropertyManager(QtFontPropertyManager *manager) +{ + connect(manager, SIGNAL(valueChanged(QtProperty*,QFont)), + this, SLOT(slotPropertyChanged(QtProperty*,QFont))); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtFontEditorFactory::createEditor(QtFontPropertyManager *manager, + QtProperty *property, QWidget *parent) +{ + QtFontEditWidget *editor = d_ptr->createEditor(property, parent); + editor->setValue(manager->value(property)); + connect(editor, SIGNAL(valueChanged(QFont)), this, SLOT(slotSetValue(QFont))); + connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); + return editor; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtFontEditorFactory::disconnectPropertyManager(QtFontPropertyManager *manager) +{ + disconnect(manager, SIGNAL(valueChanged(QtProperty*,QFont)), this, SLOT(slotPropertyChanged(QtProperty*,QFont))); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qteditorfactory.cxx" +#include "qteditorfactory.moc" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qteditorfactory.h b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qteditorfactory.h new file mode 100644 index 000000000..47e7b507f --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qteditorfactory.h @@ -0,0 +1,448 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTEDITORFACTORY_H +#define QTEDITORFACTORY_H + +#include "qtpropertymanager.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtSpinBoxFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSpinBoxFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtSpinBoxFactory(QObject *parent = 0); + ~QtSpinBoxFactory(); +protected: + void connectPropertyManager(QtIntPropertyManager *manager); + QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtIntPropertyManager *manager); +private: + QtSpinBoxFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSpinBoxFactory) + Q_DISABLE_COPY(QtSpinBoxFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtSliderFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSliderFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtSliderFactory(QObject *parent = 0); + ~QtSliderFactory(); +protected: + void connectPropertyManager(QtIntPropertyManager *manager); + QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtIntPropertyManager *manager); +private: + QtSliderFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSliderFactory) + Q_DISABLE_COPY(QtSliderFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtScrollBarFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtScrollBarFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtScrollBarFactory(QObject *parent = 0); + ~QtScrollBarFactory(); +protected: + void connectPropertyManager(QtIntPropertyManager *manager); + QWidget *createEditor(QtIntPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtIntPropertyManager *manager); +private: + QtScrollBarFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtScrollBarFactory) + Q_DISABLE_COPY(QtScrollBarFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtCheckBoxFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCheckBoxFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtCheckBoxFactory(QObject *parent = 0); + ~QtCheckBoxFactory(); +protected: + void connectPropertyManager(QtBoolPropertyManager *manager); + QWidget *createEditor(QtBoolPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtBoolPropertyManager *manager); +private: + QtCheckBoxFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCheckBoxFactory) + Q_DISABLE_COPY(QtCheckBoxFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, bool)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(bool)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtDoubleSpinBoxFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDoubleSpinBoxFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtDoubleSpinBoxFactory(QObject *parent = 0); + ~QtDoubleSpinBoxFactory(); +protected: + void connectPropertyManager(QtDoublePropertyManager *manager); + QWidget *createEditor(QtDoublePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtDoublePropertyManager *manager); +private: + QtDoubleSpinBoxFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDoubleSpinBoxFactory) + Q_DISABLE_COPY(QtDoubleSpinBoxFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, double, double)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotDecimalsChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(double)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtLineEditFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtLineEditFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtLineEditFactory(QObject *parent = 0); + ~QtLineEditFactory(); +protected: + void connectPropertyManager(QtStringPropertyManager *manager); + QWidget *createEditor(QtStringPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtStringPropertyManager *manager); +private: + QtLineEditFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtLineEditFactory) + Q_DISABLE_COPY(QtLineEditFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QString &)) + Q_PRIVATE_SLOT(d_func(), void slotRegExpChanged(QtProperty *, const QRegExp &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QString &)) + Q_PRIVATE_SLOT(d_func(), void slotEditingFinished()) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtDateEditFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDateEditFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtDateEditFactory(QObject *parent = 0); + ~QtDateEditFactory(); +protected: + void connectPropertyManager(QtDatePropertyManager *manager); + QWidget *createEditor(QtDatePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtDatePropertyManager *manager); +private: + QtDateEditFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDateEditFactory) + Q_DISABLE_COPY(QtDateEditFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, + const QDate &, const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtTimeEditFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtTimeEditFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtTimeEditFactory(QObject *parent = 0); + ~QtTimeEditFactory(); +protected: + void connectPropertyManager(QtTimePropertyManager *manager); + QWidget *createEditor(QtTimePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtTimePropertyManager *manager); +private: + QtTimeEditFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtTimeEditFactory) + Q_DISABLE_COPY(QtTimeEditFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QTime &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QTime &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtDateTimeEditFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDateTimeEditFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtDateTimeEditFactory(QObject *parent = 0); + ~QtDateTimeEditFactory(); +protected: + void connectPropertyManager(QtDateTimePropertyManager *manager); + QWidget *createEditor(QtDateTimePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtDateTimePropertyManager *manager); +private: + QtDateTimeEditFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDateTimeEditFactory) + Q_DISABLE_COPY(QtDateTimeEditFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QDateTime &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QDateTime &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtKeySequenceEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtKeySequenceEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtKeySequenceEditorFactory(QObject *parent = 0); + ~QtKeySequenceEditorFactory(); +protected: + void connectPropertyManager(QtKeySequencePropertyManager *manager); + QWidget *createEditor(QtKeySequencePropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtKeySequencePropertyManager *manager); +private: + QtKeySequenceEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtKeySequenceEditorFactory) + Q_DISABLE_COPY(QtKeySequenceEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QKeySequence &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QKeySequence &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtCharEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCharEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtCharEditorFactory(QObject *parent = 0); + ~QtCharEditorFactory(); +protected: + void connectPropertyManager(QtCharPropertyManager *manager); + QWidget *createEditor(QtCharPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtCharPropertyManager *manager); +private: + QtCharEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCharEditorFactory) + Q_DISABLE_COPY(QtCharEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QChar &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QChar &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtEnumEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtEnumEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtEnumEditorFactory(QObject *parent = 0); + ~QtEnumEditorFactory(); +protected: + void connectPropertyManager(QtEnumPropertyManager *manager); + QWidget *createEditor(QtEnumPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtEnumPropertyManager *manager); +private: + QtEnumEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtEnumEditorFactory) + Q_DISABLE_COPY(QtEnumEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotEnumNamesChanged(QtProperty *, + const QStringList &)) + Q_PRIVATE_SLOT(d_func(), void slotEnumIconsChanged(QtProperty *, + const QMap &)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtCursorEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCursorEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtCursorEditorFactory(QObject *parent = 0); + ~QtCursorEditorFactory(); +protected: + void connectPropertyManager(QtCursorPropertyManager *manager); + QWidget *createEditor(QtCursorPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtCursorPropertyManager *manager); +private: + QtCursorEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCursorEditorFactory) + Q_DISABLE_COPY(QtCursorEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QCursor &)) + Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) +}; + +class QtColorEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtColorEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtColorEditorFactory(QObject *parent = 0); + ~QtColorEditorFactory(); +protected: + void connectPropertyManager(QtColorPropertyManager *manager); + QWidget *createEditor(QtColorPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtColorPropertyManager *manager); +private: + QtColorEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtColorEditorFactory) + Q_DISABLE_COPY(QtColorEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QColor &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QColor &)) +}; + +class QtFontEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtFontEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtFontEditorFactory(QObject *parent = 0); + ~QtFontEditorFactory(); +protected: + void connectPropertyManager(QtFontPropertyManager *manager); + QWidget *createEditor(QtFontPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtFontPropertyManager *manager); +private: + QtFontEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtFontEditorFactory) + Q_DISABLE_COPY(QtFontEditorFactory) + Q_PRIVATE_SLOT(d_func(), void slotPropertyChanged(QtProperty *, const QFont &)) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed(QObject *)) + Q_PRIVATE_SLOT(d_func(), void slotSetValue(const QFont &)) +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtgroupboxpropertybrowser.cpp b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtgroupboxpropertybrowser.cpp new file mode 100644 index 000000000..d789c8695 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtgroupboxpropertybrowser.cpp @@ -0,0 +1,578 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtgroupboxpropertybrowser.h" +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtGroupBoxPropertyBrowserPrivate +{ + QtGroupBoxPropertyBrowser *q_ptr; + Q_DECLARE_PUBLIC(QtGroupBoxPropertyBrowser) +public: + + void init(QWidget *parent); + + void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); + void propertyRemoved(QtBrowserItem *index); + void propertyChanged(QtBrowserItem *index); + QWidget *createEditor(QtProperty *property, QWidget *parent) const + { return q_ptr->createEditor(property, parent); } + + void slotEditorDestroyed(); + void slotUpdate(); + + struct WidgetItem + { + WidgetItem() : widget(0), label(0), widgetLabel(0), + groupBox(0), layout(0), line(0), parent(0) { } + QWidget *widget; // can be null + QLabel *label; + QLabel *widgetLabel; + QGroupBox *groupBox; + QGridLayout *layout; + QFrame *line; + WidgetItem *parent; + QList children; + }; +private: + void updateLater(); + void updateItem(WidgetItem *item); + void insertRow(QGridLayout *layout, int row) const; + void removeRow(QGridLayout *layout, int row) const; + + bool hasHeader(WidgetItem *item) const; + + QMap m_indexToItem; + QMap m_itemToIndex; + QMap m_widgetToItem; + QGridLayout *m_mainLayout; + QList m_children; + QList m_recreateQueue; +}; + +void QtGroupBoxPropertyBrowserPrivate::init(QWidget *parent) +{ + m_mainLayout = new QGridLayout(); + parent->setLayout(m_mainLayout); + QLayoutItem *item = new QSpacerItem(0, 0, + QSizePolicy::Fixed, QSizePolicy::Expanding); + m_mainLayout->addItem(item, 0, 0); +} + +void QtGroupBoxPropertyBrowserPrivate::slotEditorDestroyed() +{ + QWidget *editor = qobject_cast(q_ptr->sender()); + if (!editor) + return; + if (!m_widgetToItem.contains(editor)) + return; + m_widgetToItem[editor]->widget = 0; + m_widgetToItem.remove(editor); +} + +void QtGroupBoxPropertyBrowserPrivate::slotUpdate() +{ + QListIterator itItem(m_recreateQueue); + while (itItem.hasNext()) { + WidgetItem *item = itItem.next(); + + WidgetItem *par = item->parent; + QWidget *w = 0; + QGridLayout *l = 0; + int oldRow = -1; + if (!par) { + w = q_ptr; + l = m_mainLayout; + oldRow = m_children.indexOf(item); + } else { + w = par->groupBox; + l = par->layout; + oldRow = par->children.indexOf(item); + if (hasHeader(par)) + oldRow += 2; + } + + if (item->widget) { + item->widget->setParent(w); + } else if (item->widgetLabel) { + item->widgetLabel->setParent(w); + } else { + item->widgetLabel = new QLabel(w); + } + int span = 1; + if (item->widget) + l->addWidget(item->widget, oldRow, 1, 1, 1); + else if (item->widgetLabel) + l->addWidget(item->widgetLabel, oldRow, 1, 1, 1); + else + span = 2; + item->label = new QLabel(w); + item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + l->addWidget(item->label, oldRow, 0, 1, span); + + updateItem(item); + } + m_recreateQueue.clear(); +} + +void QtGroupBoxPropertyBrowserPrivate::updateLater() +{ + QTimer::singleShot(0, q_ptr, SLOT(slotUpdate())); +} + +void QtGroupBoxPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) +{ + WidgetItem *afterItem = m_indexToItem.value(afterIndex); + WidgetItem *parentItem = m_indexToItem.value(index->parent()); + + WidgetItem *newItem = new WidgetItem(); + newItem->parent = parentItem; + + QGridLayout *layout = 0; + QWidget *parentWidget = 0; + int row = -1; + if (!afterItem) { + row = 0; + if (parentItem) + parentItem->children.insert(0, newItem); + else + m_children.insert(0, newItem); + } else { + if (parentItem) { + row = parentItem->children.indexOf(afterItem) + 1; + parentItem->children.insert(row, newItem); + } else { + row = m_children.indexOf(afterItem) + 1; + m_children.insert(row, newItem); + } + } + if (parentItem && hasHeader(parentItem)) + row += 2; + + if (!parentItem) { + layout = m_mainLayout; + parentWidget = q_ptr;; + } else { + if (!parentItem->groupBox) { + m_recreateQueue.removeAll(parentItem); + WidgetItem *par = parentItem->parent; + QWidget *w = 0; + QGridLayout *l = 0; + int oldRow = -1; + if (!par) { + w = q_ptr; + l = m_mainLayout; + oldRow = m_children.indexOf(parentItem); + } else { + w = par->groupBox; + l = par->layout; + oldRow = par->children.indexOf(parentItem); + if (hasHeader(par)) + oldRow += 2; + } + parentItem->groupBox = new QGroupBox(w); + parentItem->layout = new QGridLayout(); + parentItem->groupBox->setLayout(parentItem->layout); + if (parentItem->label) { + l->removeWidget(parentItem->label); + delete parentItem->label; + parentItem->label = 0; + } + if (parentItem->widget) { + l->removeWidget(parentItem->widget); + parentItem->widget->setParent(parentItem->groupBox); + parentItem->layout->addWidget(parentItem->widget, 0, 0, 1, 2); + parentItem->line = new QFrame(parentItem->groupBox); + } else if (parentItem->widgetLabel) { + l->removeWidget(parentItem->widgetLabel); + delete parentItem->widgetLabel; + parentItem->widgetLabel = 0; + } + if (parentItem->line) { + parentItem->line->setFrameShape(QFrame::HLine); + parentItem->line->setFrameShadow(QFrame::Sunken); + parentItem->layout->addWidget(parentItem->line, 1, 0, 1, 2); + } + l->addWidget(parentItem->groupBox, oldRow, 0, 1, 2); + updateItem(parentItem); + } + layout = parentItem->layout; + parentWidget = parentItem->groupBox; + } + + newItem->label = new QLabel(parentWidget); + newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); + newItem->widget = createEditor(index->property(), parentWidget); + if (!newItem->widget) { + newItem->widgetLabel = new QLabel(parentWidget); + } else { + QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed())); + m_widgetToItem[newItem->widget] = newItem; + } + + insertRow(layout, row); + int span = 1; + if (newItem->widget) + layout->addWidget(newItem->widget, row, 1); + else if (newItem->widgetLabel) + layout->addWidget(newItem->widgetLabel, row, 1); + else + span = 2; + layout->addWidget(newItem->label, row, 0, 1, span); + + m_itemToIndex[newItem] = index; + m_indexToItem[index] = newItem; + + updateItem(newItem); +} + +void QtGroupBoxPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) +{ + WidgetItem *item = m_indexToItem.value(index); + + m_indexToItem.remove(index); + m_itemToIndex.remove(item); + + WidgetItem *parentItem = item->parent; + + int row = -1; + + if (parentItem) { + row = parentItem->children.indexOf(item); + parentItem->children.removeAt(row); + if (hasHeader(parentItem)) + row += 2; + } else { + row = m_children.indexOf(item); + m_children.removeAt(row); + } + + if (item->widget) + delete item->widget; + if (item->label) + delete item->label; + if (item->widgetLabel) + delete item->widgetLabel; + if (item->groupBox) + delete item->groupBox; + + if (!parentItem) { + removeRow(m_mainLayout, row); + } else if (parentItem->children.count() != 0) { + removeRow(parentItem->layout, row); + } else { + WidgetItem *par = parentItem->parent; + QWidget *w = 0; + QGridLayout *l = 0; + int oldRow = -1; + if (!par) { + w = q_ptr; + l = m_mainLayout; + oldRow = m_children.indexOf(parentItem); + } else { + w = par->groupBox; + l = par->layout; + oldRow = par->children.indexOf(parentItem); + if (hasHeader(par)) + oldRow += 2; + } + + if (parentItem->widget) { + parentItem->widget->hide(); + parentItem->widget->setParent(0); + } else if (parentItem->widgetLabel) { + parentItem->widgetLabel->hide(); + parentItem->widgetLabel->setParent(0); + } else { + //parentItem->widgetLabel = new QLabel(w); + } + l->removeWidget(parentItem->groupBox); + delete parentItem->groupBox; + parentItem->groupBox = 0; + parentItem->line = 0; + parentItem->layout = 0; + if (!m_recreateQueue.contains(parentItem)) + m_recreateQueue.append(parentItem); + updateLater(); + } + m_recreateQueue.removeAll(item); + + delete item; +} + +void QtGroupBoxPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const +{ + QMap itemToPos; + int idx = 0; + while (idx < layout->count()) { + int r, c, rs, cs; + layout->getItemPosition(idx, &r, &c, &rs, &cs); + if (r >= row) { + itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs); + } else { + idx++; + } + } + + const QMap::ConstIterator icend = itemToPos.constEnd(); + for (QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { + const QRect r = it.value(); + layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); + } +} + +void QtGroupBoxPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const +{ + QMap itemToPos; + int idx = 0; + while (idx < layout->count()) { + int r, c, rs, cs; + layout->getItemPosition(idx, &r, &c, &rs, &cs); + if (r > row) { + itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs); + } else { + idx++; + } + } + + const QMap::ConstIterator icend = itemToPos.constEnd(); + for (QMap::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) { + const QRect r = it.value(); + layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height()); + } +} + +bool QtGroupBoxPropertyBrowserPrivate::hasHeader(WidgetItem *item) const +{ + if (item->widget) + return true; + return false; +} + +void QtGroupBoxPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) +{ + WidgetItem *item = m_indexToItem.value(index); + + updateItem(item); +} + +void QtGroupBoxPropertyBrowserPrivate::updateItem(WidgetItem *item) +{ + QtProperty *property = m_itemToIndex[item]->property(); + if (item->groupBox) { + QFont font = item->groupBox->font(); + font.setUnderline(property->isModified()); + item->groupBox->setFont(font); + item->groupBox->setTitle(property->propertyName()); + item->groupBox->setToolTip(property->toolTip()); + item->groupBox->setStatusTip(property->statusTip()); + item->groupBox->setWhatsThis(property->whatsThis()); + item->groupBox->setEnabled(property->isEnabled()); + } + if (item->label) { + QFont font = item->label->font(); + font.setUnderline(property->isModified()); + item->label->setFont(font); + item->label->setText(property->propertyName()); + item->label->setToolTip(property->toolTip()); + item->label->setStatusTip(property->statusTip()); + item->label->setWhatsThis(property->whatsThis()); + item->label->setEnabled(property->isEnabled()); + } + if (item->widgetLabel) { + QFont font = item->widgetLabel->font(); + font.setUnderline(false); + item->widgetLabel->setFont(font); + item->widgetLabel->setText(property->valueText()); + item->widgetLabel->setEnabled(property->isEnabled()); + } + if (item->widget) { + QFont font = item->widget->font(); + font.setUnderline(false); + item->widget->setFont(font); + item->widget->setEnabled(property->isEnabled()); + item->widget->setToolTip(property->valueText()); + } + //item->setIcon(1, property->valueIcon()); +} + + + +/*! + \class QtGroupBoxPropertyBrowser + + \brief The QtGroupBoxPropertyBrowser class provides a QGroupBox + based property browser. + + A property browser is a widget that enables the user to edit a + given set of properties. Each property is represented by a label + specifying the property's name, and an editing widget (e.g. a line + edit or a combobox) holding its value. A property can have zero or + more subproperties. + + QtGroupBoxPropertyBrowser provides group boxes for all nested + properties, i.e. subproperties are enclosed by a group box with + the parent property's name as its title. For example: + + \image qtgroupboxpropertybrowser.png + + Use the QtAbstractPropertyBrowser API to add, insert and remove + properties from an instance of the QtGroupBoxPropertyBrowser + class. The properties themselves are created and managed by + implementations of the QtAbstractPropertyManager class. + + \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser +*/ + +/*! + Creates a property browser with the given \a parent. +*/ +QtGroupBoxPropertyBrowser::QtGroupBoxPropertyBrowser(QWidget *parent) + : QtAbstractPropertyBrowser(parent) +{ + d_ptr = new QtGroupBoxPropertyBrowserPrivate; + d_ptr->q_ptr = this; + + d_ptr->init(this); +} + +/*! + Destroys this property browser. + + Note that the properties that were inserted into this browser are + \e not destroyed since they may still be used in other + browsers. The properties are owned by the manager that created + them. + + \sa QtProperty, QtAbstractPropertyManager +*/ +QtGroupBoxPropertyBrowser::~QtGroupBoxPropertyBrowser() +{ + const QMap::ConstIterator icend = d_ptr->m_itemToIndex.constEnd(); + for (QMap::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it) + delete it.key(); + delete d_ptr; +} + +/*! + \reimp +*/ +void QtGroupBoxPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) +{ + d_ptr->propertyInserted(item, afterItem); +} + +/*! + \reimp +*/ +void QtGroupBoxPropertyBrowser::itemRemoved(QtBrowserItem *item) +{ + d_ptr->propertyRemoved(item); +} + +/*! + \reimp +*/ +void QtGroupBoxPropertyBrowser::itemChanged(QtBrowserItem *item) +{ + d_ptr->propertyChanged(item); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtgroupboxpropertybrowser.cxx" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtgroupboxpropertybrowser.h b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtgroupboxpropertybrowser.h new file mode 100644 index 000000000..075f3286e --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtgroupboxpropertybrowser.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTGROUPBOXPROPERTYBROWSER_H +#define QTGROUPBOXPROPERTYBROWSER_H + +#include "qtpropertybrowser.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtGroupBoxPropertyBrowserPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtGroupBoxPropertyBrowser : public QtAbstractPropertyBrowser +{ + Q_OBJECT +public: + + QtGroupBoxPropertyBrowser(QWidget *parent = 0); + ~QtGroupBoxPropertyBrowser(); + +protected: + virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); + virtual void itemRemoved(QtBrowserItem *item); + virtual void itemChanged(QtBrowserItem *item); + +private: + + QtGroupBoxPropertyBrowserPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtGroupBoxPropertyBrowser) + Q_DISABLE_COPY(QtGroupBoxPropertyBrowser) + Q_PRIVATE_SLOT(d_func(), void slotUpdate()) + Q_PRIVATE_SLOT(d_func(), void slotEditorDestroyed()) + +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.cpp b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.cpp new file mode 100644 index 000000000..e8c103d9b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.cpp @@ -0,0 +1,2053 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtpropertybrowser.h" +#include +#include +#include + +#if defined(Q_CC_MSVC) +# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ +#endif + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtPropertyPrivate +{ +public: + QtPropertyPrivate(QtAbstractPropertyManager *manager) : m_enabled(true), m_modified(false), m_bold(false), m_manager(manager) {} + QtProperty *q_ptr; + + QSet m_parentItems; + QList m_subItems; + + QString m_toolTip; + QString m_statusTip; + QString m_whatsThis; + QString m_name; + bool m_enabled; + bool m_modified; + bool m_bold; + + QtAbstractPropertyManager * const m_manager; +}; + +class QtAbstractPropertyManagerPrivate +{ + QtAbstractPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtAbstractPropertyManager) +public: + void propertyDestroyed(QtProperty *property); + void propertyChanged(QtProperty *property) const; + void propertyRemoved(QtProperty *property, + QtProperty *parentProperty) const; + void propertyInserted(QtProperty *property, QtProperty *parentProperty, + QtProperty *afterProperty) const; + + QSet m_properties; +}; + +/*! + \class QtProperty + + \brief The QtProperty class encapsulates an instance of a property. + + Properties are created by objects of QtAbstractPropertyManager + subclasses; a manager can create properties of a given type, and + is used in conjunction with the QtAbstractPropertyBrowser class. A + property is always owned by the manager that created it, which can + be retrieved using the propertyManager() function. + + QtProperty contains the most common property attributes, and + provides functions for retrieving as well as setting their values: + + \table + \header \o Getter \o Setter + \row + \o propertyName() \o setPropertyName() + \row + \o statusTip() \o setStatusTip() + \row + \o toolTip() \o setToolTip() + \row + \o whatsThis() \o setWhatsThis() + \row + \o isEnabled() \o setEnabled() + \row + \o isModified() \o setModified() + \row + \o valueText() \o Nop + \row + \o valueIcon() \o Nop + \endtable + + It is also possible to nest properties: QtProperty provides the + addSubProperty(), insertSubProperty() and removeSubProperty() functions to + manipulate the set of subproperties. Use the subProperties() + function to retrieve a property's current set of subproperties. + Note that nested properties are not owned by the parent property, + i.e. each subproperty is owned by the manager that created it. + + \sa QtAbstractPropertyManager, QtBrowserItem +*/ + +/*! + Creates a property with the given \a manager. + + This constructor is only useful when creating a custom QtProperty + subclass (e.g. QtVariantProperty). To create a regular QtProperty + object, use the QtAbstractPropertyManager::addProperty() + function instead. + + \sa QtAbstractPropertyManager::addProperty() +*/ +QtProperty::QtProperty(QtAbstractPropertyManager *manager) +{ + d_ptr = new QtPropertyPrivate(manager); + d_ptr->q_ptr = this; +} + +/*! + Destroys this property. + + Note that subproperties are detached but not destroyed, i.e. they + can still be used in another context. + + \sa QtAbstractPropertyManager::clear() + +*/ +QtProperty::~QtProperty() +{ + QSetIterator itParent(d_ptr->m_parentItems); + while (itParent.hasNext()) { + QtProperty *property = itParent.next(); + property->d_ptr->m_manager->d_ptr->propertyRemoved(this, property); + } + + d_ptr->m_manager->d_ptr->propertyDestroyed(this); + + QListIterator itChild(d_ptr->m_subItems); + while (itChild.hasNext()) { + QtProperty *property = itChild.next(); + property->d_ptr->m_parentItems.remove(this); + } + + itParent.toFront(); + while (itParent.hasNext()) { + QtProperty *property = itParent.next(); + property->d_ptr->m_subItems.removeAll(this); + } + delete d_ptr; +} + +/*! + Returns the set of subproperties. + + Note that subproperties are not owned by \e this property, but by + the manager that created them. + + \sa insertSubProperty(), removeSubProperty() +*/ +QList QtProperty::subProperties() const +{ + return d_ptr->m_subItems; +} + +/*! + Returns a pointer to the manager that owns this property. +*/ +QtAbstractPropertyManager *QtProperty::propertyManager() const +{ + return d_ptr->m_manager; +} + +/*! + Returns the property's tool tip. + + \sa setToolTip() +*/ +QString QtProperty::toolTip() const +{ + return d_ptr->m_toolTip; +} + +/*! + Returns the property's status tip. + + \sa setStatusTip() +*/ +QString QtProperty::statusTip() const +{ + return d_ptr->m_statusTip; +} + +/*! + Returns the property's "What's This" help text. + + \sa setWhatsThis() +*/ +QString QtProperty::whatsThis() const +{ + return d_ptr->m_whatsThis; +} + +/*! + Returns the property's name. + + \sa setPropertyName() +*/ +QString QtProperty::propertyName() const +{ + return d_ptr->m_name; +} + +/*! + Returns whether the property is enabled. + + \sa setEnabled() +*/ +bool QtProperty::isEnabled() const +{ + return d_ptr->m_enabled; +} + +/*! + Returns whether the property is modified. + + \sa setModified() +*/ +bool QtProperty::isModified() const +{ + return d_ptr->m_modified; +} + +/*! + Returns whether the property is displayed as bold. + + \sa setBold() +*/ +bool QtProperty::isBold() const +{ + return d_ptr->m_bold; +} + +/*! + Returns whether the property has a value. + + \sa QtAbstractPropertyManager::hasValue() +*/ +bool QtProperty::hasValue() const +{ + return d_ptr->m_manager->hasValue(this); +} + +/*! + Returns an icon representing the current state of this property. + + If the given property type can not generate such an icon, this + function returns an invalid icon. + + \sa QtAbstractPropertyManager::valueIcon() +*/ +QIcon QtProperty::valueIcon() const +{ + return d_ptr->m_manager->valueIcon(this); +} + +/*! + Returns a string representing the current state of this property. + + If the given property type can not generate such a string, this + function returns an empty string. + + \sa QtAbstractPropertyManager::valueText() +*/ +QString QtProperty::valueText() const +{ + return d_ptr->m_manager->valueText(this); +} + +/*! + Sets the property's tool tip to the given \a text. + + \sa toolTip() +*/ +void QtProperty::setToolTip(const QString &text) +{ + if (d_ptr->m_toolTip == text) + return; + + d_ptr->m_toolTip = text; + propertyChanged(); +} + +/*! + Sets the property's status tip to the given \a text. + + \sa statusTip() +*/ +void QtProperty::setStatusTip(const QString &text) +{ + if (d_ptr->m_statusTip == text) + return; + + d_ptr->m_statusTip = text; + propertyChanged(); +} + +/*! + Sets the property's "What's This" help text to the given \a text. + + \sa whatsThis() +*/ +void QtProperty::setWhatsThis(const QString &text) +{ + if (d_ptr->m_whatsThis == text) + return; + + d_ptr->m_whatsThis = text; + propertyChanged(); +} + +/*! + \fn void QtProperty::setPropertyName(const QString &name) + + Sets the property's name to the given \a name. + + \sa propertyName() +*/ +void QtProperty::setPropertyName(const QString &text) +{ + if (d_ptr->m_name == text) + return; + + d_ptr->m_name = text; + propertyChanged(); +} + +/*! + Enables or disables the property according to the passed \a enable value. + + \sa isEnabled() +*/ +void QtProperty::setEnabled(bool enable) +{ + if (d_ptr->m_enabled == enable) + return; + + d_ptr->m_enabled = enable; + propertyChanged(); +} + +/*! + Sets the property's modified state according to the passed \a modified value. + + \sa isModified() +*/ +void QtProperty::setModified(bool modified) +{ + if (d_ptr->m_modified == modified) + return; + + d_ptr->m_modified = modified; + propertyChanged(); +} + +/*! + Sets the property's bold state according to the passed \a bold value. + + \sa isBold() +*/ +void QtProperty::setBold(bool bold) +{ + if (d_ptr->m_bold == bold) + return; + + d_ptr->m_bold = bold; + propertyChanged(); +} + +/*! + Appends the given \a property to this property's subproperties. + + If the given \a property already is added, this function does + nothing. + + \sa insertSubProperty(), removeSubProperty() +*/ +void QtProperty::addSubProperty(QtProperty *property) +{ + QtProperty *after = 0; + if (d_ptr->m_subItems.count() > 0) + after = d_ptr->m_subItems.last(); + insertSubProperty(property, after); +} + +/*! + \fn void QtProperty::insertSubProperty(QtProperty *property, QtProperty *precedingProperty) + + Inserts the given \a property after the specified \a + precedingProperty into this property's list of subproperties. If + \a precedingProperty is 0, the specified \a property is inserted + at the beginning of the list. + + If the given \a property already is inserted, this function does + nothing. + + \sa addSubProperty(), removeSubProperty() +*/ +void QtProperty::insertSubProperty(QtProperty *property, + QtProperty *afterProperty) +{ + if (!property) + return; + + if (property == this) + return; + + // traverse all children of item. if this item is a child of item then cannot add. + QList pendingList = property->subProperties(); + QMap visited; + while (!pendingList.isEmpty()) { + QtProperty *i = pendingList.first(); + if (i == this) + return; + pendingList.removeFirst(); + if (visited.contains(i)) + continue; + visited[i] = true; + pendingList += i->subProperties(); + } + + pendingList = subProperties(); + int pos = 0; + int newPos = 0; + QtProperty *properAfterProperty = 0; + while (pos < pendingList.count()) { + QtProperty *i = pendingList.at(pos); + if (i == property) + return; // if item is already inserted in this item then cannot add. + if (i == afterProperty) { + newPos = pos + 1; + properAfterProperty = afterProperty; + } + pos++; + } + + d_ptr->m_subItems.insert(newPos, property); + property->d_ptr->m_parentItems.insert(this); + + d_ptr->m_manager->d_ptr->propertyInserted(property, this, properAfterProperty); +} + +/*! + Removes the given \a property from the list of subproperties + without deleting it. + + \sa addSubProperty(), insertSubProperty() +*/ +void QtProperty::removeSubProperty(QtProperty *property) +{ + if (!property) + return; + + d_ptr->m_manager->d_ptr->propertyRemoved(property, this); + + QList pendingList = subProperties(); + int pos = 0; + while (pos < pendingList.count()) { + if (pendingList.at(pos) == property) { + d_ptr->m_subItems.removeAt(pos); + property->d_ptr->m_parentItems.remove(this); + + return; + } + pos++; + } +} + +/*! + \internal +*/ +void QtProperty::propertyChanged() +{ + d_ptr->m_manager->d_ptr->propertyChanged(this); +} + +//////////////////////////////// + +void QtAbstractPropertyManagerPrivate::propertyDestroyed(QtProperty *property) +{ + if (m_properties.contains(property)) { + emit q_ptr->propertyDestroyed(property); + q_ptr->uninitializeProperty(property); + m_properties.remove(property); + } +} + +void QtAbstractPropertyManagerPrivate::propertyChanged(QtProperty *property) const +{ + emit q_ptr->propertyChanged(property); +} + +void QtAbstractPropertyManagerPrivate::propertyRemoved(QtProperty *property, + QtProperty *parentProperty) const +{ + emit q_ptr->propertyRemoved(property, parentProperty); +} + +void QtAbstractPropertyManagerPrivate::propertyInserted(QtProperty *property, + QtProperty *parentProperty, QtProperty *afterProperty) const +{ + emit q_ptr->propertyInserted(property, parentProperty, afterProperty); +} + +/*! + \class QtAbstractPropertyManager + + \brief The QtAbstractPropertyManager provides an interface for + property managers. + + A manager can create and manage properties of a given type, and is + used in conjunction with the QtAbstractPropertyBrowser class. + + When using a property browser widget, the properties are created + and managed by implementations of the QtAbstractPropertyManager + class. To ensure that the properties' values will be displayed + using suitable editing widgets, the managers are associated with + objects of QtAbstractEditorFactory subclasses. The property browser + will use these associations to determine which factories it should + use to create the preferred editing widgets. + + The QtAbstractPropertyManager class provides common functionality + like creating a property using the addProperty() function, and + retrieving the properties created by the manager using the + properties() function. The class also provides signals that are + emitted when the manager's properties change: propertyInserted(), + propertyRemoved(), propertyChanged() and propertyDestroyed(). + + QtAbstractPropertyManager subclasses are supposed to provide their + own type specific API. Note that several ready-made + implementations are available: + + \list + \o QtBoolPropertyManager + \o QtColorPropertyManager + \o QtDatePropertyManager + \o QtDateTimePropertyManager + \o QtDoublePropertyManager + \o QtEnumPropertyManager + \o QtFlagPropertyManager + \o QtFontPropertyManager + \o QtGroupPropertyManager + \o QtIntPropertyManager + \o QtPointPropertyManager + \o QtRectPropertyManager + \o QtSizePropertyManager + \o QtSizePolicyPropertyManager + \o QtStringPropertyManager + \o QtTimePropertyManager + \o QtVariantPropertyManager + \endlist + + \sa QtAbstractEditorFactoryBase, QtAbstractPropertyBrowser, QtProperty +*/ + +/*! + \fn void QtAbstractPropertyManager::propertyInserted(QtProperty *newProperty, + QtProperty *parentProperty, QtProperty *precedingProperty) + + This signal is emitted when a new subproperty is inserted into an + existing property, passing pointers to the \a newProperty, \a + parentProperty and \a precedingProperty as parameters. + + If \a precedingProperty is 0, the \a newProperty was inserted at + the beginning of the \a parentProperty's subproperties list. + + Note that signal is emitted only if the \a parentProperty is created + by this manager. + + \sa QtAbstractPropertyBrowser::itemInserted() +*/ + +/*! + \fn void QtAbstractPropertyManager::propertyChanged(QtProperty *property) + + This signal is emitted whenever a property's data changes, passing + a pointer to the \a property as parameter. + + Note that signal is only emitted for properties that are created by + this manager. + + \sa QtAbstractPropertyBrowser::itemChanged() +*/ + +/*! + \fn void QtAbstractPropertyManager::propertyRemoved(QtProperty *property, QtProperty *parent) + + This signal is emitted when a subproperty is removed, passing + pointers to the removed \a property and the \a parent property as + parameters. + + Note that signal is emitted only when the \a parent property is + created by this manager. + + \sa QtAbstractPropertyBrowser::itemRemoved() +*/ + +/*! + \fn void QtAbstractPropertyManager::propertyDestroyed(QtProperty *property) + + This signal is emitted when the specified \a property is about to + be destroyed. + + Note that signal is only emitted for properties that are created + by this manager. + + \sa clear(), uninitializeProperty() +*/ + +/*! + \fn void QtAbstractPropertyBrowser::currentItemChanged(QtBrowserItem *current) + + This signal is emitted when the current item changes. The current item is specified by \a current. + + \sa QtAbstractPropertyBrowser::setCurrentItem() +*/ + +/*! + Creates an abstract property manager with the given \a parent. +*/ +QtAbstractPropertyManager::QtAbstractPropertyManager(QObject *parent) + : QObject(parent) +{ + d_ptr = new QtAbstractPropertyManagerPrivate; + d_ptr->q_ptr = this; + +} + +/*! + Destroys the manager. All properties created by the manager are + destroyed. +*/ +QtAbstractPropertyManager::~QtAbstractPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Destroys all the properties that this manager has created. + + \sa propertyDestroyed(), uninitializeProperty() +*/ +void QtAbstractPropertyManager::clear() const +{ + while (!properties().isEmpty()) { + QSetIterator itProperty(properties()); + QtProperty *prop = itProperty.next(); + delete prop; + } +} + +/*! + Returns the set of properties created by this manager. + + \sa addProperty() +*/ +QSet QtAbstractPropertyManager::properties() const +{ + return d_ptr->m_properties; +} + +/*! + Returns whether the given \a property has a value. + + The default implementation of this function returns true. + + \sa QtProperty::hasValue() +*/ +bool QtAbstractPropertyManager::hasValue(const QtProperty *property) const +{ + Q_UNUSED(property) + return true; +} + +/*! + Returns an icon representing the current state of the given \a + property. + + The default implementation of this function returns an invalid + icon. + + \sa QtProperty::valueIcon() +*/ +QIcon QtAbstractPropertyManager::valueIcon(const QtProperty *property) const +{ + Q_UNUSED(property) + return QIcon(); +} + +/*! + Returns a string representing the current state of the given \a + property. + + The default implementation of this function returns an empty + string. + + \sa QtProperty::valueText() +*/ +QString QtAbstractPropertyManager::valueText(const QtProperty *property) const +{ + Q_UNUSED(property) + return QString(); +} + +/*! + Creates a property with the given \a name which then is owned by this manager. + + Internally, this function calls the createProperty() and + initializeProperty() functions. + + \sa initializeProperty(), properties() +*/ +QtProperty *QtAbstractPropertyManager::addProperty(const QString &name) +{ + QtProperty *property = createProperty(); + if (property) { + property->setPropertyName(name); + d_ptr->m_properties.insert(property); + initializeProperty(property); + } + return property; +} + +/*! + Creates a property. + + The base implementation produce QtProperty instances; Reimplement + this function to make this manager produce objects of a QtProperty + subclass. + + \sa addProperty(), initializeProperty() +*/ +QtProperty *QtAbstractPropertyManager::createProperty() +{ + return new QtProperty(this); +} + +/*! + \fn void QtAbstractPropertyManager::initializeProperty(QtProperty *property) = 0 + + This function is called whenever a new valid property pointer has + been created, passing the pointer as parameter. + + The purpose is to let the manager know that the \a property has + been created so that it can provide additional attributes for the + new property, e.g. QtIntPropertyManager adds \l + {QtIntPropertyManager::value()}{value}, \l + {QtIntPropertyManager::minimum()}{minimum} and \l + {QtIntPropertyManager::maximum()}{maximum} attributes. Since each manager + subclass adds type specific attributes, this function is pure + virtual and must be reimplemented when deriving from the + QtAbstractPropertyManager class. + + \sa addProperty(), createProperty() +*/ + +/*! + This function is called just before the specified \a property is destroyed. + + The purpose is to let the property manager know that the \a + property is being destroyed so that it can remove the property's + additional attributes. + + \sa clear(), propertyDestroyed() +*/ +void QtAbstractPropertyManager::uninitializeProperty(QtProperty *property) +{ + Q_UNUSED(property) +} + +//////////////////////////////////// + +/*! + \class QtAbstractEditorFactoryBase + + \brief The QtAbstractEditorFactoryBase provides an interface for + editor factories. + + An editor factory is a class that is able to create an editing + widget of a specified type (e.g. line edits or comboboxes) for a + given QtProperty object, and it is used in conjunction with the + QtAbstractPropertyManager and QtAbstractPropertyBrowser classes. + + When using a property browser widget, the properties are created + and managed by implementations of the QtAbstractPropertyManager + class. To ensure that the properties' values will be displayed + using suitable editing widgets, the managers are associated with + objects of QtAbstractEditorFactory subclasses. The property browser + will use these associations to determine which factories it should + use to create the preferred editing widgets. + + Typically, an editor factory is created by subclassing the + QtAbstractEditorFactory template class which inherits + QtAbstractEditorFactoryBase. But note that several ready-made + implementations are available: + + \list + \o QtCheckBoxFactory + \o QtDateEditFactory + \o QtDateTimeEditFactory + \o QtDoubleSpinBoxFactory + \o QtEnumEditorFactory + \o QtLineEditFactory + \o QtScrollBarFactory + \o QtSliderFactory + \o QtSpinBoxFactory + \o QtTimeEditFactory + \o QtVariantEditorFactory + \endlist + + \sa QtAbstractPropertyManager, QtAbstractPropertyBrowser +*/ + +/*! + \fn virtual QWidget *QtAbstractEditorFactoryBase::createEditor(QtProperty *property, + QWidget *parent) = 0 + + Creates an editing widget (with the given \a parent) for the given + \a property. + + This function is reimplemented in QtAbstractEditorFactory template class + which also provides a pure virtual convenience overload of this + function enabling access to the property's manager. + + \sa QtAbstractEditorFactory::createEditor() +*/ + +/*! + \fn QtAbstractEditorFactoryBase::QtAbstractEditorFactoryBase(QObject *parent = 0) + + Creates an abstract editor factory with the given \a parent. +*/ + +/*! + \fn virtual void QtAbstractEditorFactoryBase::breakConnection(QtAbstractPropertyManager *manager) = 0 + + \internal + + Detaches property manager from factory. + This method is reimplemented in QtAbstractEditorFactory template subclass. + You don't need to reimplement it in your subclasses. Instead implement more convenient + QtAbstractEditorFactory::disconnectPropertyManager() which gives you access to particular manager subclass. +*/ + +/*! + \fn virtual void QtAbstractEditorFactoryBase::managerDestroyed(QObject *manager) = 0 + + \internal + + This method is called when property manager is being destroyed. + Basically it notifies factory not to produce editors for properties owned by \a manager. + You don't need to reimplement it in your subclass. This method is implemented in + QtAbstractEditorFactory template subclass. +*/ + +/*! + \class QtAbstractEditorFactory + + \brief The QtAbstractEditorFactory is the base template class for editor + factories. + + An editor factory is a class that is able to create an editing + widget of a specified type (e.g. line edits or comboboxes) for a + given QtProperty object, and it is used in conjunction with the + QtAbstractPropertyManager and QtAbstractPropertyBrowser classes. + + Note that the QtAbstractEditorFactory functions are using the + PropertyManager template argument class which can be any + QtAbstractPropertyManager subclass. For example: + + \code + QtSpinBoxFactory *factory; + QSet managers = factory->propertyManagers(); + \endcode + + Note that QtSpinBoxFactory by definition creates editing widgets + \e only for properties created by QtIntPropertyManager. + + When using a property browser widget, the properties are created + and managed by implementations of the QtAbstractPropertyManager + class. To ensure that the properties' values will be displayed + using suitable editing widgets, the managers are associated with + objects of QtAbstractEditorFactory subclasses. The property browser will + use these associations to determine which factories it should use + to create the preferred editing widgets. + + A QtAbstractEditorFactory object is capable of producing editors for + several property managers at the same time. To create an + association between this factory and a given manager, use the + addPropertyManager() function. Use the removePropertyManager() function to make + this factory stop producing editors for a given property + manager. Use the propertyManagers() function to retrieve the set of + managers currently associated with this factory. + + Several ready-made implementations of the QtAbstractEditorFactory class + are available: + + \list + \o QtCheckBoxFactory + \o QtDateEditFactory + \o QtDateTimeEditFactory + \o QtDoubleSpinBoxFactory + \o QtEnumEditorFactory + \o QtLineEditFactory + \o QtScrollBarFactory + \o QtSliderFactory + \o QtSpinBoxFactory + \o QtTimeEditFactory + \o QtVariantEditorFactory + \endlist + + When deriving from the QtAbstractEditorFactory class, several pure virtual + functions must be implemented: the connectPropertyManager() function is + used by the factory to connect to the given manager's signals, the + createEditor() function is supposed to create an editor for the + given property controlled by the given manager, and finally the + disconnectPropertyManager() function is used by the factory to disconnect + from the specified manager's signals. + + \sa QtAbstractEditorFactoryBase, QtAbstractPropertyManager +*/ + +/*! + \fn QtAbstractEditorFactory::QtAbstractEditorFactory(QObject *parent = 0) + + Creates an editor factory with the given \a parent. + + \sa addPropertyManager() +*/ + +/*! + \fn QWidget *QtAbstractEditorFactory::createEditor(QtProperty *property, QWidget *parent) + + Creates an editing widget (with the given \a parent) for the given + \a property. +*/ + +/*! + \fn void QtAbstractEditorFactory::addPropertyManager(PropertyManager *manager) + + Adds the given \a manager to this factory's set of managers, + making this factory produce editing widgets for properties created + by the given manager. + + The PropertyManager type is a template argument class, and represents the chosen + QtAbstractPropertyManager subclass. + + \sa propertyManagers(), removePropertyManager() +*/ + +/*! + \fn void QtAbstractEditorFactory::removePropertyManager(PropertyManager *manager) + + Removes the given \a manager from this factory's set of + managers. The PropertyManager type is a template argument class, and may be + any QtAbstractPropertyManager subclass. + + \sa propertyManagers(), addPropertyManager() +*/ + +/*! + \fn virtual void QtAbstractEditorFactory::connectPropertyManager(PropertyManager *manager) = 0 + + Connects this factory to the given \a manager's signals. The + PropertyManager type is a template argument class, and represents + the chosen QtAbstractPropertyManager subclass. + + This function is used internally by the addPropertyManager() function, and + makes it possible to update an editing widget when the associated + property's data changes. This is typically done in custom slots + responding to the signals emitted by the property's manager, + e.g. QtIntPropertyManager::valueChanged() and + QtIntPropertyManager::rangeChanged(). + + \sa propertyManagers(), disconnectPropertyManager() +*/ + +/*! + \fn virtual QWidget *QtAbstractEditorFactory::createEditor(PropertyManager *manager, QtProperty *property, + QWidget *parent) = 0 + + Creates an editing widget with the given \a parent for the + specified \a property created by the given \a manager. The + PropertyManager type is a template argument class, and represents + the chosen QtAbstractPropertyManager subclass. + + This function must be implemented in derived classes: It is + recommended to store a pointer to the widget and map it to the + given \a property, since the widget must be updated whenever the + associated property's data changes. This is typically done in + custom slots responding to the signals emitted by the property's + manager, e.g. QtIntPropertyManager::valueChanged() and + QtIntPropertyManager::rangeChanged(). + + \sa connectPropertyManager() +*/ + +/*! + \fn virtual void QtAbstractEditorFactory::disconnectPropertyManager(PropertyManager *manager) = 0 + + Disconnects this factory from the given \a manager's signals. The + PropertyManager type is a template argument class, and represents + the chosen QtAbstractPropertyManager subclass. + + This function is used internally by the removePropertyManager() function. + + \sa propertyManagers(), connectPropertyManager() +*/ + +/*! + \fn QSet QtAbstractEditorFactory::propertyManagers() const + + Returns the factory's set of associated managers. The + PropertyManager type is a template argument class, and represents + the chosen QtAbstractPropertyManager subclass. + + \sa addPropertyManager(), removePropertyManager() +*/ + +/*! + \fn PropertyManager *QtAbstractEditorFactory::propertyManager(QtProperty *property) const + + Returns the property manager for the given \a property, or 0 if + the given \a property doesn't belong to any of this factory's + registered managers. + + The PropertyManager type is a template argument class, and represents the chosen + QtAbstractPropertyManager subclass. + + \sa propertyManagers() +*/ + +/*! + \fn virtual void QtAbstractEditorFactory::managerDestroyed(QObject *manager) + + \internal + \reimp +*/ + +//////////////////////////////////// +class QtBrowserItemPrivate +{ +public: + QtBrowserItemPrivate(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent) + : m_browser(browser), m_property(property), m_parent(parent), q_ptr(0) {} + + void addChild(QtBrowserItem *index, QtBrowserItem *after); + void removeChild(QtBrowserItem *index); + + QtAbstractPropertyBrowser * const m_browser; + QtProperty *m_property; + QtBrowserItem *m_parent; + + QtBrowserItem *q_ptr; + + QList m_children; + +}; + +void QtBrowserItemPrivate::addChild(QtBrowserItem *index, QtBrowserItem *after) +{ + if (m_children.contains(index)) + return; + int idx = m_children.indexOf(after) + 1; // we insert after returned idx, if it was -1 then we set idx to 0; + m_children.insert(idx, index); +} + +void QtBrowserItemPrivate::removeChild(QtBrowserItem *index) +{ + m_children.removeAll(index); +} + + +/*! + \class QtBrowserItem + + \brief The QtBrowserItem class represents a property in + a property browser instance. + + Browser items are created whenever a QtProperty is inserted to the + property browser. A QtBrowserItem uniquely identifies a + browser's item. Thus, if the same QtProperty is inserted multiple + times, each occurrence gets its own unique QtBrowserItem. The + items are owned by QtAbstractPropertyBrowser and automatically + deleted when they are removed from the browser. + + You can traverse a browser's properties by calling parent() and + children(). The property and the browser associated with an item + are available as property() and browser(). + + \sa QtAbstractPropertyBrowser, QtProperty +*/ + +/*! + Returns the property which is accosiated with this item. Note that + several items can be associated with the same property instance in + the same property browser. + + \sa QtAbstractPropertyBrowser::items() +*/ + +QtProperty *QtBrowserItem::property() const +{ + return d_ptr->m_property; +} + +/*! + Returns the parent item of \e this item. Returns 0 if \e this item + is associated with top-level property in item's property browser. + + \sa children() +*/ + +QtBrowserItem *QtBrowserItem::parent() const +{ + return d_ptr->m_parent; +} + +/*! + Returns the children items of \e this item. The properties + reproduced from children items are always the same as + reproduced from associated property' children, for example: + + \code + QtBrowserItem *item; + QList childrenItems = item->children(); + + QList childrenProperties = item->property()->subProperties(); + \endcode + + The \e childrenItems list represents the same list as \e childrenProperties. +*/ + +QList QtBrowserItem::children() const +{ + return d_ptr->m_children; +} + +/*! + Returns the property browser which owns \e this item. +*/ + +QtAbstractPropertyBrowser *QtBrowserItem::browser() const +{ + return d_ptr->m_browser; +} + +QtBrowserItem::QtBrowserItem(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent) +{ + d_ptr = new QtBrowserItemPrivate(browser, property, parent); + d_ptr->q_ptr = this; +} + +QtBrowserItem::~QtBrowserItem() +{ + delete d_ptr; +} + + +//////////////////////////////////// + +typedef QMap > Map1; +typedef QMap > > Map2; +Q_GLOBAL_STATIC(Map1, m_viewToManagerToFactory) +Q_GLOBAL_STATIC(Map2, m_managerToFactoryToViews) + +class QtAbstractPropertyBrowserPrivate +{ + QtAbstractPropertyBrowser *q_ptr; + Q_DECLARE_PUBLIC(QtAbstractPropertyBrowser) +public: + QtAbstractPropertyBrowserPrivate(); + + void insertSubTree(QtProperty *property, + QtProperty *parentProperty); + void removeSubTree(QtProperty *property, + QtProperty *parentProperty); + void createBrowserIndexes(QtProperty *property, QtProperty *parentProperty, QtProperty *afterProperty); + void removeBrowserIndexes(QtProperty *property, QtProperty *parentProperty); + QtBrowserItem *createBrowserIndex(QtProperty *property, QtBrowserItem *parentIndex, QtBrowserItem *afterIndex); + void removeBrowserIndex(QtBrowserItem *index); + void clearIndex(QtBrowserItem *index); + + void slotPropertyInserted(QtProperty *property, + QtProperty *parentProperty, QtProperty *afterProperty); + void slotPropertyRemoved(QtProperty *property, QtProperty *parentProperty); + void slotPropertyDestroyed(QtProperty *property); + void slotPropertyDataChanged(QtProperty *property); + + QList m_subItems; + QMap > m_managerToProperties; + QMap > m_propertyToParents; + + QMap m_topLevelPropertyToIndex; + QList m_topLevelIndexes; + QMap > m_propertyToIndexes; + + QtBrowserItem *m_currentItem; +}; + +QtAbstractPropertyBrowserPrivate::QtAbstractPropertyBrowserPrivate() : + m_currentItem(0) +{ +} + +void QtAbstractPropertyBrowserPrivate::insertSubTree(QtProperty *property, + QtProperty *parentProperty) +{ + if (m_propertyToParents.contains(property)) { + // property was already inserted, so its manager is connected + // and all its children are inserted and theirs managers are connected + // we just register new parent (parent has to be new). + m_propertyToParents[property].append(parentProperty); + // don't need to update m_managerToProperties map since + // m_managerToProperties[manager] already contains property. + return; + } + QtAbstractPropertyManager *manager = property->propertyManager(); + if (m_managerToProperties[manager].isEmpty()) { + // connect manager's signals + q_ptr->connect(manager, SIGNAL(propertyInserted(QtProperty *, + QtProperty *, QtProperty *)), + q_ptr, SLOT(slotPropertyInserted(QtProperty *, + QtProperty *, QtProperty *))); + q_ptr->connect(manager, SIGNAL(propertyRemoved(QtProperty *, + QtProperty *)), + q_ptr, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + q_ptr->connect(manager, SIGNAL(propertyDestroyed(QtProperty *)), + q_ptr, SLOT(slotPropertyDestroyed(QtProperty *))); + q_ptr->connect(manager, SIGNAL(propertyChanged(QtProperty *)), + q_ptr, SLOT(slotPropertyDataChanged(QtProperty *))); + } + m_managerToProperties[manager].append(property); + m_propertyToParents[property].append(parentProperty); + + QList subList = property->subProperties(); + QListIterator itSub(subList); + while (itSub.hasNext()) { + QtProperty *subProperty = itSub.next(); + insertSubTree(subProperty, property); + } +} + +void QtAbstractPropertyBrowserPrivate::removeSubTree(QtProperty *property, + QtProperty *parentProperty) +{ + if (!m_propertyToParents.contains(property)) { + // ASSERT + return; + } + + m_propertyToParents[property].removeAll(parentProperty); + if (!m_propertyToParents[property].isEmpty()) + return; + + m_propertyToParents.remove(property); + QtAbstractPropertyManager *manager = property->propertyManager(); + m_managerToProperties[manager].removeAll(property); + if (m_managerToProperties[manager].isEmpty()) { + // disconnect manager's signals + q_ptr->disconnect(manager, SIGNAL(propertyInserted(QtProperty *, + QtProperty *, QtProperty *)), + q_ptr, SLOT(slotPropertyInserted(QtProperty *, + QtProperty *, QtProperty *))); + q_ptr->disconnect(manager, SIGNAL(propertyRemoved(QtProperty *, + QtProperty *)), + q_ptr, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + q_ptr->disconnect(manager, SIGNAL(propertyDestroyed(QtProperty *)), + q_ptr, SLOT(slotPropertyDestroyed(QtProperty *))); + q_ptr->disconnect(manager, SIGNAL(propertyChanged(QtProperty *)), + q_ptr, SLOT(slotPropertyDataChanged(QtProperty *))); + + m_managerToProperties.remove(manager); + } + + QList subList = property->subProperties(); + QListIterator itSub(subList); + while (itSub.hasNext()) { + QtProperty *subProperty = itSub.next(); + removeSubTree(subProperty, property); + } +} + +void QtAbstractPropertyBrowserPrivate::createBrowserIndexes(QtProperty *property, QtProperty *parentProperty, QtProperty *afterProperty) +{ + QMap parentToAfter; + if (afterProperty) { + QMap >::ConstIterator it = + m_propertyToIndexes.find(afterProperty); + if (it == m_propertyToIndexes.constEnd()) + return; + + QList indexes = it.value(); + QListIterator itIndex(indexes); + while (itIndex.hasNext()) { + QtBrowserItem *idx = itIndex.next(); + QtBrowserItem *parentIdx = idx->parent(); + if ((parentProperty && parentIdx && parentIdx->property() == parentProperty) || (!parentProperty && !parentIdx)) + parentToAfter[idx->parent()] = idx; + } + } else if (parentProperty) { + QMap >::ConstIterator it = + m_propertyToIndexes.find(parentProperty); + if (it == m_propertyToIndexes.constEnd()) + return; + + QList indexes = it.value(); + QListIterator itIndex(indexes); + while (itIndex.hasNext()) { + QtBrowserItem *idx = itIndex.next(); + parentToAfter[idx] = 0; + } + } else { + parentToAfter[0] = 0; + } + + const QMap::ConstIterator pcend = parentToAfter.constEnd(); + for (QMap::ConstIterator it = parentToAfter.constBegin(); it != pcend; ++it) + createBrowserIndex(property, it.key(), it.value()); +} + +QtBrowserItem *QtAbstractPropertyBrowserPrivate::createBrowserIndex(QtProperty *property, + QtBrowserItem *parentIndex, QtBrowserItem *afterIndex) +{ + QtBrowserItem *newIndex = new QtBrowserItem(q_ptr, property, parentIndex); + if (parentIndex) { + parentIndex->d_ptr->addChild(newIndex, afterIndex); + } else { + m_topLevelPropertyToIndex[property] = newIndex; + m_topLevelIndexes.insert(m_topLevelIndexes.indexOf(afterIndex) + 1, newIndex); + } + m_propertyToIndexes[property].append(newIndex); + + q_ptr->itemInserted(newIndex, afterIndex); + + QList subItems = property->subProperties(); + QListIterator itChild(subItems); + QtBrowserItem *afterChild = 0; + while (itChild.hasNext()) { + QtProperty *child = itChild.next(); + afterChild = createBrowserIndex(child, newIndex, afterChild); + } + return newIndex; +} + +void QtAbstractPropertyBrowserPrivate::removeBrowserIndexes(QtProperty *property, QtProperty *parentProperty) +{ + QList toRemove; + QMap >::ConstIterator it = + m_propertyToIndexes.find(property); + if (it == m_propertyToIndexes.constEnd()) + return; + + QList indexes = it.value(); + QListIterator itIndex(indexes); + while (itIndex.hasNext()) { + QtBrowserItem *idx = itIndex.next(); + QtBrowserItem *parentIdx = idx->parent(); + if ((parentProperty && parentIdx && parentIdx->property() == parentProperty) || (!parentProperty && !parentIdx)) + toRemove.append(idx); + } + + QListIterator itRemove(toRemove); + while (itRemove.hasNext()) { + QtBrowserItem *index = itRemove.next(); + removeBrowserIndex(index); + } +} + +void QtAbstractPropertyBrowserPrivate::removeBrowserIndex(QtBrowserItem *index) +{ + QList children = index->children(); + for (int i = children.count(); i > 0; i--) { + removeBrowserIndex(children.at(i - 1)); + } + + q_ptr->itemRemoved(index); + + if (index->parent()) { + index->parent()->d_ptr->removeChild(index); + } else { + m_topLevelPropertyToIndex.remove(index->property()); + m_topLevelIndexes.removeAll(index); + } + + QtProperty *property = index->property(); + + m_propertyToIndexes[property].removeAll(index); + if (m_propertyToIndexes[property].isEmpty()) + m_propertyToIndexes.remove(property); + + delete index; +} + +void QtAbstractPropertyBrowserPrivate::clearIndex(QtBrowserItem *index) +{ + QList children = index->children(); + QListIterator itChild(children); + while (itChild.hasNext()) { + clearIndex(itChild.next()); + } + delete index; +} + +void QtAbstractPropertyBrowserPrivate::slotPropertyInserted(QtProperty *property, + QtProperty *parentProperty, QtProperty *afterProperty) +{ + if (!m_propertyToParents.contains(parentProperty)) + return; + createBrowserIndexes(property, parentProperty, afterProperty); + insertSubTree(property, parentProperty); + //q_ptr->propertyInserted(property, parentProperty, afterProperty); +} + +void QtAbstractPropertyBrowserPrivate::slotPropertyRemoved(QtProperty *property, + QtProperty *parentProperty) +{ + if (!m_propertyToParents.contains(parentProperty)) + return; + removeSubTree(property, parentProperty); // this line should be probably moved down after propertyRemoved call + //q_ptr->propertyRemoved(property, parentProperty); + removeBrowserIndexes(property, parentProperty); +} + +void QtAbstractPropertyBrowserPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (!m_subItems.contains(property)) + return; + q_ptr->removeProperty(property); +} + +void QtAbstractPropertyBrowserPrivate::slotPropertyDataChanged(QtProperty *property) +{ + if (!m_propertyToParents.contains(property)) + return; + + QMap >::ConstIterator it = + m_propertyToIndexes.find(property); + if (it == m_propertyToIndexes.constEnd()) + return; + + QList indexes = it.value(); + QListIterator itIndex(indexes); + while (itIndex.hasNext()) { + QtBrowserItem *idx = itIndex.next(); + q_ptr->itemChanged(idx); + } + //q_ptr->propertyChanged(property); +} + +/*! + \class QtAbstractPropertyBrowser + + \brief QtAbstractPropertyBrowser provides a base class for + implementing property browsers. + + A property browser is a widget that enables the user to edit a + given set of properties. Each property is represented by a label + specifying the property's name, and an editing widget (e.g. a line + edit or a combobox) holding its value. A property can have zero or + more subproperties. + + \image qtpropertybrowser.png + + The top level properties can be retrieved using the + properties() function. To traverse each property's + subproperties, use the QtProperty::subProperties() function. In + addition, the set of top level properties can be manipulated using + the addProperty(), insertProperty() and removeProperty() + functions. Note that the QtProperty class provides a corresponding + set of functions making it possible to manipulate the set of + subproperties as well. + + To remove all the properties from the property browser widget, use + the clear() function. This function will clear the editor, but it + will not delete the properties since they can still be used in + other editors. + + The properties themselves are created and managed by + implementations of the QtAbstractPropertyManager class. A manager + can handle (i.e. create and manage) properties of a given type. In + the property browser the managers are associated with + implementations of the QtAbstractEditorFactory: A factory is a + class able to create an editing widget of a specified type. + + When using a property browser widget, managers must be created for + each of the required property types before the properties + themselves can be created. To ensure that the properties' values + will be displayed using suitable editing widgets, the managers + must be associated with objects of the preferred factory + implementations using the setFactoryForManager() function. The + property browser will use these associations to determine which + factory it should use to create the preferred editing widget. + + Note that a factory can be associated with many managers, but a + manager can only be associated with one single factory within the + context of a single property browser. The associations between + managers and factories can at any time be removed using the + unsetFactoryForManager() function. + + Whenever the property data changes or a property is inserted or + removed, the itemChanged(), itemInserted() or + itemRemoved() functions are called, respectively. These + functions must be reimplemented in derived classes in order to + update the property browser widget. Be aware that some property + instances can appear several times in an abstract tree + structure. For example: + + \table 100% + \row + \o + \code + QtProperty *property1, *property2, *property3; + + property2->addSubProperty(property1); + property3->addSubProperty(property2); + + QtAbstractPropertyBrowser *editor; + + editor->addProperty(property1); + editor->addProperty(property2); + editor->addProperty(property3); + \endcode + \o \image qtpropertybrowser-duplicate.png + \endtable + + The addProperty() function returns a QtBrowserItem that uniquely + identifies the created item. + + To make a property editable in the property browser, the + createEditor() function must be called to provide the + property with a suitable editing widget. + + Note that there are two ready-made property browser + implementations: + + \list + \o QtGroupBoxPropertyBrowser + \o QtTreePropertyBrowser + \endlist + + \sa QtAbstractPropertyManager, QtAbstractEditorFactoryBase +*/ + +/*! + \fn void QtAbstractPropertyBrowser::setFactoryForManager(PropertyManager *manager, + QtAbstractEditorFactory *factory) + + Connects the given \a manager to the given \a factory, ensuring + that properties of the \a manager's type will be displayed with an + editing widget suitable for their value. + + For example: + + \code + QtIntPropertyManager *intManager; + QtDoublePropertyManager *doubleManager; + + QtProperty *myInteger = intManager->addProperty(); + QtProperty *myDouble = doubleManager->addProperty(); + + QtSpinBoxFactory *spinBoxFactory; + QtDoubleSpinBoxFactory *doubleSpinBoxFactory; + + QtAbstractPropertyBrowser *editor; + editor->setFactoryForManager(intManager, spinBoxFactory); + editor->setFactoryForManager(doubleManager, doubleSpinBoxFactory); + + editor->addProperty(myInteger); + editor->addProperty(myDouble); + \endcode + + In this example the \c myInteger property's value is displayed + with a QSpinBox widget, while the \c myDouble property's value is + displayed with a QDoubleSpinBox widget. + + Note that a factory can be associated with many managers, but a + manager can only be associated with one single factory. If the + given \a manager already is associated with another factory, the + old association is broken before the new one established. + + This function ensures that the given \a manager and the given \a + factory are compatible, and it automatically calls the + QtAbstractEditorFactory::addPropertyManager() function if necessary. + + \sa unsetFactoryForManager() +*/ + +/*! + \fn virtual void QtAbstractPropertyBrowser::itemInserted(QtBrowserItem *insertedItem, + QtBrowserItem *precedingItem) = 0 + + This function is called to update the widget whenever a property + is inserted or added to the property browser, passing pointers to + the \a insertedItem of property and the specified + \a precedingItem as parameters. + + If \a precedingItem is 0, the \a insertedItem was put at + the beginning of its parent item's list of subproperties. If + the parent of \a insertedItem is 0, the \a insertedItem was added as a top + level property of \e this property browser. + + This function must be reimplemented in derived classes. Note that + if the \a insertedItem's property has subproperties, this + method will be called for those properties as soon as the current call is finished. + + \sa insertProperty(), addProperty() +*/ + +/*! + \fn virtual void QtAbstractPropertyBrowser::itemRemoved(QtBrowserItem *item) = 0 + + This function is called to update the widget whenever a property + is removed from the property browser, passing the pointer to the + \a item of the property as parameters. The passed \a item is + deleted just after this call is finished. + + If the the parent of \a item is 0, the removed \a item was a + top level property in this editor. + + This function must be reimplemented in derived classes. Note that + if the removed \a item's property has subproperties, this + method will be called for those properties just before the current call is started. + + \sa removeProperty() +*/ + +/*! + \fn virtual void QtAbstractPropertyBrowser::itemChanged(QtBrowserItem *item) = 0 + + This function is called whenever a property's data changes, + passing a pointer to the \a item of property as parameter. + + This function must be reimplemented in derived classes in order to + update the property browser widget whenever a property's name, + tool tip, status tip, "what's this" text, value text or value icon + changes. + + Note that if the property browser contains several occurrences of + the same property, this method will be called once for each + occurrence (with a different item each time). + + \sa QtProperty, items() +*/ + +/*! + Creates an abstract property browser with the given \a parent. +*/ +QtAbstractPropertyBrowser::QtAbstractPropertyBrowser(QWidget *parent) + : QWidget(parent) +{ + d_ptr = new QtAbstractPropertyBrowserPrivate; + d_ptr->q_ptr = this; + +} + +/*! + Destroys the property browser, and destroys all the items that were + created by this property browser. + + Note that the properties that were displayed in the editor are not + deleted since they still can be used in other editors. Neither + does the destructor delete the property managers and editor + factories that were used by this property browser widget unless + this widget was their parent. + + \sa QtAbstractPropertyManager::~QtAbstractPropertyManager() +*/ +QtAbstractPropertyBrowser::~QtAbstractPropertyBrowser() +{ + QList indexes = topLevelItems(); + QListIterator itItem(indexes); + while (itItem.hasNext()) + d_ptr->clearIndex(itItem.next()); + delete d_ptr; +} + +/*! + Returns the property browser's list of top level properties. + + To traverse the subproperties, use the QtProperty::subProperties() + function. + + \sa addProperty(), insertProperty(), removeProperty() +*/ +QList QtAbstractPropertyBrowser::properties() const +{ + return d_ptr->m_subItems; +} + +/*! + Returns the property browser's list of all items associated + with the given \a property. + + There is one item per instance of the property in the browser. + + \sa topLevelItem() +*/ + +QList QtAbstractPropertyBrowser::items(QtProperty *property) const +{ + return d_ptr->m_propertyToIndexes.value(property); +} + +/*! + Returns the top-level items associated with the given \a property. + + Returns 0 if \a property wasn't inserted into this property + browser or isn't a top-level one. + + \sa topLevelItems(), items() +*/ + +QtBrowserItem *QtAbstractPropertyBrowser::topLevelItem(QtProperty *property) const +{ + return d_ptr->m_topLevelPropertyToIndex.value(property); +} + +/*! + Returns the list of top-level items. + + \sa topLevelItem() +*/ + +QList QtAbstractPropertyBrowser::topLevelItems() const +{ + return d_ptr->m_topLevelIndexes; +} + +/*! + Removes all the properties from the editor, but does not delete + them since they can still be used in other editors. + + \sa removeProperty(), QtAbstractPropertyManager::clear() +*/ +void QtAbstractPropertyBrowser::clear() +{ + QList subList = properties(); + QListIterator itSub(subList); + itSub.toBack(); + while (itSub.hasPrevious()) { + QtProperty *property = itSub.previous(); + removeProperty(property); + } +} + +/*! + Appends the given \a property (and its subproperties) to the + property browser's list of top level properties. Returns the item + created by property browser which is associated with the \a property. + In order to get all children items created by the property + browser in this call, the returned item should be traversed. + + If the specified \a property is already added, this function does + nothing and returns 0. + + \sa insertProperty(), QtProperty::addSubProperty(), properties() +*/ +QtBrowserItem *QtAbstractPropertyBrowser::addProperty(QtProperty *property) +{ + QtProperty *afterProperty = 0; + if (d_ptr->m_subItems.count() > 0) + afterProperty = d_ptr->m_subItems.last(); + return insertProperty(property, afterProperty); +} + +/*! + \fn QtBrowserItem *QtAbstractPropertyBrowser::insertProperty(QtProperty *property, + QtProperty *afterProperty) + + Inserts the given \a property (and its subproperties) after + the specified \a afterProperty in the browser's list of top + level properties. Returns item created by property browser which + is associated with the \a property. In order to get all children items + created by the property browser in this call returned item should be traversed. + + If the specified \a afterProperty is 0, the given \a property is + inserted at the beginning of the list. If \a property is + already inserted, this function does nothing and returns 0. + + \sa addProperty(), QtProperty::insertSubProperty(), properties() +*/ +QtBrowserItem *QtAbstractPropertyBrowser::insertProperty(QtProperty *property, + QtProperty *afterProperty) +{ + if (!property) + return 0; + + // if item is already inserted in this item then cannot add. + QList pendingList = properties(); + int pos = 0; + int newPos = 0; + QtProperty *properAfterProperty = 0; + while (pos < pendingList.count()) { + QtProperty *prop = pendingList.at(pos); + if (prop == property) + return 0; + if (prop == afterProperty) { + newPos = pos + 1; + properAfterProperty = afterProperty; + } + pos++; + } + d_ptr->createBrowserIndexes(property, 0, afterProperty); + + // traverse inserted subtree and connect to manager's signals + d_ptr->insertSubTree(property, 0); + + d_ptr->m_subItems.insert(newPos, property); + //propertyInserted(property, 0, properAfterProperty); + return topLevelItem(property); +} + +/*! + Removes the specified \a property (and its subproperties) from the + property browser's list of top level properties. All items + that were associated with the given \a property and its children + are deleted. + + Note that the properties are \e not deleted since they can still + be used in other editors. + + \sa clear(), QtProperty::removeSubProperty(), properties() +*/ +void QtAbstractPropertyBrowser::removeProperty(QtProperty *property) +{ + if (!property) + return; + + QList pendingList = properties(); + int pos = 0; + while (pos < pendingList.count()) { + if (pendingList.at(pos) == property) { + d_ptr->m_subItems.removeAt(pos); //perhaps this two lines + d_ptr->removeSubTree(property, 0); //should be moved down after propertyRemoved call. + //propertyRemoved(property, 0); + + d_ptr->removeBrowserIndexes(property, 0); + + // when item is deleted, item will call removeItem for top level items, + // and itemRemoved for nested items. + + return; + } + pos++; + } +} + +/*! + Creates an editing widget (with the given \a parent) for the given + \a property according to the previously established associations + between property managers and editor factories. + + If the property is created by a property manager which was not + associated with any of the existing factories in \e this property + editor, the function returns 0. + + To make a property editable in the property browser, the + createEditor() function must be called to provide the + property with a suitable editing widget. + + Reimplement this function to provide additional decoration for the + editing widgets created by the installed factories. + + \sa setFactoryForManager() +*/ +QWidget *QtAbstractPropertyBrowser::createEditor(QtProperty *property, + QWidget *parent) +{ + QtAbstractEditorFactoryBase *factory = 0; + QtAbstractPropertyManager *manager = property->propertyManager(); + + if (m_viewToManagerToFactory()->contains(this) && + (*m_viewToManagerToFactory())[this].contains(manager)) { + factory = (*m_viewToManagerToFactory())[this][manager]; + } + + if (!factory) + return 0; + return factory->createEditor(property, parent); +} + +bool QtAbstractPropertyBrowser::addFactory(QtAbstractPropertyManager *abstractManager, + QtAbstractEditorFactoryBase *abstractFactory) +{ + bool connectNeeded = false; + if (!m_managerToFactoryToViews()->contains(abstractManager) || + !(*m_managerToFactoryToViews())[abstractManager].contains(abstractFactory)) { + connectNeeded = true; + } else if ((*m_managerToFactoryToViews())[abstractManager][abstractFactory] + .contains(this)) { + return connectNeeded; + } + + if (m_viewToManagerToFactory()->contains(this) && + (*m_viewToManagerToFactory())[this].contains(abstractManager)) { + unsetFactoryForManager(abstractManager); + } + + (*m_managerToFactoryToViews())[abstractManager][abstractFactory].append(this); + (*m_viewToManagerToFactory())[this][abstractManager] = abstractFactory; + + return connectNeeded; +} + +/*! + Removes the association between the given \a manager and the + factory bound to it, automatically calling the + QtAbstractEditorFactory::removePropertyManager() function if necessary. + + \sa setFactoryForManager() +*/ +void QtAbstractPropertyBrowser::unsetFactoryForManager(QtAbstractPropertyManager *manager) +{ + if (!m_viewToManagerToFactory()->contains(this) || + !(*m_viewToManagerToFactory())[this].contains(manager)) { + return; + } + + QtAbstractEditorFactoryBase *abstractFactory = + (*m_viewToManagerToFactory())[this][manager]; + (*m_viewToManagerToFactory())[this].remove(manager); + if ((*m_viewToManagerToFactory())[this].isEmpty()) { + (*m_viewToManagerToFactory()).remove(this); + } + + (*m_managerToFactoryToViews())[manager][abstractFactory].removeAll(this); + if ((*m_managerToFactoryToViews())[manager][abstractFactory].isEmpty()) { + (*m_managerToFactoryToViews())[manager].remove(abstractFactory); + abstractFactory->breakConnection(manager); + if ((*m_managerToFactoryToViews())[manager].isEmpty()) { + (*m_managerToFactoryToViews()).remove(manager); + } + } +} + +/*! + Returns the current item in the property browser. + + \sa setCurrentItem() +*/ +QtBrowserItem *QtAbstractPropertyBrowser::currentItem() const +{ + return d_ptr->m_currentItem; +} + +/*! + Sets the current item in the property browser to \a item. + + \sa currentItem(), currentItemChanged() +*/ +void QtAbstractPropertyBrowser::setCurrentItem(QtBrowserItem *item) +{ + QtBrowserItem *oldItem = d_ptr->m_currentItem; + d_ptr->m_currentItem = item; + if (oldItem != item) + emit currentItemChanged(item); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtpropertybrowser.cxx" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.h b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.h new file mode 100644 index 000000000..35b7ac0f8 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.h @@ -0,0 +1,379 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTPROPERTYBROWSER_H +#define QTPROPERTYBROWSER_H + +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +#if defined(Q_WS_WIN) +# if !defined(QT_QTPROPERTYBROWSER_EXPORT) && !defined(QT_QTPROPERTYBROWSER_IMPORT) +# define QT_QTPROPERTYBROWSER_EXPORT +# elif defined(QT_QTPROPERTYBROWSER_IMPORT) +# if defined(QT_QTPROPERTYBROWSER_EXPORT) +# undef QT_QTPROPERTYBROWSER_EXPORT +# endif +# define QT_QTPROPERTYBROWSER_EXPORT __declspec(dllimport) +# elif defined(QT_QTPROPERTYBROWSER_EXPORT) +# undef QT_QTPROPERTYBROWSER_EXPORT +# define QT_QTPROPERTYBROWSER_EXPORT __declspec(dllexport) +# endif +#else +# define QT_QTPROPERTYBROWSER_EXPORT +#endif + + +class QtAbstractPropertyManager; +class QtPropertyPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtProperty +{ +public: + virtual ~QtProperty(); + + QList subProperties() const; + + QtAbstractPropertyManager *propertyManager() const; + + QString toolTip() const; + QString statusTip() const; + QString whatsThis() const; + QString propertyName() const; + + bool isEnabled() const; + bool isModified() const; + bool isBold() const; + + bool hasValue() const; + QIcon valueIcon() const; + QString valueText() const; + + void setToolTip(const QString &text); + void setStatusTip(const QString &text); + void setWhatsThis(const QString &text); + void setPropertyName(const QString &text); + void setEnabled(bool enable); + void setModified(bool modified); + void setBold(bool bold); + + void addSubProperty(QtProperty *property); + void insertSubProperty(QtProperty *property, QtProperty *afterProperty); + void removeSubProperty(QtProperty *property); +protected: + explicit QtProperty(QtAbstractPropertyManager *manager); + void propertyChanged(); +private: + friend class QtAbstractPropertyManager; + QtPropertyPrivate *d_ptr; +}; + +class QtAbstractPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtAbstractPropertyManager : public QObject +{ + Q_OBJECT +public: + + explicit QtAbstractPropertyManager(QObject *parent = 0); + ~QtAbstractPropertyManager(); + + QSet properties() const; + void clear() const; + + QtProperty *addProperty(const QString &name = QString()); +Q_SIGNALS: + + void propertyInserted(QtProperty *property, + QtProperty *parent, QtProperty *after); + void propertyChanged(QtProperty *property); + void propertyRemoved(QtProperty *property, QtProperty *parent); + void propertyDestroyed(QtProperty *property); +protected: + virtual bool hasValue(const QtProperty *property) const; + virtual QIcon valueIcon(const QtProperty *property) const; + virtual QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property) = 0; + virtual void uninitializeProperty(QtProperty *property); + virtual QtProperty *createProperty(); +private: + friend class QtProperty; + QtAbstractPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtAbstractPropertyManager) + Q_DISABLE_COPY(QtAbstractPropertyManager) +}; + +class QT_QTPROPERTYBROWSER_EXPORT QtAbstractEditorFactoryBase : public QObject +{ + Q_OBJECT +public: + virtual QWidget *createEditor(QtProperty *property, QWidget *parent) = 0; +protected: + explicit QtAbstractEditorFactoryBase(QObject *parent = 0) + : QObject(parent) {} + + virtual void breakConnection(QtAbstractPropertyManager *manager) = 0; +protected Q_SLOTS: + virtual void managerDestroyed(QObject *manager) = 0; + + friend class QtAbstractPropertyBrowser; +}; + +template +class QtAbstractEditorFactory : public QtAbstractEditorFactoryBase +{ +public: + explicit QtAbstractEditorFactory(QObject *parent) : QtAbstractEditorFactoryBase(parent) {} + QWidget *createEditor(QtProperty *property, QWidget *parent) + { + QSetIterator it(m_managers); + while (it.hasNext()) { + PropertyManager *manager = it.next(); + if (manager == property->propertyManager()) { + return createEditor(manager, property, parent); + } + } + return 0; + } + void addPropertyManager(PropertyManager *manager) + { + if (m_managers.contains(manager)) + return; + m_managers.insert(manager); + connectPropertyManager(manager); + connect(manager, SIGNAL(destroyed(QObject *)), + this, SLOT(managerDestroyed(QObject *))); + } + void removePropertyManager(PropertyManager *manager) + { + if (!m_managers.contains(manager)) + return; + disconnect(manager, SIGNAL(destroyed(QObject *)), + this, SLOT(managerDestroyed(QObject *))); + disconnectPropertyManager(manager); + m_managers.remove(manager); + } + QSet propertyManagers() const + { + return m_managers; + } + PropertyManager *propertyManager(QtProperty *property) const + { + QtAbstractPropertyManager *manager = property->propertyManager(); + QSetIterator itManager(m_managers); + while (itManager.hasNext()) { + PropertyManager *m = itManager.next(); + if (m == manager) { + return m; + } + } + return 0; + } +protected: + virtual void connectPropertyManager(PropertyManager *manager) = 0; + virtual QWidget *createEditor(PropertyManager *manager, QtProperty *property, + QWidget *parent) = 0; + virtual void disconnectPropertyManager(PropertyManager *manager) = 0; + void managerDestroyed(QObject *manager) + { + QSetIterator it(m_managers); + while (it.hasNext()) { + PropertyManager *m = it.next(); + if (m == manager) { + m_managers.remove(m); + return; + } + } + } +private: + void breakConnection(QtAbstractPropertyManager *manager) + { + QSetIterator it(m_managers); + while (it.hasNext()) { + PropertyManager *m = it.next(); + if (m == manager) { + removePropertyManager(m); + return; + } + } + } +private: + QSet m_managers; + friend class QtAbstractPropertyEditor; +}; + +class QtAbstractPropertyBrowser; +class QtBrowserItemPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtBrowserItem +{ +public: + QtProperty *property() const; + QtBrowserItem *parent() const; + QList children() const; + QtAbstractPropertyBrowser *browser() const; +private: + explicit QtBrowserItem(QtAbstractPropertyBrowser *browser, QtProperty *property, QtBrowserItem *parent); + ~QtBrowserItem(); + QtBrowserItemPrivate *d_ptr; + friend class QtAbstractPropertyBrowserPrivate; +}; + +class QtAbstractPropertyBrowserPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtAbstractPropertyBrowser : public QWidget +{ + Q_OBJECT +public: + + explicit QtAbstractPropertyBrowser(QWidget *parent = 0); + ~QtAbstractPropertyBrowser(); + + QList properties() const; + QList items(QtProperty *property) const; + QtBrowserItem *topLevelItem(QtProperty *property) const; + QList topLevelItems() const; + void clear(); + + template + void setFactoryForManager(PropertyManager *manager, + QtAbstractEditorFactory *factory) { + QtAbstractPropertyManager *abstractManager = manager; + QtAbstractEditorFactoryBase *abstractFactory = factory; + + if (addFactory(abstractManager, abstractFactory)) + factory->addPropertyManager(manager); + } + + void unsetFactoryForManager(QtAbstractPropertyManager *manager); + + QtBrowserItem *currentItem() const; + void setCurrentItem(QtBrowserItem *); + +Q_SIGNALS: + void currentItemChanged(QtBrowserItem *); + +public Q_SLOTS: + + QtBrowserItem *addProperty(QtProperty *property); + QtBrowserItem *insertProperty(QtProperty *property, QtProperty *afterProperty); + void removeProperty(QtProperty *property); + +protected: + + virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) = 0; + virtual void itemRemoved(QtBrowserItem *item) = 0; + // can be tooltip, statustip, whatsthis, name, icon, text. + virtual void itemChanged(QtBrowserItem *item) = 0; + + virtual QWidget *createEditor(QtProperty *property, QWidget *parent); +private: + + bool addFactory(QtAbstractPropertyManager *abstractManager, + QtAbstractEditorFactoryBase *abstractFactory); + + QtAbstractPropertyBrowserPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtAbstractPropertyBrowser) + Q_DISABLE_COPY(QtAbstractPropertyBrowser) + Q_PRIVATE_SLOT(d_func(), void slotPropertyInserted(QtProperty *, + QtProperty *, QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyRemoved(QtProperty *, + QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDataChanged(QtProperty *)) + +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif // QTPROPERTYBROWSER_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.pri b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.pri new file mode 100644 index 000000000..6a6050c75 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.pri @@ -0,0 +1,30 @@ +include(../common.pri) +INCLUDEPATH += $$PWD +DEPENDPATH += $$PWD + +qtpropertybrowser-uselib:!qtpropertybrowser-buildlib { + LIBS += -L$$QTPROPERTYBROWSER_LIBDIR -l$$QTPROPERTYBROWSER_LIBNAME +} else { + SOURCES += $$PWD/qtpropertybrowser.cpp \ + $$PWD/qtpropertymanager.cpp \ + $$PWD/qteditorfactory.cpp \ + $$PWD/qtvariantproperty.cpp \ + $$PWD/qttreepropertybrowser.cpp \ + $$PWD/qtbuttonpropertybrowser.cpp \ + $$PWD/qtgroupboxpropertybrowser.cpp \ + $$PWD/qtpropertybrowserutils.cpp + HEADERS += $$PWD/qtpropertybrowser.h \ + $$PWD/qtpropertymanager.h \ + $$PWD/qteditorfactory.h \ + $$PWD/qtvariantproperty.h \ + $$PWD/qttreepropertybrowser.h \ + $$PWD/qtbuttonpropertybrowser.h \ + $$PWD/qtgroupboxpropertybrowser.h \ + $$PWD/qtpropertybrowserutils_p.h + RESOURCES += $$PWD/qtpropertybrowser.qrc +} + +win32 { + contains(TEMPLATE, lib):contains(CONFIG, shared):DEFINES += QT_QTPROPERTYBROWSER_EXPORT + else:qtpropertybrowser-uselib:DEFINES += QT_QTPROPERTYBROWSER_IMPORT +} diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.qrc b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.qrc new file mode 100644 index 000000000..4f91ab782 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowser.qrc @@ -0,0 +1,23 @@ + + + images/cursor-arrow.png + images/cursor-busy.png + images/cursor-closedhand.png + images/cursor-cross.png + images/cursor-forbidden.png + images/cursor-hand.png + images/cursor-hsplit.png + images/cursor-ibeam.png + images/cursor-openhand.png + images/cursor-sizeall.png + images/cursor-sizeb.png + images/cursor-sizef.png + images/cursor-sizeh.png + images/cursor-sizev.png + images/cursor-uparrow.png + images/cursor-vsplit.png + images/cursor-wait.png + images/cursor-whatsthis.png + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowserutils.cpp b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowserutils.cpp new file mode 100644 index 000000000..ce198bfca --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowserutils.cpp @@ -0,0 +1,477 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtpropertybrowserutils_p.h" +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +QtCursorDatabase::QtCursorDatabase() +{ + appendCursor(Qt::ArrowCursor, QApplication::translate("QtCursorDatabase", "Arrow", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-arrow.png"))); + appendCursor(Qt::UpArrowCursor, QApplication::translate("QtCursorDatabase", "Up Arrow", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-uparrow.png"))); + appendCursor(Qt::CrossCursor, QApplication::translate("QtCursorDatabase", "Cross", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-cross.png"))); + appendCursor(Qt::WaitCursor, QApplication::translate("QtCursorDatabase", "Wait", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-wait.png"))); + appendCursor(Qt::IBeamCursor, QApplication::translate("QtCursorDatabase", "IBeam", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-ibeam.png"))); + appendCursor(Qt::SizeVerCursor, QApplication::translate("QtCursorDatabase", "Size Vertical", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizev.png"))); + appendCursor(Qt::SizeHorCursor, QApplication::translate("QtCursorDatabase", "Size Horizontal", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizeh.png"))); + appendCursor(Qt::SizeFDiagCursor, QApplication::translate("QtCursorDatabase", "Size Backslash", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizef.png"))); + appendCursor(Qt::SizeBDiagCursor, QApplication::translate("QtCursorDatabase", "Size Slash", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizeb.png"))); + appendCursor(Qt::SizeAllCursor, QApplication::translate("QtCursorDatabase", "Size All", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-sizeall.png"))); + appendCursor(Qt::BlankCursor, QApplication::translate("QtCursorDatabase", "Blank", 0, + QApplication::UnicodeUTF8), QIcon()); + appendCursor(Qt::SplitVCursor, QApplication::translate("QtCursorDatabase", "Split Vertical", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-vsplit.png"))); + appendCursor(Qt::SplitHCursor, QApplication::translate("QtCursorDatabase", "Split Horizontal", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-hsplit.png"))); + appendCursor(Qt::PointingHandCursor, QApplication::translate("QtCursorDatabase", "Pointing Hand", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-hand.png"))); + appendCursor(Qt::ForbiddenCursor, QApplication::translate("QtCursorDatabase", "Forbidden", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-forbidden.png"))); + appendCursor(Qt::OpenHandCursor, QApplication::translate("QtCursorDatabase", "Open Hand", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-openhand.png"))); + appendCursor(Qt::ClosedHandCursor, QApplication::translate("QtCursorDatabase", "Closed Hand", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-closedhand.png"))); + appendCursor(Qt::WhatsThisCursor, QApplication::translate("QtCursorDatabase", "What's This", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-whatsthis.png"))); + appendCursor(Qt::BusyCursor, QApplication::translate("QtCursorDatabase", "Busy", 0, + QApplication::UnicodeUTF8), QIcon(QLatin1String(":/trolltech/qtpropertybrowser/images/cursor-busy.png"))); +} + +void QtCursorDatabase::appendCursor(Qt::CursorShape shape, const QString &name, const QIcon &icon) +{ + if (m_cursorShapeToValue.contains(shape)) + return; + int value = m_cursorNames.count(); + m_cursorNames.append(name); + m_cursorIcons[value] = icon; + m_valueToCursorShape[value] = shape; + m_cursorShapeToValue[shape] = value; +} + +QStringList QtCursorDatabase::cursorShapeNames() const +{ + return m_cursorNames; +} + +QMap QtCursorDatabase::cursorShapeIcons() const +{ + return m_cursorIcons; +} + +QString QtCursorDatabase::cursorToShapeName(const QCursor &cursor) const +{ + int val = cursorToValue(cursor); + if (val >= 0) + return m_cursorNames.at(val); + return QString(); +} + +QIcon QtCursorDatabase::cursorToShapeIcon(const QCursor &cursor) const +{ + int val = cursorToValue(cursor); + return m_cursorIcons.value(val); +} + +int QtCursorDatabase::cursorToValue(const QCursor &cursor) const +{ +#ifndef QT_NO_CURSOR + Qt::CursorShape shape = cursor.shape(); + if (m_cursorShapeToValue.contains(shape)) + return m_cursorShapeToValue[shape]; +#endif + return -1; +} + +#ifndef QT_NO_CURSOR +QCursor QtCursorDatabase::valueToCursor(int value) const +{ + if (m_valueToCursorShape.contains(value)) + return QCursor(m_valueToCursorShape[value]); + return QCursor(); +} +#endif + +QPixmap QtPropertyBrowserUtils::brushValuePixmap(const QBrush &b) +{ + QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + + QPainter painter(&img); + painter.setCompositionMode(QPainter::CompositionMode_Source); + painter.fillRect(0, 0, img.width(), img.height(), b); + QColor color = b.color(); + if (color.alpha() != 255) { // indicate alpha by an inset + QBrush opaqueBrush = b; + color.setAlpha(255); + opaqueBrush.setColor(color); + painter.fillRect(img.width() / 4, img.height() / 4, + img.width() / 2, img.height() / 2, opaqueBrush); + } + painter.end(); + return QPixmap::fromImage(img); +} + +QIcon QtPropertyBrowserUtils::brushValueIcon(const QBrush &b) +{ + return QIcon(brushValuePixmap(b)); +} + +QString QtPropertyBrowserUtils::colorValueText(const QColor &c) +{ + return QApplication::translate("QtPropertyBrowserUtils", "[%1, %2, %3] (%4)", 0, QApplication::UnicodeUTF8) + .arg(QString::number(c.red())) + .arg(QString::number(c.green())) + .arg(QString::number(c.blue())) + .arg(QString::number(c.alpha())); +} + +QPixmap QtPropertyBrowserUtils::fontValuePixmap(const QFont &font) +{ + QFont f = font; + QImage img(16, 16, QImage::Format_ARGB32_Premultiplied); + img.fill(0); + QPainter p(&img); + p.setRenderHint(QPainter::TextAntialiasing, true); + p.setRenderHint(QPainter::Antialiasing, true); + f.setPointSize(13); + p.setFont(f); + QTextOption t; + t.setAlignment(Qt::AlignCenter); + p.drawText(QRect(0, 0, 16, 16), QString(QLatin1Char('A')), t); + return QPixmap::fromImage(img); +} + +QIcon QtPropertyBrowserUtils::fontValueIcon(const QFont &f) +{ + return QIcon(fontValuePixmap(f)); +} + +QString QtPropertyBrowserUtils::fontValueText(const QFont &f) +{ + return QApplication::translate("QtPropertyBrowserUtils", "[%1, %2]", 0, QApplication::UnicodeUTF8) + .arg(f.family()) + .arg(f.pointSize()); +} + + +QtBoolEdit::QtBoolEdit(QWidget *parent) : + QWidget(parent), + m_checkBox(new QCheckBox(this)), + m_textVisible(true) +{ + QHBoxLayout *lt = new QHBoxLayout; + if (QApplication::layoutDirection() == Qt::LeftToRight) + lt->setContentsMargins(4, 0, 0, 0); + else + lt->setContentsMargins(0, 0, 4, 0); + lt->addWidget(m_checkBox); + setLayout(lt); + connect(m_checkBox, SIGNAL(toggled(bool)), this, SIGNAL(toggled(bool))); + setFocusProxy(m_checkBox); + m_checkBox->setText(QString()); +} + +void QtBoolEdit::setTextVisible(bool textVisible) +{ + if (m_textVisible == textVisible) + return; + + m_textVisible = textVisible; + m_checkBox->setText(QString()); +} + +Qt::CheckState QtBoolEdit::checkState() const +{ + return m_checkBox->checkState(); +} + +void QtBoolEdit::setCheckState(Qt::CheckState state) +{ + m_checkBox->setCheckState(state); +} + +bool QtBoolEdit::isChecked() const +{ + return m_checkBox->isChecked(); +} + +void QtBoolEdit::setChecked(bool c) +{ + m_checkBox->setChecked(c); + if (!m_textVisible) + return; + m_checkBox->setText(QString()); +} + +bool QtBoolEdit::blockCheckBoxSignals(bool block) +{ + return m_checkBox->blockSignals(block); +} + +void QtBoolEdit::mousePressEvent(QMouseEvent *event) +{ + if (event->buttons() == Qt::LeftButton) { + m_checkBox->click(); + event->accept(); + } else { + QWidget::mousePressEvent(event); + } +} + + +QtKeySequenceEdit::QtKeySequenceEdit(QWidget *parent) + : QWidget(parent), m_num(0), m_lineEdit(new QLineEdit(this)) +{ + QHBoxLayout *layout = new QHBoxLayout(this); + layout->addWidget(m_lineEdit); + layout->setMargin(0); + m_lineEdit->installEventFilter(this); + m_lineEdit->setReadOnly(true); + m_lineEdit->setFocusProxy(this); + setFocusPolicy(m_lineEdit->focusPolicy()); + setAttribute(Qt::WA_InputMethodEnabled); +} + +bool QtKeySequenceEdit::eventFilter(QObject *o, QEvent *e) +{ + if (o == m_lineEdit && e->type() == QEvent::ContextMenu) { + QContextMenuEvent *c = static_cast(e); + QMenu *menu = m_lineEdit->createStandardContextMenu(); + const QList actions = menu->actions(); + QListIterator itAction(actions); + while (itAction.hasNext()) { + QAction *action = itAction.next(); + action->setShortcut(QKeySequence()); + QString actionString = action->text(); + const int pos = actionString.lastIndexOf(QLatin1Char('\t')); + if (pos > 0) + actionString.remove(pos, actionString.length() - pos); + action->setText(actionString); + } + QAction *actionBefore = 0; + if (actions.count() > 0) + actionBefore = actions[0]; + QAction *clearAction = new QAction(tr("Clear Shortcut"), menu); + menu->insertAction(actionBefore, clearAction); + menu->insertSeparator(actionBefore); + clearAction->setEnabled(!m_keySequence.isEmpty()); + connect(clearAction, SIGNAL(triggered()), this, SLOT(slotClearShortcut())); + menu->exec(c->globalPos()); + delete menu; + e->accept(); + return true; + } + + return QWidget::eventFilter(o, e); +} + +void QtKeySequenceEdit::slotClearShortcut() +{ + if (m_keySequence.isEmpty()) + return; + setKeySequence(QKeySequence()); + emit keySequenceChanged(m_keySequence); +} + +void QtKeySequenceEdit::handleKeyEvent(QKeyEvent *e) +{ + int nextKey = e->key(); + if (nextKey == Qt::Key_Control || nextKey == Qt::Key_Shift || + nextKey == Qt::Key_Meta || nextKey == Qt::Key_Alt || + nextKey == Qt::Key_Super_L || nextKey == Qt::Key_AltGr) + return; + + nextKey |= translateModifiers(e->modifiers(), e->text()); + int k0 = m_keySequence[0]; + int k1 = m_keySequence[1]; + int k2 = m_keySequence[2]; + int k3 = m_keySequence[3]; + switch (m_num) { + case 0: k0 = nextKey; k1 = 0; k2 = 0; k3 = 0; break; + case 1: k1 = nextKey; k2 = 0; k3 = 0; break; + case 2: k2 = nextKey; k3 = 0; break; + case 3: k3 = nextKey; break; + default: break; + } + ++m_num; + if (m_num > 3) + m_num = 0; + m_keySequence = QKeySequence(k0, k1, k2, k3); + m_lineEdit->setText(m_keySequence.toString(QKeySequence::NativeText)); + e->accept(); + emit keySequenceChanged(m_keySequence); +} + +void QtKeySequenceEdit::setKeySequence(const QKeySequence &sequence) +{ + if (sequence == m_keySequence) + return; + m_num = 0; + m_keySequence = sequence; + m_lineEdit->setText(m_keySequence.toString(QKeySequence::NativeText)); +} + +QKeySequence QtKeySequenceEdit::keySequence() const +{ + return m_keySequence; +} + +int QtKeySequenceEdit::translateModifiers(Qt::KeyboardModifiers state, const QString &text) const +{ + int result = 0; + if ((state & Qt::ShiftModifier) && (text.size() == 0 || !text.at(0).isPrint() || text.at(0).isLetter() || text.at(0).isSpace())) + result |= Qt::SHIFT; + if (state & Qt::ControlModifier) + result |= Qt::CTRL; + if (state & Qt::MetaModifier) + result |= Qt::META; + if (state & Qt::AltModifier) + result |= Qt::ALT; + return result; +} + +void QtKeySequenceEdit::focusInEvent(QFocusEvent *e) +{ + m_lineEdit->event(e); + m_lineEdit->selectAll(); + QWidget::focusInEvent(e); +} + +void QtKeySequenceEdit::focusOutEvent(QFocusEvent *e) +{ + m_num = 0; + m_lineEdit->event(e); + QWidget::focusOutEvent(e); +} + +void QtKeySequenceEdit::keyPressEvent(QKeyEvent *e) +{ + handleKeyEvent(e); + e->accept(); +} + +void QtKeySequenceEdit::keyReleaseEvent(QKeyEvent *e) +{ + m_lineEdit->event(e); +} + +bool QtKeySequenceEdit::event(QEvent *e) +{ + if (e->type() == QEvent::Shortcut || + e->type() == QEvent::ShortcutOverride || + e->type() == QEvent::KeyRelease) { + e->accept(); + return true; + } + return QWidget::event(e); +} + + + + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowserutils_p.h b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowserutils_p.h new file mode 100644 index 000000000..dca4b8c37 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertybrowserutils_p.h @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of Qt Designer. This header +// file may change from version to version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QTPROPERTYBROWSERUTILS_H +#define QTPROPERTYBROWSERUTILS_H + +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QMouseEvent; +class QCheckBox; +class QLineEdit; + +class QtCursorDatabase +{ +public: + QtCursorDatabase(); + + QStringList cursorShapeNames() const; + QMap cursorShapeIcons() const; + QString cursorToShapeName(const QCursor &cursor) const; + QIcon cursorToShapeIcon(const QCursor &cursor) const; + int cursorToValue(const QCursor &cursor) const; +#ifndef QT_NO_CURSOR + QCursor valueToCursor(int value) const; +#endif +private: + void appendCursor(Qt::CursorShape shape, const QString &name, const QIcon &icon); + QStringList m_cursorNames; + QMap m_cursorIcons; + QMap m_valueToCursorShape; + QMap m_cursorShapeToValue; +}; + +class QtPropertyBrowserUtils +{ +public: + static QPixmap brushValuePixmap(const QBrush &b); + static QIcon brushValueIcon(const QBrush &b); + static QString colorValueText(const QColor &c); + static QPixmap fontValuePixmap(const QFont &f); + static QIcon fontValueIcon(const QFont &f); + static QString fontValueText(const QFont &f); +}; + +class QtBoolEdit : public QWidget { + Q_OBJECT +public: + QtBoolEdit(QWidget *parent = 0); + + bool textVisible() const { return m_textVisible; } + void setTextVisible(bool textVisible); + + Qt::CheckState checkState() const; + void setCheckState(Qt::CheckState state); + + bool isChecked() const; + void setChecked(bool c); + + bool blockCheckBoxSignals(bool block); + +Q_SIGNALS: + void toggled(bool); + +protected: + void mousePressEvent(QMouseEvent * event); + +private: + QCheckBox *m_checkBox; + bool m_textVisible; +}; + +class QtKeySequenceEdit : public QWidget +{ + Q_OBJECT +public: + QtKeySequenceEdit(QWidget *parent = 0); + + QKeySequence keySequence() const; + bool eventFilter(QObject *o, QEvent *e); +public Q_SLOTS: + void setKeySequence(const QKeySequence &sequence); +Q_SIGNALS: + void keySequenceChanged(const QKeySequence &sequence); +protected: + void focusInEvent(QFocusEvent *e); + void focusOutEvent(QFocusEvent *e); + void keyPressEvent(QKeyEvent *e); + void keyReleaseEvent(QKeyEvent *e); + bool event(QEvent *e); +private slots: + void slotClearShortcut(); +private: + void handleKeyEvent(QKeyEvent *e); + int translateModifiers(Qt::KeyboardModifiers state, const QString &text) const; + + int m_num; + QKeySequence m_keySequence; + QLineEdit *m_lineEdit; +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertymanager.cpp b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertymanager.cpp new file mode 100644 index 000000000..20de19786 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertymanager.cpp @@ -0,0 +1,6465 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtpropertymanager.h" +#include "qtpropertybrowserutils_p.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#if defined(Q_CC_MSVC) +# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ +#endif + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +template +static void setSimpleMinimumData(PrivateData *data, const Value &minVal) +{ + data->minVal = minVal; + if (data->maxVal < data->minVal) + data->maxVal = data->minVal; + + if (data->val < data->minVal) + data->val = data->minVal; +} + +template +static void setSimpleMaximumData(PrivateData *data, const Value &maxVal) +{ + data->maxVal = maxVal; + if (data->minVal > data->maxVal) + data->minVal = data->maxVal; + + if (data->val > data->maxVal) + data->val = data->maxVal; +} + +template +static void setSizeMinimumData(PrivateData *data, const Value &newMinVal) +{ + data->minVal = newMinVal; + if (data->maxVal.width() < data->minVal.width()) + data->maxVal.setWidth(data->minVal.width()); + if (data->maxVal.height() < data->minVal.height()) + data->maxVal.setHeight(data->minVal.height()); + + if (data->val.width() < data->minVal.width()) + data->val.setWidth(data->minVal.width()); + if (data->val.height() < data->minVal.height()) + data->val.setHeight(data->minVal.height()); +} + +template +static void setSizeMaximumData(PrivateData *data, const Value &newMaxVal) +{ + data->maxVal = newMaxVal; + if (data->minVal.width() > data->maxVal.width()) + data->minVal.setWidth(data->maxVal.width()); + if (data->minVal.height() > data->maxVal.height()) + data->minVal.setHeight(data->maxVal.height()); + + if (data->val.width() > data->maxVal.width()) + data->val.setWidth(data->maxVal.width()); + if (data->val.height() > data->maxVal.height()) + data->val.setHeight(data->maxVal.height()); +} + +template +static SizeValue qBoundSize(const SizeValue &minVal, const SizeValue &val, const SizeValue &maxVal) +{ + SizeValue croppedVal = val; + if (minVal.width() > val.width()) + croppedVal.setWidth(minVal.width()); + else if (maxVal.width() < val.width()) + croppedVal.setWidth(maxVal.width()); + + if (minVal.height() > val.height()) + croppedVal.setHeight(minVal.height()); + else if (maxVal.height() < val.height()) + croppedVal.setHeight(maxVal.height()); + + return croppedVal; +} + +// Match the exact signature of qBound for VS 6. +QSize qBound(QSize minVal, QSize val, QSize maxVal) +{ + return qBoundSize(minVal, val, maxVal); +} + +QSizeF qBound(QSizeF minVal, QSizeF val, QSizeF maxVal) +{ + return qBoundSize(minVal, val, maxVal); +} + +namespace { + +namespace { +template +void orderBorders(Value &minVal, Value &maxVal) +{ + if (minVal > maxVal) + qSwap(minVal, maxVal); +} + +template +static void orderSizeBorders(Value &minVal, Value &maxVal) +{ + Value fromSize = minVal; + Value toSize = maxVal; + if (fromSize.width() > toSize.width()) { + fromSize.setWidth(maxVal.width()); + toSize.setWidth(minVal.width()); + } + if (fromSize.height() > toSize.height()) { + fromSize.setHeight(maxVal.height()); + toSize.setHeight(minVal.height()); + } + minVal = fromSize; + maxVal = toSize; +} + +void orderBorders(QSize &minVal, QSize &maxVal) +{ + orderSizeBorders(minVal, maxVal); +} + +void orderBorders(QSizeF &minVal, QSizeF &maxVal) +{ + orderSizeBorders(minVal, maxVal); +} + +} +} +//////// + +template +static Value getData(const QMap &propertyMap, + Value PrivateData::*data, + const QtProperty *property, const Value &defaultValue = Value()) +{ + typedef QMap PropertyToData; + typedef Q_TYPENAME PropertyToData::const_iterator PropertyToDataConstIterator; + const PropertyToDataConstIterator it = propertyMap.constFind(property); + if (it == propertyMap.constEnd()) + return defaultValue; + return it.value().*data; +} + +template +static Value getValue(const QMap &propertyMap, + const QtProperty *property, const Value &defaultValue = Value()) +{ + return getData(propertyMap, &PrivateData::val, property, defaultValue); +} + +template +static Value getMinimum(const QMap &propertyMap, + const QtProperty *property, const Value &defaultValue = Value()) +{ + return getData(propertyMap, &PrivateData::minVal, property, defaultValue); +} + +template +static Value getMaximum(const QMap &propertyMap, + const QtProperty *property, const Value &defaultValue = Value()) +{ + return getData(propertyMap, &PrivateData::maxVal, property, defaultValue); +} + +template +static void setSimpleValue(QMap &propertyMap, + PropertyManager *manager, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + QtProperty *property, const Value &val) +{ + typedef QMap PropertyToData; + typedef Q_TYPENAME PropertyToData::iterator PropertyToDataIterator; + const PropertyToDataIterator it = propertyMap.find(property); + if (it == propertyMap.end()) + return; + + if (it.value() == val) + return; + + it.value() = val; + + emit (manager->*propertyChangedSignal)(property); + emit (manager->*valueChangedSignal)(property, val); +} + +template +static void setValueInRange(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + QtProperty *property, const Value &val, + void (PropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, ValueChangeParameter)) +{ + typedef Q_TYPENAME PropertyManagerPrivate::Data PrivateData; + typedef QMap PropertyToData; + typedef Q_TYPENAME PropertyToData::iterator PropertyToDataIterator; + const PropertyToDataIterator it = managerPrivate->m_values.find(property); + if (it == managerPrivate->m_values.end()) + return; + + PrivateData &data = it.value(); + + if (data.val == val) + return; + + const Value oldVal = data.val; + + data.val = qBound(data.minVal, val, data.maxVal); + + if (data.val == oldVal) + return; + + if (setSubPropertyValue) + (managerPrivate->*setSubPropertyValue)(property, data.val); + + emit (manager->*propertyChangedSignal)(property); + emit (manager->*valueChangedSignal)(property, data.val); +} + +template +static void setBorderValues(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), + QtProperty *property, const Value &minVal, const Value &maxVal, + void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, + ValueChangeParameter, ValueChangeParameter, ValueChangeParameter)) +{ + typedef Q_TYPENAME PropertyManagerPrivate::Data PrivateData; + typedef QMap PropertyToData; + typedef Q_TYPENAME PropertyToData::iterator PropertyToDataIterator; + const PropertyToDataIterator it = managerPrivate->m_values.find(property); + if (it == managerPrivate->m_values.end()) + return; + + Value fromVal = minVal; + Value toVal = maxVal; + orderBorders(fromVal, toVal); + + PrivateData &data = it.value(); + + if (data.minVal == fromVal && data.maxVal == toVal) + return; + + const Value oldVal = data.val; + + data.setMinimumValue(fromVal); + data.setMaximumValue(toVal); + + emit (manager->*rangeChangedSignal)(property, data.minVal, data.maxVal); + + if (setSubPropertyRange) + (managerPrivate->*setSubPropertyRange)(property, data.minVal, data.maxVal, data.val); + + if (data.val == oldVal) + return; + + emit (manager->*propertyChangedSignal)(property); + emit (manager->*valueChangedSignal)(property, data.val); +} + +template +static void setBorderValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), + QtProperty *property, + Value (PrivateData::*getRangeVal)() const, + void (PrivateData::*setRangeVal)(ValueChangeParameter), const Value &borderVal, + void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, + ValueChangeParameter, ValueChangeParameter, ValueChangeParameter)) +{ + typedef QMap PropertyToData; + typedef Q_TYPENAME PropertyToData::iterator PropertyToDataIterator; + const PropertyToDataIterator it = managerPrivate->m_values.find(property); + if (it == managerPrivate->m_values.end()) + return; + + PrivateData &data = it.value(); + + if ((data.*getRangeVal)() == borderVal) + return; + + const Value oldVal = data.val; + + (data.*setRangeVal)(borderVal); + + emit (manager->*rangeChangedSignal)(property, data.minVal, data.maxVal); + + if (setSubPropertyRange) + (managerPrivate->*setSubPropertyRange)(property, data.minVal, data.maxVal, data.val); + + if (data.val == oldVal) + return; + + emit (manager->*propertyChangedSignal)(property); + emit (manager->*valueChangedSignal)(property, data.val); +} + +template +static void setMinimumValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), + QtProperty *property, const Value &minVal) +{ + void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, + ValueChangeParameter, ValueChangeParameter, ValueChangeParameter) = 0; + setBorderValue(manager, managerPrivate, + propertyChangedSignal, valueChangedSignal, rangeChangedSignal, + property, &PropertyManagerPrivate::Data::minimumValue, &PropertyManagerPrivate::Data::setMinimumValue, minVal, setSubPropertyRange); +} + +template +static void setMaximumValue(PropertyManager *manager, PropertyManagerPrivate *managerPrivate, + void (PropertyManager::*propertyChangedSignal)(QtProperty *), + void (PropertyManager::*valueChangedSignal)(QtProperty *, ValueChangeParameter), + void (PropertyManager::*rangeChangedSignal)(QtProperty *, ValueChangeParameter, ValueChangeParameter), + QtProperty *property, const Value &maxVal) +{ + void (PropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, + ValueChangeParameter, ValueChangeParameter, ValueChangeParameter) = 0; + setBorderValue(manager, managerPrivate, + propertyChangedSignal, valueChangedSignal, rangeChangedSignal, + property, &PropertyManagerPrivate::Data::maximumValue, &PropertyManagerPrivate::Data::setMaximumValue, maxVal, setSubPropertyRange); +} + +class QtMetaEnumWrapper : public QObject +{ + Q_OBJECT + Q_PROPERTY(QSizePolicy::Policy policy READ policy) +public: + QSizePolicy::Policy policy() const { return QSizePolicy::Ignored; } +private: + QtMetaEnumWrapper(QObject *parent) : QObject(parent) {} +}; + +class QtMetaEnumProvider +{ +public: + QtMetaEnumProvider(); + + QStringList policyEnumNames() const { return m_policyEnumNames; } + QStringList languageEnumNames() const { return m_languageEnumNames; } + QStringList countryEnumNames(QLocale::Language language) const { return m_countryEnumNames.value(language); } + + QSizePolicy::Policy indexToSizePolicy(int index) const; + int sizePolicyToIndex(QSizePolicy::Policy policy) const; + + void indexToLocale(int languageIndex, int countryIndex, QLocale::Language *language, QLocale::Country *country) const; + void localeToIndex(QLocale::Language language, QLocale::Country country, int *languageIndex, int *countryIndex) const; + +private: + void initLocale(); + + QStringList m_policyEnumNames; + QStringList m_languageEnumNames; + QMap m_countryEnumNames; + QMap m_indexToLanguage; + QMap m_languageToIndex; + QMap > m_indexToCountry; + QMap > m_countryToIndex; + QMetaEnum m_policyEnum; +}; + +#if QT_VERSION < 0x040300 + +static QList countriesForLanguage(QLocale::Language language) +{ + QList countries; + QLocale::Country country = QLocale::AnyCountry; + while (country <= QLocale::LastCountry) { + QLocale locale(language, country); + if (locale.language() == language && !countries.contains(locale.country())) + countries << locale.country(); + country = (QLocale::Country)((uint)country + 1); // ++country + } + return countries; +} + +#endif + +static QList sortCountries(const QList &countries) +{ + QMultiMap nameToCountry; + QListIterator itCountry(countries); + while (itCountry.hasNext()) { + QLocale::Country country = itCountry.next(); + nameToCountry.insert(QLocale::countryToString(country), country); + } + return nameToCountry.values(); +} + +void QtMetaEnumProvider::initLocale() +{ + QMultiMap nameToLanguage; + QLocale::Language language = QLocale::C; + while (language <= QLocale::LastLanguage) { + QLocale locale(language); + if (locale.language() == language) + nameToLanguage.insert(QLocale::languageToString(language), language); + language = (QLocale::Language)((uint)language + 1); // ++language + } + + const QLocale system = QLocale::system(); + if (!nameToLanguage.contains(QLocale::languageToString(system.language()))) + nameToLanguage.insert(QLocale::languageToString(system.language()), system.language()); + + QList languages = nameToLanguage.values(); + QListIterator itLang(languages); + while (itLang.hasNext()) { + QLocale::Language language = itLang.next(); + QList countries; +#if QT_VERSION < 0x040300 + countries = countriesForLanguage(language); +#else + countries = QLocale::countriesForLanguage(language); +#endif + if (countries.isEmpty() && language == system.language()) + countries << system.country(); + + if (!countries.isEmpty() && !m_languageToIndex.contains(language)) { + countries = sortCountries(countries); + int langIdx = m_languageEnumNames.count(); + m_indexToLanguage[langIdx] = language; + m_languageToIndex[language] = langIdx; + QStringList countryNames; + QListIterator it(countries); + int countryIdx = 0; + while (it.hasNext()) { + QLocale::Country country = it.next(); + countryNames << QLocale::countryToString(country); + m_indexToCountry[langIdx][countryIdx] = country; + m_countryToIndex[language][country] = countryIdx; + ++countryIdx; + } + m_languageEnumNames << QLocale::languageToString(language); + m_countryEnumNames[language] = countryNames; + } + } +} + +QtMetaEnumProvider::QtMetaEnumProvider() +{ + QMetaProperty p; + + p = QtMetaEnumWrapper::staticMetaObject.property( + QtMetaEnumWrapper::staticMetaObject.propertyOffset() + 0); + m_policyEnum = p.enumerator(); + const int keyCount = m_policyEnum.keyCount(); + for (int i = 0; i < keyCount; i++) + m_policyEnumNames << QLatin1String(m_policyEnum.key(i)); + + initLocale(); +} + +QSizePolicy::Policy QtMetaEnumProvider::indexToSizePolicy(int index) const +{ + return static_cast(m_policyEnum.value(index)); +} + +int QtMetaEnumProvider::sizePolicyToIndex(QSizePolicy::Policy policy) const +{ + const int keyCount = m_policyEnum.keyCount(); + for (int i = 0; i < keyCount; i++) + if (indexToSizePolicy(i) == policy) + return i; + return -1; +} + +void QtMetaEnumProvider::indexToLocale(int languageIndex, int countryIndex, QLocale::Language *language, QLocale::Country *country) const +{ + QLocale::Language l = QLocale::C; + QLocale::Country c = QLocale::AnyCountry; + if (m_indexToLanguage.contains(languageIndex)) { + l = m_indexToLanguage[languageIndex]; + if (m_indexToCountry.contains(languageIndex) && m_indexToCountry[languageIndex].contains(countryIndex)) + c = m_indexToCountry[languageIndex][countryIndex]; + } + if (language) + *language = l; + if (country) + *country = c; +} + +void QtMetaEnumProvider::localeToIndex(QLocale::Language language, QLocale::Country country, int *languageIndex, int *countryIndex) const +{ + int l = -1; + int c = -1; + if (m_languageToIndex.contains(language)) { + l = m_languageToIndex[language]; + if (m_countryToIndex.contains(language) && m_countryToIndex[language].contains(country)) + c = m_countryToIndex[language][country]; + } + + if (languageIndex) + *languageIndex = l; + if (countryIndex) + *countryIndex = c; +} + +Q_GLOBAL_STATIC(QtMetaEnumProvider, metaEnumProvider) + +// QtGroupPropertyManager + +/*! + \class QtGroupPropertyManager + + \brief The QtGroupPropertyManager provides and manages group properties. + + This class is intended to provide a grouping element without any value. + + \sa QtAbstractPropertyManager +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtGroupPropertyManager::QtGroupPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtGroupPropertyManager::~QtGroupPropertyManager() +{ + +} + +/*! + \reimp +*/ +bool QtGroupPropertyManager::hasValue(const QtProperty *property) const +{ + Q_UNUSED(property) + return false; +} + +/*! + \reimp +*/ +void QtGroupPropertyManager::initializeProperty(QtProperty *property) +{ + Q_UNUSED(property) +} + +/*! + \reimp +*/ +void QtGroupPropertyManager::uninitializeProperty(QtProperty *property) +{ + Q_UNUSED(property) +} + +// QtIntPropertyManager + +class QtIntPropertyManagerPrivate +{ + QtIntPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtIntPropertyManager) +public: + + struct Data + { + Data() : val(0), minVal(-INT_MAX), maxVal(INT_MAX), singleStep(1) {} + int val; + int minVal; + int maxVal; + int singleStep; + int minimumValue() const { return minVal; } + int maximumValue() const { return maxVal; } + void setMinimumValue(int newMinVal) { setSimpleMinimumData(this, newMinVal); } + void setMaximumValue(int newMaxVal) { setSimpleMaximumData(this, newMaxVal); } + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtIntPropertyManager + + \brief The QtIntPropertyManager provides and manages int properties. + + An int property has a current value, and a range specifying the + valid values. The range is defined by a minimum and a maximum + value. + + The property's value and range can be retrieved using the value(), + minimum() and maximum() functions, and can be set using the + setValue(), setMinimum() and setMaximum() slots. Alternatively, + the range can be defined in one go using the setRange() slot. + + In addition, QtIntPropertyManager provides the valueChanged() signal which + is emitted whenever a property created by this manager changes, + and the rangeChanged() signal which is emitted whenever such a + property changes its range of valid values. + + \sa QtAbstractPropertyManager, QtSpinBoxFactory, QtSliderFactory, QtScrollBarFactory +*/ + +/*! + \fn void QtIntPropertyManager::valueChanged(QtProperty *property, int value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtIntPropertyManager::rangeChanged(QtProperty *property, int minimum, int maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid values, passing a pointer to the + \a property and the new \a minimum and \a maximum values. + + \sa setRange() +*/ + +/*! + \fn void QtIntPropertyManager::singleStepChanged(QtProperty *property, int step) + + This signal is emitted whenever a property created by this manager + changes its single step property, passing a pointer to the + \a property and the new \a step value + + \sa setSingleStep() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtIntPropertyManager::QtIntPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtIntPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtIntPropertyManager::~QtIntPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns 0. + + \sa setValue() +*/ +int QtIntPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property, 0); +} + +/*! + Returns the given \a property's minimum value. + + \sa setMinimum(), maximum(), setRange() +*/ +int QtIntPropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property, 0); +} + +/*! + Returns the given \a property's maximum value. + + \sa setMaximum(), minimum(), setRange() +*/ +int QtIntPropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property, 0); +} + +/*! + Returns the given \a property's step value. + + The step is typically used to increment or decrement a property value while pressing an arrow key. + + \sa setSingleStep() +*/ +int QtIntPropertyManager::singleStep(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtIntPropertyManagerPrivate::Data::singleStep, property, 0); +} + +/*! + \reimp +*/ +QString QtIntPropertyManager::valueText(const QtProperty *property) const +{ + const QtIntPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return QString::number(it.value().val); +} + +/*! + \fn void QtIntPropertyManager::setValue(QtProperty *property, int value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not valid according to the given \a + property's range, the \a value is adjusted to the nearest valid + value within the range. + + \sa value(), setRange(), valueChanged() +*/ +void QtIntPropertyManager::setValue(QtProperty *property, int val) +{ + void (QtIntPropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, int) = 0; + setValueInRange(this, d_ptr, + &QtIntPropertyManager::propertyChanged, + &QtIntPropertyManager::valueChanged, + property, val, setSubPropertyValue); +} + +/*! + Sets the minimum value for the given \a property to \a minVal. + + When setting the minimum value, the maximum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within the range). + + \sa minimum(), setRange(), rangeChanged() +*/ +void QtIntPropertyManager::setMinimum(QtProperty *property, int minVal) +{ + setMinimumValue(this, d_ptr, + &QtIntPropertyManager::propertyChanged, + &QtIntPropertyManager::valueChanged, + &QtIntPropertyManager::rangeChanged, + property, minVal); +} + +/*! + Sets the maximum value for the given \a property to \a maxVal. + + When setting maximum value, the minimum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within the range). + + \sa maximum(), setRange(), rangeChanged() +*/ +void QtIntPropertyManager::setMaximum(QtProperty *property, int maxVal) +{ + setMaximumValue(this, d_ptr, + &QtIntPropertyManager::propertyChanged, + &QtIntPropertyManager::valueChanged, + &QtIntPropertyManager::rangeChanged, + property, maxVal); +} + +/*! + \fn void QtIntPropertyManager::setRange(QtProperty *property, int minimum, int maximum) + + Sets the range of valid values. + + This is a convenience function defining the range of valid values + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new range, the current value is adjusted if + necessary (ensuring that the value remains within range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtIntPropertyManager::setRange(QtProperty *property, int minVal, int maxVal) +{ + void (QtIntPropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, int, int, int) = 0; + setBorderValues(this, d_ptr, + &QtIntPropertyManager::propertyChanged, + &QtIntPropertyManager::valueChanged, + &QtIntPropertyManager::rangeChanged, + property, minVal, maxVal, setSubPropertyRange); +} + +/*! + Sets the step value for the given \a property to \a step. + + The step is typically used to increment or decrement a property value while pressing an arrow key. + + \sa singleStep() +*/ +void QtIntPropertyManager::setSingleStep(QtProperty *property, int step) +{ + const QtIntPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtIntPropertyManagerPrivate::Data data = it.value(); + + if (step < 0) + step = 0; + + if (data.singleStep == step) + return; + + data.singleStep = step; + + it.value() = data; + + emit singleStepChanged(property, data.singleStep); +} + +/*! + \reimp +*/ +void QtIntPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtIntPropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtIntPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtDoublePropertyManager + +class QtDoublePropertyManagerPrivate +{ + QtDoublePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtDoublePropertyManager) +public: + + struct Data + { + Data() : val(0), minVal(-INT_MAX), maxVal(INT_MAX), singleStep(1), decimals(2) {} + double val; + double minVal; + double maxVal; + double singleStep; + int decimals; + double minimumValue() const { return minVal; } + double maximumValue() const { return maxVal; } + void setMinimumValue(double newMinVal) { setSimpleMinimumData(this, newMinVal); } + void setMaximumValue(double newMaxVal) { setSimpleMaximumData(this, newMaxVal); } + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtDoublePropertyManager + + \brief The QtDoublePropertyManager provides and manages double properties. + + A double property has a current value, and a range specifying the + valid values. The range is defined by a minimum and a maximum + value. + + The property's value and range can be retrieved using the value(), + minimum() and maximum() functions, and can be set using the + setValue(), setMinimum() and setMaximum() slots. + Alternatively, the range can be defined in one go using the + setRange() slot. + + In addition, QtDoublePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the rangeChanged() signal which is emitted whenever + such a property changes its range of valid values. + + \sa QtAbstractPropertyManager, QtDoubleSpinBoxFactory +*/ + +/*! + \fn void QtDoublePropertyManager::valueChanged(QtProperty *property, double value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtDoublePropertyManager::rangeChanged(QtProperty *property, double minimum, double maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid values, passing a pointer to the + \a property and the new \a minimum and \a maximum values + + \sa setRange() +*/ + +/*! + \fn void QtDoublePropertyManager::decimalsChanged(QtProperty *property, int prec) + + This signal is emitted whenever a property created by this manager + changes its precision of value, passing a pointer to the + \a property and the new \a prec value + + \sa setDecimals() +*/ + +/*! + \fn void QtDoublePropertyManager::singleStepChanged(QtProperty *property, double step) + + This signal is emitted whenever a property created by this manager + changes its single step property, passing a pointer to the + \a property and the new \a step value + + \sa setSingleStep() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtDoublePropertyManager::QtDoublePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtDoublePropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtDoublePropertyManager::~QtDoublePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns 0. + + \sa setValue() +*/ +double QtDoublePropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property, 0.0); +} + +/*! + Returns the given \a property's minimum value. + + \sa maximum(), setRange() +*/ +double QtDoublePropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property, 0.0); +} + +/*! + Returns the given \a property's maximum value. + + \sa minimum(), setRange() +*/ +double QtDoublePropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property, 0.0); +} + +/*! + Returns the given \a property's step value. + + The step is typically used to increment or decrement a property value while pressing an arrow key. + + \sa setSingleStep() +*/ +double QtDoublePropertyManager::singleStep(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtDoublePropertyManagerPrivate::Data::singleStep, property, 0); +} + +/*! + Returns the given \a property's precision, in decimals. + + \sa setDecimals() +*/ +int QtDoublePropertyManager::decimals(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtDoublePropertyManagerPrivate::Data::decimals, property, 0); +} + +/*! + \reimp +*/ +QString QtDoublePropertyManager::valueText(const QtProperty *property) const +{ + const QtDoublePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + return QString("%1").arg(it.value().val); +} + +/*! + \fn void QtDoublePropertyManager::setValue(QtProperty *property, double value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not valid according to the given + \a property's range, the \a value is adjusted to the nearest valid value + within the range. + + \sa value(), setRange(), valueChanged() +*/ +void QtDoublePropertyManager::setValue(QtProperty *property, double val) +{ + void (QtDoublePropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, double) = 0; + setValueInRange(this, d_ptr, + &QtDoublePropertyManager::propertyChanged, + &QtDoublePropertyManager::valueChanged, + property, val, setSubPropertyValue); +} + +/*! + Sets the step value for the given \a property to \a step. + + The step is typically used to increment or decrement a property value while pressing an arrow key. + + \sa singleStep() +*/ +void QtDoublePropertyManager::setSingleStep(QtProperty *property, double step) +{ + const QtDoublePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtDoublePropertyManagerPrivate::Data data = it.value(); + + if (step < 0) + step = 0; + + if (data.singleStep == step) + return; + + data.singleStep = step; + + it.value() = data; + + emit singleStepChanged(property, data.singleStep); +} + +/*! + \fn void QtDoublePropertyManager::setDecimals(QtProperty *property, int prec) + + Sets the precision of the given \a property to \a prec. + + The valid decimal range is 0-13. The default is 2. + + \sa decimals() +*/ +void QtDoublePropertyManager::setDecimals(QtProperty *property, int prec) +{ + const QtDoublePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtDoublePropertyManagerPrivate::Data data = it.value(); + + if (prec > 13) + prec = 13; + else if (prec < 0) + prec = 0; + + if (data.decimals == prec) + return; + + data.decimals = prec; + + it.value() = data; + + emit decimalsChanged(property, data.decimals); +} + +/*! + Sets the minimum value for the given \a property to \a minVal. + + When setting the minimum value, the maximum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within in the range). + + \sa minimum(), setRange(), rangeChanged() +*/ +void QtDoublePropertyManager::setMinimum(QtProperty *property, double minVal) +{ + setMinimumValue(this, d_ptr, + &QtDoublePropertyManager::propertyChanged, + &QtDoublePropertyManager::valueChanged, + &QtDoublePropertyManager::rangeChanged, + property, minVal); +} + +/*! + Sets the maximum value for the given \a property to \a maxVal. + + When setting the maximum value, the minimum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within in the range). + + \sa maximum(), setRange(), rangeChanged() +*/ +void QtDoublePropertyManager::setMaximum(QtProperty *property, double maxVal) +{ + setMaximumValue(this, d_ptr, + &QtDoublePropertyManager::propertyChanged, + &QtDoublePropertyManager::valueChanged, + &QtDoublePropertyManager::rangeChanged, + property, maxVal); +} + +/*! + \fn void QtDoublePropertyManager::setRange(QtProperty *property, double minimum, double maximum) + + Sets the range of valid values. + + This is a convenience function defining the range of valid values + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new range, the current value is adjusted if + necessary (ensuring that the value remains within range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtDoublePropertyManager::setRange(QtProperty *property, double minVal, double maxVal) +{ + void (QtDoublePropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, double, double, double) = 0; + setBorderValues(this, d_ptr, + &QtDoublePropertyManager::propertyChanged, + &QtDoublePropertyManager::valueChanged, + &QtDoublePropertyManager::rangeChanged, + property, minVal, maxVal, setSubPropertyRange); +} + +/*! + \reimp +*/ +void QtDoublePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtDoublePropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtDoublePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtStringPropertyManager + +class QtStringPropertyManagerPrivate +{ + QtStringPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtStringPropertyManager) +public: + + struct Data + { + Data() : regExp(QString(QLatin1Char('*')), Qt::CaseSensitive, QRegExp::Wildcard) + { + } + QString val; + QRegExp regExp; + }; + + typedef QMap PropertyValueMap; + QMap m_values; +}; + +/*! + \class QtStringPropertyManager + + \brief The QtStringPropertyManager provides and manages QString properties. + + A string property's value can be retrieved using the value() + function, and set using the setValue() slot. + + The current value can be checked against a regular expression. To + set the regular expression use the setRegExp() slot, use the + regExp() function to retrieve the currently set expression. + + In addition, QtStringPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the regExpChanged() signal which is emitted whenever + such a property changes its currently set regular expression. + + \sa QtAbstractPropertyManager, QtLineEditFactory +*/ + +/*! + \fn void QtStringPropertyManager::valueChanged(QtProperty *property, const QString &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtStringPropertyManager::regExpChanged(QtProperty *property, const QRegExp ®Exp) + + This signal is emitted whenever a property created by this manager + changes its currenlty set regular expression, passing a pointer to + the \a property and the new \a regExp as parameters. + + \sa setRegExp() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtStringPropertyManager::QtStringPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtStringPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtStringPropertyManager::~QtStringPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns an empty string. + + \sa setValue() +*/ +QString QtStringPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's currently set regular expression. + + If the given \a property is not managed by this manager, this + function returns an empty expression. + + \sa setRegExp() +*/ +QRegExp QtStringPropertyManager::regExp(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtStringPropertyManagerPrivate::Data::regExp, property, QRegExp()); +} + +/*! + \reimp +*/ +QString QtStringPropertyManager::valueText(const QtProperty *property) const +{ + const QtStringPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().val; +} + +/*! + \fn void QtStringPropertyManager::setValue(QtProperty *property, const QString &value) + + Sets the value of the given \a property to \a value. + + If the specified \a value doesn't match the given \a property's + regular expression, this function does nothing. + + \sa value(), setRegExp(), valueChanged() +*/ +void QtStringPropertyManager::setValue(QtProperty *property, const QString &val) +{ + const QtStringPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtStringPropertyManagerPrivate::Data data = it.value(); + + if (data.val == val) + return; + + if (data.regExp.isValid() && !data.regExp.exactMatch(val)) + return; + + data.val = val; + + it.value() = data; + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the regular expression of the given \a property to \a regExp. + + \sa regExp(), setValue(), regExpChanged() +*/ +void QtStringPropertyManager::setRegExp(QtProperty *property, const QRegExp ®Exp) +{ + const QtStringPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtStringPropertyManagerPrivate::Data data = it.value() ; + + if (data.regExp == regExp) + return; + + data.regExp = regExp; + + it.value() = data; + + emit regExpChanged(property, data.regExp); +} + +/*! + \reimp +*/ +void QtStringPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtStringPropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtStringPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtBoolPropertyManager + +class QtBoolPropertyManagerPrivate +{ + QtBoolPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtBoolPropertyManager) +public: + + QMap m_values; +}; + +/*! + \class QtBoolPropertyManager + + \brief The QtBoolPropertyManager class provides and manages boolean properties. + + The property's value can be retrieved using the value() function, + and set using the setValue() slot. + + In addition, QtBoolPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager, QtCheckBoxFactory +*/ + +/*! + \fn void QtBoolPropertyManager::valueChanged(QtProperty *property, bool value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtBoolPropertyManager::QtBoolPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtBoolPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtBoolPropertyManager::~QtBoolPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by \e this manager, this + function returns false. + + \sa setValue() +*/ +bool QtBoolPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, false); +} + +/*! + \reimp +*/ +QString QtBoolPropertyManager::valueText(const QtProperty *property) const +{ + return QString(); +} + +// Return an icon containing a check box indicator +static QIcon drawCheckBox(bool value) +{ + QStyleOptionButton opt; + opt.state |= value ? QStyle::State_On : QStyle::State_Off; + opt.state |= QStyle::State_Enabled; + const QStyle *style = QApplication::style(); + // Figure out size of an indicator and make sure it is not scaled down in a list view item + // by making the pixmap as big as a list view icon and centering the indicator in it. + // (if it is smaller, it can't be helped) + const int indicatorWidth = style->pixelMetric(QStyle::PM_IndicatorWidth, &opt); + const int indicatorHeight = style->pixelMetric(QStyle::PM_IndicatorHeight, &opt); + const int listViewIconSize = indicatorWidth; + const int pixmapWidth = indicatorWidth; + const int pixmapHeight = qMax(indicatorHeight, listViewIconSize); + + opt.rect = QRect(0, 0, indicatorWidth, indicatorHeight); + QPixmap pixmap = QPixmap(pixmapWidth, pixmapHeight); + pixmap.fill(Qt::transparent); + { + // Center? + const int xoff = (pixmapWidth > indicatorWidth) ? (pixmapWidth - indicatorWidth) / 2 : 0; + const int yoff = (pixmapHeight > indicatorHeight) ? (pixmapHeight - indicatorHeight) / 2 : 0; + QPainter painter(&pixmap); + painter.translate(xoff, yoff); + style->drawPrimitive(QStyle::PE_IndicatorCheckBox, &opt, &painter); + } + return QIcon(pixmap); +} + +/*! + \reimp +*/ +QIcon QtBoolPropertyManager::valueIcon(const QtProperty *property) const +{ + const QMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + + static const QIcon checkedIcon = drawCheckBox(true); + static const QIcon uncheckedIcon = drawCheckBox(false); + return it.value() ? checkedIcon : uncheckedIcon; +} + +/*! + \fn void QtBoolPropertyManager::setValue(QtProperty *property, bool value) + + Sets the value of the given \a property to \a value. + + \sa value() +*/ +void QtBoolPropertyManager::setValue(QtProperty *property, bool val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtBoolPropertyManager::propertyChanged, + &QtBoolPropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtBoolPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = false; +} + +/*! + \reimp +*/ +void QtBoolPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtDatePropertyManager + +class QtDatePropertyManagerPrivate +{ + QtDatePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtDatePropertyManager) +public: + + struct Data + { + Data() : val(QDate::currentDate()), minVal(QDate(1752, 9, 14)), + maxVal(QDate(7999, 12, 31)) {} + QDate val; + QDate minVal; + QDate maxVal; + QDate minimumValue() const { return minVal; } + QDate maximumValue() const { return maxVal; } + void setMinimumValue(const QDate &newMinVal) { setSimpleMinimumData(this, newMinVal); } + void setMaximumValue(const QDate &newMaxVal) { setSimpleMaximumData(this, newMaxVal); } + }; + + QString m_format; + + typedef QMap PropertyValueMap; + QMap m_values; +}; + +/*! + \class QtDatePropertyManager + + \brief The QtDatePropertyManager provides and manages QDate properties. + + A date property has a current value, and a range specifying the + valid dates. The range is defined by a minimum and a maximum + value. + + The property's values can be retrieved using the minimum(), + maximum() and value() functions, and can be set using the + setMinimum(), setMaximum() and setValue() slots. Alternatively, + the range can be defined in one go using the setRange() slot. + + In addition, QtDatePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the rangeChanged() signal which is emitted whenever + such a property changes its range of valid dates. + + \sa QtAbstractPropertyManager, QtDateEditFactory, QtDateTimePropertyManager +*/ + +/*! + \fn void QtDatePropertyManager::valueChanged(QtProperty *property, const QDate &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtDatePropertyManager::rangeChanged(QtProperty *property, const QDate &minimum, const QDate &maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid dates, passing a pointer to the \a + property and the new \a minimum and \a maximum dates. + + \sa setRange() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtDatePropertyManager::QtDatePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtDatePropertyManagerPrivate; + d_ptr->q_ptr = this; + + QLocale loc; + d_ptr->m_format = loc.dateFormat(QLocale::ShortFormat); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtDatePropertyManager::~QtDatePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by \e this manager, this + function returns an invalid date. + + \sa setValue() +*/ +QDate QtDatePropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's minimum date. + + \sa maximum(), setRange() +*/ +QDate QtDatePropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's maximum date. + + \sa minimum(), setRange() +*/ +QDate QtDatePropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property); +} + +/*! + \reimp +*/ +QString QtDatePropertyManager::valueText(const QtProperty *property) const +{ + const QtDatePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().val.toString(d_ptr->m_format); +} + +/*! + \fn void QtDatePropertyManager::setValue(QtProperty *property, const QDate &value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not a valid date according to the + given \a property's range, the value is adjusted to the nearest + valid value within the range. + + \sa value(), setRange(), valueChanged() +*/ +void QtDatePropertyManager::setValue(QtProperty *property, const QDate &val) +{ + void (QtDatePropertyManagerPrivate::*setSubPropertyValue)(QtProperty *, const QDate &) = 0; + setValueInRange(this, d_ptr, + &QtDatePropertyManager::propertyChanged, + &QtDatePropertyManager::valueChanged, + property, val, setSubPropertyValue); +} + +/*! + Sets the minimum value for the given \a property to \a minVal. + + When setting the minimum value, the maximum and current values are + adjusted if necessary (ensuring that the range remains valid and + that the current value is within in the range). + + \sa minimum(), setRange() +*/ +void QtDatePropertyManager::setMinimum(QtProperty *property, const QDate &minVal) +{ + setMinimumValue(this, d_ptr, + &QtDatePropertyManager::propertyChanged, + &QtDatePropertyManager::valueChanged, + &QtDatePropertyManager::rangeChanged, + property, minVal); +} + +/*! + Sets the maximum value for the given \a property to \a maxVal. + + When setting the maximum value, the minimum and current + values are adjusted if necessary (ensuring that the range remains + valid and that the current value is within in the range). + + \sa maximum(), setRange() +*/ +void QtDatePropertyManager::setMaximum(QtProperty *property, const QDate &maxVal) +{ + setMaximumValue(this, d_ptr, + &QtDatePropertyManager::propertyChanged, + &QtDatePropertyManager::valueChanged, + &QtDatePropertyManager::rangeChanged, + property, maxVal); +} + +/*! + \fn void QtDatePropertyManager::setRange(QtProperty *property, const QDate &minimum, const QDate &maximum) + + Sets the range of valid dates. + + This is a convenience function defining the range of valid dates + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new date range, the current value is adjusted if + necessary (ensuring that the value remains in date range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtDatePropertyManager::setRange(QtProperty *property, const QDate &minVal, const QDate &maxVal) +{ + void (QtDatePropertyManagerPrivate::*setSubPropertyRange)(QtProperty *, const QDate &, + const QDate &, const QDate &) = 0; + setBorderValues(this, d_ptr, + &QtDatePropertyManager::propertyChanged, + &QtDatePropertyManager::valueChanged, + &QtDatePropertyManager::rangeChanged, + property, minVal, maxVal, setSubPropertyRange); +} + +/*! + \reimp +*/ +void QtDatePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtDatePropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtDatePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtTimePropertyManager + +class QtTimePropertyManagerPrivate +{ + QtTimePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtTimePropertyManager) +public: + + QString m_format; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtTimePropertyManager + + \brief The QtTimePropertyManager provides and manages QTime properties. + + A time property's value can be retrieved using the value() + function, and set using the setValue() slot. + + In addition, QtTimePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager, QtTimeEditFactory +*/ + +/*! + \fn void QtTimePropertyManager::valueChanged(QtProperty *property, const QTime &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtTimePropertyManager::QtTimePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtTimePropertyManagerPrivate; + d_ptr->q_ptr = this; + + QLocale loc; + d_ptr->m_format = loc.timeFormat(QLocale::ShortFormat); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtTimePropertyManager::~QtTimePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns an invalid time object. + + \sa setValue() +*/ +QTime QtTimePropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QTime()); +} + +/*! + \reimp +*/ +QString QtTimePropertyManager::valueText(const QtProperty *property) const +{ + const QtTimePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().toString(d_ptr->m_format); +} + +/*! + \fn void QtTimePropertyManager::setValue(QtProperty *property, const QTime &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtTimePropertyManager::setValue(QtProperty *property, const QTime &val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtTimePropertyManager::propertyChanged, + &QtTimePropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtTimePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QTime::currentTime(); +} + +/*! + \reimp +*/ +void QtTimePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtDateTimePropertyManager + +class QtDateTimePropertyManagerPrivate +{ + QtDateTimePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtDateTimePropertyManager) +public: + + QString m_format; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! \class QtDateTimePropertyManager + + \brief The QtDateTimePropertyManager provides and manages QDateTime properties. + + A date and time property has a current value which can be + retrieved using the value() function, and set using the setValue() + slot. In addition, QtDateTimePropertyManager provides the + valueChanged() signal which is emitted whenever a property created + by this manager changes. + + \sa QtAbstractPropertyManager, QtDateTimeEditFactory, QtDatePropertyManager +*/ + +/*! + \fn void QtDateTimePropertyManager::valueChanged(QtProperty *property, const QDateTime &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtDateTimePropertyManager::QtDateTimePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtDateTimePropertyManagerPrivate; + d_ptr->q_ptr = this; + + QLocale loc; + d_ptr->m_format = loc.dateFormat(QLocale::ShortFormat); + d_ptr->m_format += QLatin1Char(' '); + d_ptr->m_format += loc.timeFormat(QLocale::ShortFormat); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtDateTimePropertyManager::~QtDateTimePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid QDateTime object. + + \sa setValue() +*/ +QDateTime QtDateTimePropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QDateTime()); +} + +/*! + \reimp +*/ +QString QtDateTimePropertyManager::valueText(const QtProperty *property) const +{ + const QtDateTimePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().toString(d_ptr->m_format); +} + +/*! + \fn void QtDateTimePropertyManager::setValue(QtProperty *property, const QDateTime &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtDateTimePropertyManager::setValue(QtProperty *property, const QDateTime &val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtDateTimePropertyManager::propertyChanged, + &QtDateTimePropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtDateTimePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QDateTime::currentDateTime(); +} + +/*! + \reimp +*/ +void QtDateTimePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtKeySequencePropertyManager + +class QtKeySequencePropertyManagerPrivate +{ + QtKeySequencePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtKeySequencePropertyManager) +public: + + QString m_format; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! \class QtKeySequencePropertyManager + + \brief The QtKeySequencePropertyManager provides and manages QKeySequence properties. + + A key sequence's value can be retrieved using the value() + function, and set using the setValue() slot. + + In addition, QtKeySequencePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager +*/ + +/*! + \fn void QtKeySequencePropertyManager::valueChanged(QtProperty *property, const QKeySequence &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtKeySequencePropertyManager::QtKeySequencePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtKeySequencePropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtKeySequencePropertyManager::~QtKeySequencePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an empty QKeySequence object. + + \sa setValue() +*/ +QKeySequence QtKeySequencePropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QKeySequence()); +} + +/*! + \reimp +*/ +QString QtKeySequencePropertyManager::valueText(const QtProperty *property) const +{ + const QtKeySequencePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + return it.value().toString(QKeySequence::NativeText); +} + +/*! + \fn void QtKeySequencePropertyManager::setValue(QtProperty *property, const QKeySequence &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtKeySequencePropertyManager::setValue(QtProperty *property, const QKeySequence &val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtKeySequencePropertyManager::propertyChanged, + &QtKeySequencePropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtKeySequencePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QKeySequence(); +} + +/*! + \reimp +*/ +void QtKeySequencePropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtCharPropertyManager + +class QtCharPropertyManagerPrivate +{ + QtCharPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtCharPropertyManager) +public: + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! \class QtCharPropertyManager + + \brief The QtCharPropertyManager provides and manages QChar properties. + + A char's value can be retrieved using the value() + function, and set using the setValue() slot. + + In addition, QtCharPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager +*/ + +/*! + \fn void QtCharPropertyManager::valueChanged(QtProperty *property, const QChar &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtCharPropertyManager::QtCharPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtCharPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtCharPropertyManager::~QtCharPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an null QChar object. + + \sa setValue() +*/ +QChar QtCharPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QChar()); +} + +/*! + \reimp +*/ +QString QtCharPropertyManager::valueText(const QtProperty *property) const +{ + const QtCharPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QChar c = it.value(); + return c.isNull() ? QString() : QString(c); +} + +/*! + \fn void QtCharPropertyManager::setValue(QtProperty *property, const QChar &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtCharPropertyManager::setValue(QtProperty *property, const QChar &val) +{ + setSimpleValue(d_ptr->m_values, this, + &QtCharPropertyManager::propertyChanged, + &QtCharPropertyManager::valueChanged, + property, val); +} + +/*! + \reimp +*/ +void QtCharPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QChar(); +} + +/*! + \reimp +*/ +void QtCharPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtLocalePropertyManager + +class QtLocalePropertyManagerPrivate +{ + QtLocalePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtLocalePropertyManager) +public: + + QtLocalePropertyManagerPrivate(); + + void slotEnumChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtEnumPropertyManager *m_enumPropertyManager; + + QMap m_propertyToLanguage; + QMap m_propertyToCountry; + + QMap m_languageToProperty; + QMap m_countryToProperty; +}; + +QtLocalePropertyManagerPrivate::QtLocalePropertyManagerPrivate() +{ +} + +void QtLocalePropertyManagerPrivate::slotEnumChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_languageToProperty.value(property, 0)) { + const QLocale loc = m_values[prop]; + QLocale::Language newLanguage = loc.language(); + QLocale::Country newCountry = loc.country(); + metaEnumProvider()->indexToLocale(value, 0, &newLanguage, 0); + QLocale newLoc(newLanguage, newCountry); + q_ptr->setValue(prop, newLoc); + } else if (QtProperty *prop = m_countryToProperty.value(property, 0)) { + const QLocale loc = m_values[prop]; + QLocale::Language newLanguage = loc.language(); + QLocale::Country newCountry = loc.country(); + metaEnumProvider()->indexToLocale(m_enumPropertyManager->value(m_propertyToLanguage.value(prop)), value, &newLanguage, &newCountry); + QLocale newLoc(newLanguage, newCountry); + q_ptr->setValue(prop, newLoc); + } +} + +void QtLocalePropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *subProp = m_languageToProperty.value(property, 0)) { + m_propertyToLanguage[subProp] = 0; + m_languageToProperty.remove(property); + } else if (QtProperty *subProp = m_countryToProperty.value(property, 0)) { + m_propertyToCountry[subProp] = 0; + m_countryToProperty.remove(property); + } +} + +/*! + \class QtLocalePropertyManager + + \brief The QtLocalePropertyManager provides and manages QLocale properties. + + A locale property has nested \e language and \e country + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by QtEnumPropertyManager object. + These submanager can be retrieved using the subEnumPropertyManager() + function. In order to provide editing widgets for the subproperties + in a property browser widget, this manager must be associated with editor factory. + + In addition, QtLocalePropertyManager provides the valueChanged() + signal which is emitted whenever a property created by this + manager changes. + + \sa QtAbstractPropertyManager, QtEnumPropertyManager +*/ + +/*! + \fn void QtLocalePropertyManager::valueChanged(QtProperty *property, const QLocale &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtLocalePropertyManager::QtLocalePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtLocalePropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); + connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotEnumChanged(QtProperty *, int))); + + connect(d_ptr->m_enumPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtLocalePropertyManager::~QtLocalePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e language + and \e country subproperties. + + In order to provide editing widgets for the mentioned subproperties + in a property browser widget, this manager must be associated with + an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtEnumPropertyManager *QtLocalePropertyManager::subEnumPropertyManager() const +{ + return d_ptr->m_enumPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns the default locale. + + \sa setValue() +*/ +QLocale QtLocalePropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QLocale()); +} + +/*! + \reimp +*/ +QString QtLocalePropertyManager::valueText(const QtProperty *property) const +{ + const QtLocalePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + QLocale loc = it.value(); + + int langIdx = 0; + int countryIdx = 0; + metaEnumProvider()->localeToIndex(loc.language(), loc.country(), &langIdx, &countryIdx); + QString str = tr("%1, %2") + .arg(metaEnumProvider()->languageEnumNames().at(langIdx)) + .arg(metaEnumProvider()->countryEnumNames(loc.language()).at(countryIdx)); + return str; +} + +/*! + \fn void QtLocalePropertyManager::setValue(QtProperty *property, const QLocale &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtLocalePropertyManager::setValue(QtProperty *property, const QLocale &val) +{ + const QtLocalePropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + const QLocale loc = it.value(); + if (loc == val) + return; + + it.value() = val; + + int langIdx = 0; + int countryIdx = 0; + metaEnumProvider()->localeToIndex(val.language(), val.country(), &langIdx, &countryIdx); + if (loc.language() != val.language()) { + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToLanguage.value(property), langIdx); + d_ptr->m_enumPropertyManager->setEnumNames(d_ptr->m_propertyToCountry.value(property), + metaEnumProvider()->countryEnumNames(val.language())); + } + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToCountry.value(property), countryIdx); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtLocalePropertyManager::initializeProperty(QtProperty *property) +{ + QLocale val; + d_ptr->m_values[property] = val; + + int langIdx = 0; + int countryIdx = 0; + metaEnumProvider()->localeToIndex(val.language(), val.country(), &langIdx, &countryIdx); + + QtProperty *languageProp = d_ptr->m_enumPropertyManager->addProperty(); + languageProp->setPropertyName(tr("Language")); + d_ptr->m_enumPropertyManager->setEnumNames(languageProp, metaEnumProvider()->languageEnumNames()); + d_ptr->m_enumPropertyManager->setValue(languageProp, langIdx); + d_ptr->m_propertyToLanguage[property] = languageProp; + d_ptr->m_languageToProperty[languageProp] = property; + property->addSubProperty(languageProp); + + QtProperty *countryProp = d_ptr->m_enumPropertyManager->addProperty(); + countryProp->setPropertyName(tr("Country")); + d_ptr->m_enumPropertyManager->setEnumNames(countryProp, metaEnumProvider()->countryEnumNames(val.language())); + d_ptr->m_enumPropertyManager->setValue(countryProp, countryIdx); + d_ptr->m_propertyToCountry[property] = countryProp; + d_ptr->m_countryToProperty[countryProp] = property; + property->addSubProperty(countryProp); +} + +/*! + \reimp +*/ +void QtLocalePropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *languageProp = d_ptr->m_propertyToLanguage[property]; + if (languageProp) { + d_ptr->m_languageToProperty.remove(languageProp); + delete languageProp; + } + d_ptr->m_propertyToLanguage.remove(property); + + QtProperty *countryProp = d_ptr->m_propertyToCountry[property]; + if (countryProp) { + d_ptr->m_countryToProperty.remove(countryProp); + delete countryProp; + } + d_ptr->m_propertyToCountry.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtPointPropertyManager + +class QtPointPropertyManagerPrivate +{ + QtPointPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtPointPropertyManager) +public: + + void slotIntChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + + QMap m_propertyToX; + QMap m_propertyToY; + + QMap m_xToProperty; + QMap m_yToProperty; +}; + +void QtPointPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *xprop = m_xToProperty.value(property, 0)) { + QPoint p = m_values[xprop]; + p.setX(value); + q_ptr->setValue(xprop, p); + } else if (QtProperty *yprop = m_yToProperty.value(property, 0)) { + QPoint p = m_values[yprop]; + p.setY(value); + q_ptr->setValue(yprop, p); + } +} + +void QtPointPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { + m_propertyToX[pointProp] = 0; + m_xToProperty.remove(property); + } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { + m_propertyToY[pointProp] = 0; + m_yToProperty.remove(property); + } +} + +/*! \class QtPointPropertyManager + + \brief The QtPointPropertyManager provides and manages QPoint properties. + + A point property has nested \e x and \e y subproperties. The + top-level property's value can be retrieved using the value() + function, and set using the setValue() slot. + + The subproperties are created by a QtIntPropertyManager object. This + manager can be retrieved using the subIntPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + In addition, QtPointPropertyManager provides the valueChanged() signal which + is emitted whenever a property created by this manager changes. + + \sa QtAbstractPropertyManager, QtIntPropertyManager, QtPointFPropertyManager +*/ + +/*! + \fn void QtPointPropertyManager::valueChanged(QtProperty *property, const QPoint &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtPointPropertyManager::QtPointPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtPointPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtPointPropertyManager::~QtPointPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e x and \e y + subproperties. + + In order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtPointPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns a point with coordinates (0, 0). + + \sa setValue() +*/ +QPoint QtPointPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QPoint()); +} + +/*! + \reimp +*/ +QString QtPointPropertyManager::valueText(const QtProperty *property) const +{ + const QtPointPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QPoint v = it.value(); + return QString(tr("(%1, %2)").arg(QString::number(v.x())) + .arg(QString::number(v.y()))); +} + +/*! + \fn void QtPointPropertyManager::setValue(QtProperty *property, const QPoint &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtPointPropertyManager::setValue(QtProperty *property, const QPoint &val) +{ + const QtPointPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value() == val) + return; + + it.value() = val; + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToX[property], val.x()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToY[property], val.y()); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtPointPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QPoint(0, 0); + + QtProperty *xProp = d_ptr->m_intPropertyManager->addProperty(); + xProp->setPropertyName(tr("X")); + d_ptr->m_intPropertyManager->setValue(xProp, 0); + d_ptr->m_propertyToX[property] = xProp; + d_ptr->m_xToProperty[xProp] = property; + property->addSubProperty(xProp); + + QtProperty *yProp = d_ptr->m_intPropertyManager->addProperty(); + yProp->setPropertyName(tr("Y")); + d_ptr->m_intPropertyManager->setValue(yProp, 0); + d_ptr->m_propertyToY[property] = yProp; + d_ptr->m_yToProperty[yProp] = property; + property->addSubProperty(yProp); +} + +/*! + \reimp +*/ +void QtPointPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *xProp = d_ptr->m_propertyToX[property]; + if (xProp) { + d_ptr->m_xToProperty.remove(xProp); + delete xProp; + } + d_ptr->m_propertyToX.remove(property); + + QtProperty *yProp = d_ptr->m_propertyToY[property]; + if (yProp) { + d_ptr->m_yToProperty.remove(yProp); + delete yProp; + } + d_ptr->m_propertyToY.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtPointFPropertyManager + +class QtPointFPropertyManagerPrivate +{ + QtPointFPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtPointFPropertyManager) +public: + + struct Data + { + Data() : decimals(2) {} + QPointF val; + int decimals; + }; + + void slotDoubleChanged(QtProperty *property, double value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtDoublePropertyManager *m_doublePropertyManager; + + QMap m_propertyToX; + QMap m_propertyToY; + + QMap m_xToProperty; + QMap m_yToProperty; +}; + +void QtPointFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) +{ + if (QtProperty *prop = m_xToProperty.value(property, 0)) { + QPointF p = m_values[prop].val; + p.setX(value); + q_ptr->setValue(prop, p); + } else if (QtProperty *prop = m_yToProperty.value(property, 0)) { + QPointF p = m_values[prop].val; + p.setY(value); + q_ptr->setValue(prop, p); + } +} + +void QtPointFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { + m_propertyToX[pointProp] = 0; + m_xToProperty.remove(property); + } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { + m_propertyToY[pointProp] = 0; + m_yToProperty.remove(property); + } +} + +/*! \class QtPointFPropertyManager + + \brief The QtPointFPropertyManager provides and manages QPointF properties. + + A point property has nested \e x and \e y subproperties. The + top-level property's value can be retrieved using the value() + function, and set using the setValue() slot. + + The subproperties are created by a QtDoublePropertyManager object. This + manager can be retrieved using the subDoublePropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + In addition, QtPointFPropertyManager provides the valueChanged() signal which + is emitted whenever a property created by this manager changes. + + \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtPointPropertyManager +*/ + +/*! + \fn void QtPointFPropertyManager::valueChanged(QtProperty *property, const QPointF &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtPointFPropertyManager::decimalsChanged(QtProperty *property, int prec) + + This signal is emitted whenever a property created by this manager + changes its precision of value, passing a pointer to the + \a property and the new \a prec value + + \sa setDecimals() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtPointFPropertyManager::QtPointFPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtPointFPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); + connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotDoubleChanged(QtProperty *, double))); + connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtPointFPropertyManager::~QtPointFPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e x and \e y + subproperties. + + In order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtDoublePropertyManager *QtPointFPropertyManager::subDoublePropertyManager() const +{ + return d_ptr->m_doublePropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns a point with coordinates (0, 0). + + \sa setValue() +*/ +QPointF QtPointFPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's precision, in decimals. + + \sa setDecimals() +*/ +int QtPointFPropertyManager::decimals(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtPointFPropertyManagerPrivate::Data::decimals, property, 0); +} + +/*! + \reimp +*/ +QString QtPointFPropertyManager::valueText(const QtProperty *property) const +{ + const QtPointFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QPointF v = it.value().val; + const int dec = it.value().decimals; + return QString(tr("(%1, %2)").arg(QString::number(v.x(), 'f', dec)) + .arg(QString::number(v.y(), 'f', dec))); +} + +/*! + \fn void QtPointFPropertyManager::setValue(QtProperty *property, const QPointF &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtPointFPropertyManager::setValue(QtProperty *property, const QPointF &val) +{ + const QtPointFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value().val == val) + return; + + it.value().val = val; + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToX[property], val.x()); + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToY[property], val.y()); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \fn void QtPointFPropertyManager::setDecimals(QtProperty *property, int prec) + + Sets the precision of the given \a property to \a prec. + + The valid decimal range is 0-13. The default is 2. + + \sa decimals() +*/ +void QtPointFPropertyManager::setDecimals(QtProperty *property, int prec) +{ + const QtPointFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtPointFPropertyManagerPrivate::Data data = it.value(); + + if (prec > 13) + prec = 13; + else if (prec < 0) + prec = 0; + + if (data.decimals == prec) + return; + + data.decimals = prec; + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToX[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToY[property], prec); + + it.value() = data; + + emit decimalsChanged(property, data.decimals); +} + +/*! + \reimp +*/ +void QtPointFPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtPointFPropertyManagerPrivate::Data(); + + QtProperty *xProp = d_ptr->m_doublePropertyManager->addProperty(); + xProp->setPropertyName(tr("X")); + d_ptr->m_doublePropertyManager->setDecimals(xProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(xProp, 0); + d_ptr->m_propertyToX[property] = xProp; + d_ptr->m_xToProperty[xProp] = property; + property->addSubProperty(xProp); + + QtProperty *yProp = d_ptr->m_doublePropertyManager->addProperty(); + yProp->setPropertyName(tr("Y")); + d_ptr->m_doublePropertyManager->setDecimals(yProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(yProp, 0); + d_ptr->m_propertyToY[property] = yProp; + d_ptr->m_yToProperty[yProp] = property; + property->addSubProperty(yProp); +} + +/*! + \reimp +*/ +void QtPointFPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *xProp = d_ptr->m_propertyToX[property]; + if (xProp) { + d_ptr->m_xToProperty.remove(xProp); + delete xProp; + } + d_ptr->m_propertyToX.remove(property); + + QtProperty *yProp = d_ptr->m_propertyToY[property]; + if (yProp) { + d_ptr->m_yToProperty.remove(yProp); + delete yProp; + } + d_ptr->m_propertyToY.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtSizePropertyManager + +class QtSizePropertyManagerPrivate +{ + QtSizePropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtSizePropertyManager) +public: + + void slotIntChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + void setValue(QtProperty *property, const QSize &val); + void setRange(QtProperty *property, + const QSize &minVal, const QSize &maxVal, const QSize &val); + + struct Data + { + Data() : val(QSize(0, 0)), minVal(QSize(0, 0)), maxVal(QSize(INT_MAX, INT_MAX)) {} + QSize val; + QSize minVal; + QSize maxVal; + QSize minimumValue() const { return minVal; } + QSize maximumValue() const { return maxVal; } + void setMinimumValue(const QSize &newMinVal) { setSizeMinimumData(this, newMinVal); } + void setMaximumValue(const QSize &newMaxVal) { setSizeMaximumData(this, newMaxVal); } + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + + QMap m_propertyToW; + QMap m_propertyToH; + + QMap m_wToProperty; + QMap m_hToProperty; +}; + +void QtSizePropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_wToProperty.value(property, 0)) { + QSize s = m_values[prop].val; + s.setWidth(value); + q_ptr->setValue(prop, s); + } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { + QSize s = m_values[prop].val; + s.setHeight(value); + q_ptr->setValue(prop, s); + } +} + +void QtSizePropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { + m_propertyToW[pointProp] = 0; + m_wToProperty.remove(property); + } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { + m_propertyToH[pointProp] = 0; + m_hToProperty.remove(property); + } +} + +void QtSizePropertyManagerPrivate::setValue(QtProperty *property, const QSize &val) +{ + m_intPropertyManager->setValue(m_propertyToW.value(property), val.width()); + m_intPropertyManager->setValue(m_propertyToH.value(property), val.height()); +} + +void QtSizePropertyManagerPrivate::setRange(QtProperty *property, + const QSize &minVal, const QSize &maxVal, const QSize &val) +{ + QtProperty *wProperty = m_propertyToW.value(property); + QtProperty *hProperty = m_propertyToH.value(property); + m_intPropertyManager->setRange(wProperty, minVal.width(), maxVal.width()); + m_intPropertyManager->setValue(wProperty, val.width()); + m_intPropertyManager->setRange(hProperty, minVal.height(), maxVal.height()); + m_intPropertyManager->setValue(hProperty, val.height()); +} + +/*! + \class QtSizePropertyManager + + \brief The QtSizePropertyManager provides and manages QSize properties. + + A size property has nested \e width and \e height + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtIntPropertyManager object. This + manager can be retrieved using the subIntPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + A size property also has a range of valid values defined by a + minimum size and a maximum size. These sizes can be retrieved + using the minimum() and the maximum() functions, and set using the + setMinimum() and setMaximum() slots. Alternatively, the range can + be defined in one go using the setRange() slot. + + In addition, QtSizePropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the rangeChanged() signal which is emitted whenever + such a property changes its range of valid sizes. + + \sa QtAbstractPropertyManager, QtIntPropertyManager, QtSizeFPropertyManager +*/ + +/*! + \fn void QtSizePropertyManager::valueChanged(QtProperty *property, const QSize &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtSizePropertyManager::rangeChanged(QtProperty *property, const QSize &minimum, const QSize &maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid sizes, passing a pointer to the \a + property and the new \a minimum and \a maximum sizes. + + \sa setRange() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtSizePropertyManager::QtSizePropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtSizePropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtSizePropertyManager::~QtSizePropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e width and \e height + subproperties. + + In order to provide editing widgets for the \e width and \e height + properties in a property browser widget, this manager must be + associated with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtSizePropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid size + + \sa setValue() +*/ +QSize QtSizePropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's minimum size value. + + \sa setMinimum(), maximum(), setRange() +*/ +QSize QtSizePropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's maximum size value. + + \sa setMaximum(), minimum(), setRange() +*/ +QSize QtSizePropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property); +} + +/*! + \reimp +*/ +QString QtSizePropertyManager::valueText(const QtProperty *property) const +{ + const QtSizePropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QSize v = it.value().val; + return QString(tr("%1 x %2").arg(QString::number(v.width())) + .arg(QString::number(v.height()))); +} + +/*! + \fn void QtSizePropertyManager::setValue(QtProperty *property, const QSize &value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not valid according to the given \a + property's size range, the \a value is adjusted to the nearest + valid value within the size range. + + \sa value(), setRange(), valueChanged() +*/ +void QtSizePropertyManager::setValue(QtProperty *property, const QSize &val) +{ + setValueInRange(this, d_ptr, + &QtSizePropertyManager::propertyChanged, + &QtSizePropertyManager::valueChanged, + property, val, &QtSizePropertyManagerPrivate::setValue); +} + +/*! + Sets the minimum size value for the given \a property to \a minVal. + + When setting the minimum size value, the maximum and current + values are adjusted if necessary (ensuring that the size range + remains valid and that the current value is within the range). + + \sa minimum(), setRange(), rangeChanged() +*/ +void QtSizePropertyManager::setMinimum(QtProperty *property, const QSize &minVal) +{ + setBorderValue(this, d_ptr, + &QtSizePropertyManager::propertyChanged, + &QtSizePropertyManager::valueChanged, + &QtSizePropertyManager::rangeChanged, + property, + &QtSizePropertyManagerPrivate::Data::minimumValue, + &QtSizePropertyManagerPrivate::Data::setMinimumValue, + minVal, &QtSizePropertyManagerPrivate::setRange); +} + +/*! + Sets the maximum size value for the given \a property to \a maxVal. + + When setting the maximum size value, the minimum and current + values are adjusted if necessary (ensuring that the size range + remains valid and that the current value is within the range). + + \sa maximum(), setRange(), rangeChanged() +*/ +void QtSizePropertyManager::setMaximum(QtProperty *property, const QSize &maxVal) +{ + setBorderValue(this, d_ptr, + &QtSizePropertyManager::propertyChanged, + &QtSizePropertyManager::valueChanged, + &QtSizePropertyManager::rangeChanged, + property, + &QtSizePropertyManagerPrivate::Data::maximumValue, + &QtSizePropertyManagerPrivate::Data::setMaximumValue, + maxVal, &QtSizePropertyManagerPrivate::setRange); +} + +/*! + \fn void QtSizePropertyManager::setRange(QtProperty *property, const QSize &minimum, const QSize &maximum) + + Sets the range of valid values. + + This is a convenience function defining the range of valid values + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new range, the current value is adjusted if + necessary (ensuring that the value remains within the range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtSizePropertyManager::setRange(QtProperty *property, const QSize &minVal, const QSize &maxVal) +{ + setBorderValues(this, d_ptr, + &QtSizePropertyManager::propertyChanged, + &QtSizePropertyManager::valueChanged, + &QtSizePropertyManager::rangeChanged, + property, minVal, maxVal, &QtSizePropertyManagerPrivate::setRange); +} + +/*! + \reimp +*/ +void QtSizePropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtSizePropertyManagerPrivate::Data(); + + QtProperty *wProp = d_ptr->m_intPropertyManager->addProperty(); + wProp->setPropertyName(tr("Width")); + d_ptr->m_intPropertyManager->setValue(wProp, 0); + d_ptr->m_intPropertyManager->setMinimum(wProp, 0); + d_ptr->m_propertyToW[property] = wProp; + d_ptr->m_wToProperty[wProp] = property; + property->addSubProperty(wProp); + + QtProperty *hProp = d_ptr->m_intPropertyManager->addProperty(); + hProp->setPropertyName(tr("Height")); + d_ptr->m_intPropertyManager->setValue(hProp, 0); + d_ptr->m_intPropertyManager->setMinimum(hProp, 0); + d_ptr->m_propertyToH[property] = hProp; + d_ptr->m_hToProperty[hProp] = property; + property->addSubProperty(hProp); +} + +/*! + \reimp +*/ +void QtSizePropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *wProp = d_ptr->m_propertyToW[property]; + if (wProp) { + d_ptr->m_wToProperty.remove(wProp); + delete wProp; + } + d_ptr->m_propertyToW.remove(property); + + QtProperty *hProp = d_ptr->m_propertyToH[property]; + if (hProp) { + d_ptr->m_hToProperty.remove(hProp); + delete hProp; + } + d_ptr->m_propertyToH.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtSizeFPropertyManager + +class QtSizeFPropertyManagerPrivate +{ + QtSizeFPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtSizeFPropertyManager) +public: + + void slotDoubleChanged(QtProperty *property, double value); + void slotPropertyDestroyed(QtProperty *property); + void setValue(QtProperty *property, const QSizeF &val); + void setRange(QtProperty *property, + const QSizeF &minVal, const QSizeF &maxVal, const QSizeF &val); + + struct Data + { + Data() : val(QSizeF(0, 0)), minVal(QSizeF(0, 0)), maxVal(QSizeF(INT_MAX, INT_MAX)), decimals(2) {} + QSizeF val; + QSizeF minVal; + QSizeF maxVal; + int decimals; + QSizeF minimumValue() const { return minVal; } + QSizeF maximumValue() const { return maxVal; } + void setMinimumValue(const QSizeF &newMinVal) { setSizeMinimumData(this, newMinVal); } + void setMaximumValue(const QSizeF &newMaxVal) { setSizeMaximumData(this, newMaxVal); } + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtDoublePropertyManager *m_doublePropertyManager; + + QMap m_propertyToW; + QMap m_propertyToH; + + QMap m_wToProperty; + QMap m_hToProperty; +}; + +void QtSizeFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) +{ + if (QtProperty *prop = m_wToProperty.value(property, 0)) { + QSizeF s = m_values[prop].val; + s.setWidth(value); + q_ptr->setValue(prop, s); + } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { + QSizeF s = m_values[prop].val; + s.setHeight(value); + q_ptr->setValue(prop, s); + } +} + +void QtSizeFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { + m_propertyToW[pointProp] = 0; + m_wToProperty.remove(property); + } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { + m_propertyToH[pointProp] = 0; + m_hToProperty.remove(property); + } +} + +void QtSizeFPropertyManagerPrivate::setValue(QtProperty *property, const QSizeF &val) +{ + m_doublePropertyManager->setValue(m_propertyToW.value(property), val.width()); + m_doublePropertyManager->setValue(m_propertyToH.value(property), val.height()); +} + +void QtSizeFPropertyManagerPrivate::setRange(QtProperty *property, + const QSizeF &minVal, const QSizeF &maxVal, const QSizeF &val) +{ + m_doublePropertyManager->setRange(m_propertyToW[property], minVal.width(), maxVal.width()); + m_doublePropertyManager->setValue(m_propertyToW[property], val.width()); + m_doublePropertyManager->setRange(m_propertyToH[property], minVal.height(), maxVal.height()); + m_doublePropertyManager->setValue(m_propertyToH[property], val.height()); +} + +/*! + \class QtSizeFPropertyManager + + \brief The QtSizeFPropertyManager provides and manages QSizeF properties. + + A size property has nested \e width and \e height + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtDoublePropertyManager object. This + manager can be retrieved using the subDoublePropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + A size property also has a range of valid values defined by a + minimum size and a maximum size. These sizes can be retrieved + using the minimum() and the maximum() functions, and set using the + setMinimum() and setMaximum() slots. Alternatively, the range can + be defined in one go using the setRange() slot. + + In addition, QtSizeFPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the rangeChanged() signal which is emitted whenever + such a property changes its range of valid sizes. + + \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtSizePropertyManager +*/ + +/*! + \fn void QtSizeFPropertyManager::valueChanged(QtProperty *property, const QSizeF &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtSizeFPropertyManager::rangeChanged(QtProperty *property, const QSizeF &minimum, const QSizeF &maximum) + + This signal is emitted whenever a property created by this manager + changes its range of valid sizes, passing a pointer to the \a + property and the new \a minimum and \a maximum sizes. + + \sa setRange() +*/ + +/*! + \fn void QtSizeFPropertyManager::decimalsChanged(QtProperty *property, int prec) + + This signal is emitted whenever a property created by this manager + changes its precision of value, passing a pointer to the + \a property and the new \a prec value + + \sa setDecimals() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtSizeFPropertyManager::QtSizeFPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtSizeFPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); + connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotDoubleChanged(QtProperty *, double))); + connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtSizeFPropertyManager::~QtSizeFPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e width and \e height + subproperties. + + In order to provide editing widgets for the \e width and \e height + properties in a property browser widget, this manager must be + associated with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtDoublePropertyManager *QtSizeFPropertyManager::subDoublePropertyManager() const +{ + return d_ptr->m_doublePropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid size + + \sa setValue() +*/ +QSizeF QtSizeFPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's precision, in decimals. + + \sa setDecimals() +*/ +int QtSizeFPropertyManager::decimals(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtSizeFPropertyManagerPrivate::Data::decimals, property, 0); +} + +/*! + Returns the given \a property's minimum size value. + + \sa setMinimum(), maximum(), setRange() +*/ +QSizeF QtSizeFPropertyManager::minimum(const QtProperty *property) const +{ + return getMinimum(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's maximum size value. + + \sa setMaximum(), minimum(), setRange() +*/ +QSizeF QtSizeFPropertyManager::maximum(const QtProperty *property) const +{ + return getMaximum(d_ptr->m_values, property); +} + +/*! + \reimp +*/ +QString QtSizeFPropertyManager::valueText(const QtProperty *property) const +{ + const QtSizeFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QSizeF v = it.value().val; + const int dec = it.value().decimals; + return QString(tr("%1 x %2").arg(QString::number(v.width(), 'f', dec)) + .arg(QString::number(v.height(), 'f', dec))); +} + +/*! + \fn void QtSizeFPropertyManager::setValue(QtProperty *property, const QSizeF &value) + + Sets the value of the given \a property to \a value. + + If the specified \a value is not valid according to the given \a + property's size range, the \a value is adjusted to the nearest + valid value within the size range. + + \sa value(), setRange(), valueChanged() +*/ +void QtSizeFPropertyManager::setValue(QtProperty *property, const QSizeF &val) +{ + setValueInRange(this, d_ptr, + &QtSizeFPropertyManager::propertyChanged, + &QtSizeFPropertyManager::valueChanged, + property, val, &QtSizeFPropertyManagerPrivate::setValue); +} + +/*! + \fn void QtSizeFPropertyManager::setDecimals(QtProperty *property, int prec) + + Sets the precision of the given \a property to \a prec. + + The valid decimal range is 0-13. The default is 2. + + \sa decimals() +*/ +void QtSizeFPropertyManager::setDecimals(QtProperty *property, int prec) +{ + const QtSizeFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtSizeFPropertyManagerPrivate::Data data = it.value(); + + if (prec > 13) + prec = 13; + else if (prec < 0) + prec = 0; + + if (data.decimals == prec) + return; + + data.decimals = prec; + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToW[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToH[property], prec); + + it.value() = data; + + emit decimalsChanged(property, data.decimals); +} + +/*! + Sets the minimum size value for the given \a property to \a minVal. + + When setting the minimum size value, the maximum and current + values are adjusted if necessary (ensuring that the size range + remains valid and that the current value is within the range). + + \sa minimum(), setRange(), rangeChanged() +*/ +void QtSizeFPropertyManager::setMinimum(QtProperty *property, const QSizeF &minVal) +{ + setBorderValue(this, d_ptr, + &QtSizeFPropertyManager::propertyChanged, + &QtSizeFPropertyManager::valueChanged, + &QtSizeFPropertyManager::rangeChanged, + property, + &QtSizeFPropertyManagerPrivate::Data::minimumValue, + &QtSizeFPropertyManagerPrivate::Data::setMinimumValue, + minVal, &QtSizeFPropertyManagerPrivate::setRange); +} + +/*! + Sets the maximum size value for the given \a property to \a maxVal. + + When setting the maximum size value, the minimum and current + values are adjusted if necessary (ensuring that the size range + remains valid and that the current value is within the range). + + \sa maximum(), setRange(), rangeChanged() +*/ +void QtSizeFPropertyManager::setMaximum(QtProperty *property, const QSizeF &maxVal) +{ + setBorderValue(this, d_ptr, + &QtSizeFPropertyManager::propertyChanged, + &QtSizeFPropertyManager::valueChanged, + &QtSizeFPropertyManager::rangeChanged, + property, + &QtSizeFPropertyManagerPrivate::Data::maximumValue, + &QtSizeFPropertyManagerPrivate::Data::setMaximumValue, + maxVal, &QtSizeFPropertyManagerPrivate::setRange); +} + +/*! + \fn void QtSizeFPropertyManager::setRange(QtProperty *property, const QSizeF &minimum, const QSizeF &maximum) + + Sets the range of valid values. + + This is a convenience function defining the range of valid values + in one go; setting the \a minimum and \a maximum values for the + given \a property with a single function call. + + When setting a new range, the current value is adjusted if + necessary (ensuring that the value remains within the range). + + \sa setMinimum(), setMaximum(), rangeChanged() +*/ +void QtSizeFPropertyManager::setRange(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal) +{ + setBorderValues(this, d_ptr, + &QtSizeFPropertyManager::propertyChanged, + &QtSizeFPropertyManager::valueChanged, + &QtSizeFPropertyManager::rangeChanged, + property, minVal, maxVal, &QtSizeFPropertyManagerPrivate::setRange); +} + +/*! + \reimp +*/ +void QtSizeFPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtSizeFPropertyManagerPrivate::Data(); + + QtProperty *wProp = d_ptr->m_doublePropertyManager->addProperty(); + wProp->setPropertyName(tr("Width")); + d_ptr->m_doublePropertyManager->setDecimals(wProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(wProp, 0); + d_ptr->m_doublePropertyManager->setMinimum(wProp, 0); + d_ptr->m_propertyToW[property] = wProp; + d_ptr->m_wToProperty[wProp] = property; + property->addSubProperty(wProp); + + QtProperty *hProp = d_ptr->m_doublePropertyManager->addProperty(); + hProp->setPropertyName(tr("Height")); + d_ptr->m_doublePropertyManager->setDecimals(hProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(hProp, 0); + d_ptr->m_doublePropertyManager->setMinimum(hProp, 0); + d_ptr->m_propertyToH[property] = hProp; + d_ptr->m_hToProperty[hProp] = property; + property->addSubProperty(hProp); +} + +/*! + \reimp +*/ +void QtSizeFPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *wProp = d_ptr->m_propertyToW[property]; + if (wProp) { + d_ptr->m_wToProperty.remove(wProp); + delete wProp; + } + d_ptr->m_propertyToW.remove(property); + + QtProperty *hProp = d_ptr->m_propertyToH[property]; + if (hProp) { + d_ptr->m_hToProperty.remove(hProp); + delete hProp; + } + d_ptr->m_propertyToH.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtRectPropertyManager + +class QtRectPropertyManagerPrivate +{ + QtRectPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtRectPropertyManager) +public: + + void slotIntChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + void setConstraint(QtProperty *property, const QRect &constraint, const QRect &val); + + struct Data + { + Data() : val(0, 0, 0, 0) {} + QRect val; + QRect constraint; + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + + QMap m_propertyToX; + QMap m_propertyToY; + QMap m_propertyToW; + QMap m_propertyToH; + + QMap m_xToProperty; + QMap m_yToProperty; + QMap m_wToProperty; + QMap m_hToProperty; +}; + +void QtRectPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_xToProperty.value(property, 0)) { + QRect r = m_values[prop].val; + r.moveLeft(value); + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_yToProperty.value(property)) { + QRect r = m_values[prop].val; + r.moveTop(value); + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_wToProperty.value(property, 0)) { + Data data = m_values[prop]; + QRect r = data.val; + r.setWidth(value); + if (!data.constraint.isNull() && data.constraint.x() + data.constraint.width() < r.x() + r.width()) { + r.moveLeft(data.constraint.left() + data.constraint.width() - r.width()); + } + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { + Data data = m_values[prop]; + QRect r = data.val; + r.setHeight(value); + if (!data.constraint.isNull() && data.constraint.y() + data.constraint.height() < r.y() + r.height()) { + r.moveTop(data.constraint.top() + data.constraint.height() - r.height()); + } + q_ptr->setValue(prop, r); + } +} + +void QtRectPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { + m_propertyToX[pointProp] = 0; + m_xToProperty.remove(property); + } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { + m_propertyToY[pointProp] = 0; + m_yToProperty.remove(property); + } else if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { + m_propertyToW[pointProp] = 0; + m_wToProperty.remove(property); + } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { + m_propertyToH[pointProp] = 0; + m_hToProperty.remove(property); + } +} + +void QtRectPropertyManagerPrivate::setConstraint(QtProperty *property, + const QRect &constraint, const QRect &val) +{ + const bool isNull = constraint.isNull(); + const int left = isNull ? INT_MIN : constraint.left(); + const int right = isNull ? INT_MAX : constraint.left() + constraint.width(); + const int top = isNull ? INT_MIN : constraint.top(); + const int bottom = isNull ? INT_MAX : constraint.top() + constraint.height(); + const int width = isNull ? INT_MAX : constraint.width(); + const int height = isNull ? INT_MAX : constraint.height(); + + m_intPropertyManager->setRange(m_propertyToX[property], left, right); + m_intPropertyManager->setRange(m_propertyToY[property], top, bottom); + m_intPropertyManager->setRange(m_propertyToW[property], 0, width); + m_intPropertyManager->setRange(m_propertyToH[property], 0, height); + + m_intPropertyManager->setValue(m_propertyToX[property], val.x()); + m_intPropertyManager->setValue(m_propertyToY[property], val.y()); + m_intPropertyManager->setValue(m_propertyToW[property], val.width()); + m_intPropertyManager->setValue(m_propertyToH[property], val.height()); +} + +/*! + \class QtRectPropertyManager + + \brief The QtRectPropertyManager provides and manages QRect properties. + + A rectangle property has nested \e x, \e y, \e width and \e height + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtIntPropertyManager object. This + manager can be retrieved using the subIntPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + A rectangle property also has a constraint rectangle which can be + retrieved using the constraint() function, and set using the + setConstraint() slot. + + In addition, QtRectPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the constraintChanged() signal which is emitted + whenever such a property changes its constraint rectangle. + + \sa QtAbstractPropertyManager, QtIntPropertyManager, QtRectFPropertyManager +*/ + +/*! + \fn void QtRectPropertyManager::valueChanged(QtProperty *property, const QRect &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtRectPropertyManager::constraintChanged(QtProperty *property, const QRect &constraint) + + This signal is emitted whenever property changes its constraint + rectangle, passing a pointer to the \a property and the new \a + constraint rectangle as parameters. + + \sa setConstraint() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtRectPropertyManager::QtRectPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtRectPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtRectPropertyManager::~QtRectPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e x, \e y, \e width + and \e height subproperties. + + In order to provide editing widgets for the mentioned + subproperties in a property browser widget, this manager must be + associated with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtRectPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid rectangle. + + \sa setValue(), constraint() +*/ +QRect QtRectPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's constraining rectangle. If returned value is null QRect it means there is no constraint applied. + + \sa value(), setConstraint() +*/ +QRect QtRectPropertyManager::constraint(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtRectPropertyManagerPrivate::Data::constraint, property, QRect()); +} + +/*! + \reimp +*/ +QString QtRectPropertyManager::valueText(const QtProperty *property) const +{ + const QtRectPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QRect v = it.value().val; + return QString(tr("[(%1, %2), %3 x %4]").arg(QString::number(v.x())) + .arg(QString::number(v.y())) + .arg(QString::number(v.width())) + .arg(QString::number(v.height()))); +} + +/*! + \fn void QtRectPropertyManager::setValue(QtProperty *property, const QRect &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + If the specified \a value is not inside the given \a property's + constraining rectangle, the value is adjusted accordingly to fit + within the constraint. + + \sa value(), setConstraint(), valueChanged() +*/ +void QtRectPropertyManager::setValue(QtProperty *property, const QRect &val) +{ + const QtRectPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectPropertyManagerPrivate::Data data = it.value(); + + QRect newRect = val.normalized(); + if (!data.constraint.isNull() && !data.constraint.contains(newRect)) { + const QRect r1 = data.constraint; + const QRect r2 = newRect; + newRect.setLeft(qMax(r1.left(), r2.left())); + newRect.setRight(qMin(r1.right(), r2.right())); + newRect.setTop(qMax(r1.top(), r2.top())); + newRect.setBottom(qMin(r1.bottom(), r2.bottom())); + if (newRect.width() < 0 || newRect.height() < 0) + return; + } + + if (data.val == newRect) + return; + + data.val = newRect; + + it.value() = data; + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToX[property], newRect.x()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToY[property], newRect.y()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToW[property], newRect.width()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToH[property], newRect.height()); + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's constraining rectangle to \a + constraint. + + When setting the constraint, the current value is adjusted if + necessary (ensuring that the current rectangle value is inside the + constraint). In order to reset the constraint pass a null QRect value. + + \sa setValue(), constraint(), constraintChanged() +*/ +void QtRectPropertyManager::setConstraint(QtProperty *property, const QRect &constraint) +{ + const QtRectPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectPropertyManagerPrivate::Data data = it.value(); + + QRect newConstraint = constraint.normalized(); + if (data.constraint == newConstraint) + return; + + const QRect oldVal = data.val; + + data.constraint = newConstraint; + + if (!data.constraint.isNull() && !data.constraint.contains(oldVal)) { + QRect r1 = data.constraint; + QRect r2 = data.val; + + if (r2.width() > r1.width()) + r2.setWidth(r1.width()); + if (r2.height() > r1.height()) + r2.setHeight(r1.height()); + if (r2.left() < r1.left()) + r2.moveLeft(r1.left()); + else if (r2.right() > r1.right()) + r2.moveRight(r1.right()); + if (r2.top() < r1.top()) + r2.moveTop(r1.top()); + else if (r2.bottom() > r1.bottom()) + r2.moveBottom(r1.bottom()); + + data.val = r2; + } + + it.value() = data; + + emit constraintChanged(property, data.constraint); + + d_ptr->setConstraint(property, data.constraint, data.val); + + if (data.val == oldVal) + return; + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + \reimp +*/ +void QtRectPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtRectPropertyManagerPrivate::Data(); + + QtProperty *xProp = d_ptr->m_intPropertyManager->addProperty(); + xProp->setPropertyName(tr("X")); + d_ptr->m_intPropertyManager->setValue(xProp, 0); + d_ptr->m_propertyToX[property] = xProp; + d_ptr->m_xToProperty[xProp] = property; + property->addSubProperty(xProp); + + QtProperty *yProp = d_ptr->m_intPropertyManager->addProperty(); + yProp->setPropertyName(tr("Y")); + d_ptr->m_intPropertyManager->setValue(yProp, 0); + d_ptr->m_propertyToY[property] = yProp; + d_ptr->m_yToProperty[yProp] = property; + property->addSubProperty(yProp); + + QtProperty *wProp = d_ptr->m_intPropertyManager->addProperty(); + wProp->setPropertyName(tr("Width")); + d_ptr->m_intPropertyManager->setValue(wProp, 0); + d_ptr->m_intPropertyManager->setMinimum(wProp, 0); + d_ptr->m_propertyToW[property] = wProp; + d_ptr->m_wToProperty[wProp] = property; + property->addSubProperty(wProp); + + QtProperty *hProp = d_ptr->m_intPropertyManager->addProperty(); + hProp->setPropertyName(tr("Height")); + d_ptr->m_intPropertyManager->setValue(hProp, 0); + d_ptr->m_intPropertyManager->setMinimum(hProp, 0); + d_ptr->m_propertyToH[property] = hProp; + d_ptr->m_hToProperty[hProp] = property; + property->addSubProperty(hProp); +} + +/*! + \reimp +*/ +void QtRectPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *xProp = d_ptr->m_propertyToX[property]; + if (xProp) { + d_ptr->m_xToProperty.remove(xProp); + delete xProp; + } + d_ptr->m_propertyToX.remove(property); + + QtProperty *yProp = d_ptr->m_propertyToY[property]; + if (yProp) { + d_ptr->m_yToProperty.remove(yProp); + delete yProp; + } + d_ptr->m_propertyToY.remove(property); + + QtProperty *wProp = d_ptr->m_propertyToW[property]; + if (wProp) { + d_ptr->m_wToProperty.remove(wProp); + delete wProp; + } + d_ptr->m_propertyToW.remove(property); + + QtProperty *hProp = d_ptr->m_propertyToH[property]; + if (hProp) { + d_ptr->m_hToProperty.remove(hProp); + delete hProp; + } + d_ptr->m_propertyToH.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtRectFPropertyManager + +class QtRectFPropertyManagerPrivate +{ + QtRectFPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtRectFPropertyManager) +public: + + void slotDoubleChanged(QtProperty *property, double value); + void slotPropertyDestroyed(QtProperty *property); + void setConstraint(QtProperty *property, const QRectF &constraint, const QRectF &val); + + struct Data + { + Data() : val(0, 0, 0, 0), decimals(2) {} + QRectF val; + QRectF constraint; + int decimals; + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtDoublePropertyManager *m_doublePropertyManager; + + QMap m_propertyToX; + QMap m_propertyToY; + QMap m_propertyToW; + QMap m_propertyToH; + + QMap m_xToProperty; + QMap m_yToProperty; + QMap m_wToProperty; + QMap m_hToProperty; +}; + +void QtRectFPropertyManagerPrivate::slotDoubleChanged(QtProperty *property, double value) +{ + if (QtProperty *prop = m_xToProperty.value(property, 0)) { + QRectF r = m_values[prop].val; + r.moveLeft(value); + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_yToProperty.value(property, 0)) { + QRectF r = m_values[prop].val; + r.moveTop(value); + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_wToProperty.value(property, 0)) { + Data data = m_values[prop]; + QRectF r = data.val; + r.setWidth(value); + if (!data.constraint.isNull() && data.constraint.x() + data.constraint.width() < r.x() + r.width()) { + r.moveLeft(data.constraint.left() + data.constraint.width() - r.width()); + } + q_ptr->setValue(prop, r); + } else if (QtProperty *prop = m_hToProperty.value(property, 0)) { + Data data = m_values[prop]; + QRectF r = data.val; + r.setHeight(value); + if (!data.constraint.isNull() && data.constraint.y() + data.constraint.height() < r.y() + r.height()) { + r.moveTop(data.constraint.top() + data.constraint.height() - r.height()); + } + q_ptr->setValue(prop, r); + } +} + +void QtRectFPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_xToProperty.value(property, 0)) { + m_propertyToX[pointProp] = 0; + m_xToProperty.remove(property); + } else if (QtProperty *pointProp = m_yToProperty.value(property, 0)) { + m_propertyToY[pointProp] = 0; + m_yToProperty.remove(property); + } else if (QtProperty *pointProp = m_wToProperty.value(property, 0)) { + m_propertyToW[pointProp] = 0; + m_wToProperty.remove(property); + } else if (QtProperty *pointProp = m_hToProperty.value(property, 0)) { + m_propertyToH[pointProp] = 0; + m_hToProperty.remove(property); + } +} + +void QtRectFPropertyManagerPrivate::setConstraint(QtProperty *property, + const QRectF &constraint, const QRectF &val) +{ + const bool isNull = constraint.isNull(); + const float left = isNull ? FLT_MIN : constraint.left(); + const float right = isNull ? FLT_MAX : constraint.left() + constraint.width(); + const float top = isNull ? FLT_MIN : constraint.top(); + const float bottom = isNull ? FLT_MAX : constraint.top() + constraint.height(); + const float width = isNull ? FLT_MAX : constraint.width(); + const float height = isNull ? FLT_MAX : constraint.height(); + + m_doublePropertyManager->setRange(m_propertyToX[property], left, right); + m_doublePropertyManager->setRange(m_propertyToY[property], top, bottom); + m_doublePropertyManager->setRange(m_propertyToW[property], 0, width); + m_doublePropertyManager->setRange(m_propertyToH[property], 0, height); + + m_doublePropertyManager->setValue(m_propertyToX[property], val.x()); + m_doublePropertyManager->setValue(m_propertyToY[property], val.y()); + m_doublePropertyManager->setValue(m_propertyToW[property], val.width()); + m_doublePropertyManager->setValue(m_propertyToH[property], val.height()); +} + +/*! + \class QtRectFPropertyManager + + \brief The QtRectFPropertyManager provides and manages QRectF properties. + + A rectangle property has nested \e x, \e y, \e width and \e height + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtDoublePropertyManager object. This + manager can be retrieved using the subDoublePropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + A rectangle property also has a constraint rectangle which can be + retrieved using the constraint() function, and set using the + setConstraint() slot. + + In addition, QtRectFPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the constraintChanged() signal which is emitted + whenever such a property changes its constraint rectangle. + + \sa QtAbstractPropertyManager, QtDoublePropertyManager, QtRectPropertyManager +*/ + +/*! + \fn void QtRectFPropertyManager::valueChanged(QtProperty *property, const QRectF &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtRectFPropertyManager::constraintChanged(QtProperty *property, const QRectF &constraint) + + This signal is emitted whenever property changes its constraint + rectangle, passing a pointer to the \a property and the new \a + constraint rectangle as parameters. + + \sa setConstraint() +*/ + +/*! + \fn void QtRectFPropertyManager::decimalsChanged(QtProperty *property, int prec) + + This signal is emitted whenever a property created by this manager + changes its precision of value, passing a pointer to the + \a property and the new \a prec value + + \sa setDecimals() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtRectFPropertyManager::QtRectFPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtRectFPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_doublePropertyManager = new QtDoublePropertyManager(this); + connect(d_ptr->m_doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotDoubleChanged(QtProperty *, double))); + connect(d_ptr->m_doublePropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtRectFPropertyManager::~QtRectFPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e x, \e y, \e width + and \e height subproperties. + + In order to provide editing widgets for the mentioned + subproperties in a property browser widget, this manager must be + associated with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtDoublePropertyManager *QtRectFPropertyManager::subDoublePropertyManager() const +{ + return d_ptr->m_doublePropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid rectangle. + + \sa setValue(), constraint() +*/ +QRectF QtRectFPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property); +} + +/*! + Returns the given \a property's precision, in decimals. + + \sa setDecimals() +*/ +int QtRectFPropertyManager::decimals(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtRectFPropertyManagerPrivate::Data::decimals, property, 0); +} + +/*! + Returns the given \a property's constraining rectangle. If returned value is null QRectF it means there is no constraint applied. + + \sa value(), setConstraint() +*/ +QRectF QtRectFPropertyManager::constraint(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtRectFPropertyManagerPrivate::Data::constraint, property, QRect()); +} + +/*! + \reimp +*/ +QString QtRectFPropertyManager::valueText(const QtProperty *property) const +{ + const QtRectFPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + const QRectF v = it.value().val; + const int dec = it.value().decimals; + return QString(tr("[(%1, %2), %3 x %4]").arg(QString::number(v.x(), 'f', dec)) + .arg(QString::number(v.y(), 'f', dec)) + .arg(QString::number(v.width(), 'f', dec)) + .arg(QString::number(v.height(), 'f', dec))); +} + +/*! + \fn void QtRectFPropertyManager::setValue(QtProperty *property, const QRectF &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + If the specified \a value is not inside the given \a property's + constraining rectangle, the value is adjusted accordingly to fit + within the constraint. + + \sa value(), setConstraint(), valueChanged() +*/ +void QtRectFPropertyManager::setValue(QtProperty *property, const QRectF &val) +{ + const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectFPropertyManagerPrivate::Data data = it.value(); + + QRectF newRect = val.normalized(); + if (!data.constraint.isNull() && !data.constraint.contains(newRect)) { + const QRectF r1 = data.constraint; + const QRectF r2 = newRect; + newRect.setLeft(qMax(r1.left(), r2.left())); + newRect.setRight(qMin(r1.right(), r2.right())); + newRect.setTop(qMax(r1.top(), r2.top())); + newRect.setBottom(qMin(r1.bottom(), r2.bottom())); + if (newRect.width() < 0 || newRect.height() < 0) + return; + } + + if (data.val == newRect) + return; + + data.val = newRect; + + it.value() = data; + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToX[property], newRect.x()); + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToY[property], newRect.y()); + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToW[property], newRect.width()); + d_ptr->m_doublePropertyManager->setValue(d_ptr->m_propertyToH[property], newRect.height()); + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's constraining rectangle to \a + constraint. + + When setting the constraint, the current value is adjusted if + necessary (ensuring that the current rectangle value is inside the + constraint). In order to reset the constraint pass a null QRectF value. + + \sa setValue(), constraint(), constraintChanged() +*/ +void QtRectFPropertyManager::setConstraint(QtProperty *property, const QRectF &constraint) +{ + const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectFPropertyManagerPrivate::Data data = it.value(); + + QRectF newConstraint = constraint.normalized(); + if (data.constraint == newConstraint) + return; + + const QRectF oldVal = data.val; + + data.constraint = newConstraint; + + if (!data.constraint.isNull() && !data.constraint.contains(oldVal)) { + QRectF r1 = data.constraint; + QRectF r2 = data.val; + + if (r2.width() > r1.width()) + r2.setWidth(r1.width()); + if (r2.height() > r1.height()) + r2.setHeight(r1.height()); + if (r2.left() < r1.left()) + r2.moveLeft(r1.left()); + else if (r2.right() > r1.right()) + r2.moveRight(r1.right()); + if (r2.top() < r1.top()) + r2.moveTop(r1.top()); + else if (r2.bottom() > r1.bottom()) + r2.moveBottom(r1.bottom()); + + data.val = r2; + } + + it.value() = data; + + emit constraintChanged(property, data.constraint); + + d_ptr->setConstraint(property, data.constraint, data.val); + + if (data.val == oldVal) + return; + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + \fn void QtRectFPropertyManager::setDecimals(QtProperty *property, int prec) + + Sets the precision of the given \a property to \a prec. + + The valid decimal range is 0-13. The default is 2. + + \sa decimals() +*/ +void QtRectFPropertyManager::setDecimals(QtProperty *property, int prec) +{ + const QtRectFPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtRectFPropertyManagerPrivate::Data data = it.value(); + + if (prec > 13) + prec = 13; + else if (prec < 0) + prec = 0; + + if (data.decimals == prec) + return; + + data.decimals = prec; + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToX[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToY[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToW[property], prec); + d_ptr->m_doublePropertyManager->setDecimals(d_ptr->m_propertyToH[property], prec); + + it.value() = data; + + emit decimalsChanged(property, data.decimals); +} + +/*! + \reimp +*/ +void QtRectFPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtRectFPropertyManagerPrivate::Data(); + + QtProperty *xProp = d_ptr->m_doublePropertyManager->addProperty(); + xProp->setPropertyName(tr("X")); + d_ptr->m_doublePropertyManager->setDecimals(xProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(xProp, 0); + d_ptr->m_propertyToX[property] = xProp; + d_ptr->m_xToProperty[xProp] = property; + property->addSubProperty(xProp); + + QtProperty *yProp = d_ptr->m_doublePropertyManager->addProperty(); + yProp->setPropertyName(tr("Y")); + d_ptr->m_doublePropertyManager->setDecimals(yProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(yProp, 0); + d_ptr->m_propertyToY[property] = yProp; + d_ptr->m_yToProperty[yProp] = property; + property->addSubProperty(yProp); + + QtProperty *wProp = d_ptr->m_doublePropertyManager->addProperty(); + wProp->setPropertyName(tr("Width")); + d_ptr->m_doublePropertyManager->setDecimals(wProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(wProp, 0); + d_ptr->m_doublePropertyManager->setMinimum(wProp, 0); + d_ptr->m_propertyToW[property] = wProp; + d_ptr->m_wToProperty[wProp] = property; + property->addSubProperty(wProp); + + QtProperty *hProp = d_ptr->m_doublePropertyManager->addProperty(); + hProp->setPropertyName(tr("Height")); + d_ptr->m_doublePropertyManager->setDecimals(hProp, decimals(property)); + d_ptr->m_doublePropertyManager->setValue(hProp, 0); + d_ptr->m_doublePropertyManager->setMinimum(hProp, 0); + d_ptr->m_propertyToH[property] = hProp; + d_ptr->m_hToProperty[hProp] = property; + property->addSubProperty(hProp); +} + +/*! + \reimp +*/ +void QtRectFPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *xProp = d_ptr->m_propertyToX[property]; + if (xProp) { + d_ptr->m_xToProperty.remove(xProp); + delete xProp; + } + d_ptr->m_propertyToX.remove(property); + + QtProperty *yProp = d_ptr->m_propertyToY[property]; + if (yProp) { + d_ptr->m_yToProperty.remove(yProp); + delete yProp; + } + d_ptr->m_propertyToY.remove(property); + + QtProperty *wProp = d_ptr->m_propertyToW[property]; + if (wProp) { + d_ptr->m_wToProperty.remove(wProp); + delete wProp; + } + d_ptr->m_propertyToW.remove(property); + + QtProperty *hProp = d_ptr->m_propertyToH[property]; + if (hProp) { + d_ptr->m_hToProperty.remove(hProp); + delete hProp; + } + d_ptr->m_propertyToH.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtEnumPropertyManager + +class QtEnumPropertyManagerPrivate +{ + QtEnumPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtEnumPropertyManager) +public: + + struct Data + { + Data() : val(-1) {} + int val; + QStringList enumNames; + QMap enumIcons; + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtEnumPropertyManager + + \brief The QtEnumPropertyManager provides and manages enum properties. + + Each enum property has an associated list of enum names which can + be retrieved using the enumNames() function, and set using the + corresponding setEnumNames() function. An enum property's value is + represented by an index in this list, and can be retrieved and set + using the value() and setValue() slots respectively. + + Each enum value can also have an associated icon. The mapping from + values to icons can be set using the setEnumIcons() function and + queried with the enumIcons() function. + + In addition, QtEnumPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. The enumNamesChanged() or enumIconsChanged() signal is emitted + whenever the list of enum names or icons is altered. + + \sa QtAbstractPropertyManager, QtEnumEditorFactory +*/ + +/*! + \fn void QtEnumPropertyManager::valueChanged(QtProperty *property, int value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtEnumPropertyManager::enumNamesChanged(QtProperty *property, const QStringList &names) + + This signal is emitted whenever a property created by this manager + changes its enum names, passing a pointer to the \a property and + the new \a names as parameters. + + \sa setEnumNames() +*/ + +/*! + \fn void QtEnumPropertyManager::enumIconsChanged(QtProperty *property, const QMap &icons) + + This signal is emitted whenever a property created by this manager + changes its enum icons, passing a pointer to the \a property and + the new mapping of values to \a icons as parameters. + + \sa setEnumIcons() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtEnumPropertyManager::QtEnumPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtEnumPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtEnumPropertyManager::~QtEnumPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value which is an index in the + list returned by enumNames() + + If the given property is not managed by this manager, this + function returns -1. + + \sa enumNames(), setValue() +*/ +int QtEnumPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property, -1); +} + +/*! + Returns the given \a property's list of enum names. + + \sa value(), setEnumNames() +*/ +QStringList QtEnumPropertyManager::enumNames(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtEnumPropertyManagerPrivate::Data::enumNames, property, QStringList()); +} + +/*! + Returns the given \a property's map of enum values to their icons. + + \sa value(), setEnumIcons() +*/ +QMap QtEnumPropertyManager::enumIcons(const QtProperty *property) const +{ + return getData >(d_ptr->m_values, &QtEnumPropertyManagerPrivate::Data::enumIcons, property, QMap()); +} + +/*! + \reimp +*/ +QString QtEnumPropertyManager::valueText(const QtProperty *property) const +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + const QtEnumPropertyManagerPrivate::Data &data = it.value(); + + const int v = data.val; + if (v >= 0 && v < data.enumNames.count()) + return data.enumNames.at(v); + return QString(); +} + +/*! + \reimp +*/ +QIcon QtEnumPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + + const QtEnumPropertyManagerPrivate::Data &data = it.value(); + + const int v = data.val; + return data.enumIcons.value(v); +} + +/*! + \fn void QtEnumPropertyManager::setValue(QtProperty *property, int value) + + Sets the value of the given \a property to \a value. + + The specified \a value must be less than the size of the given \a + property's enumNames() list, and larger than (or equal to) 0. + + \sa value(), valueChanged() +*/ +void QtEnumPropertyManager::setValue(QtProperty *property, int val) +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtEnumPropertyManagerPrivate::Data data = it.value(); + + if (val >= data.enumNames.count()) + return; + + if (val < 0 && data.enumNames.count() > 0) + return; + + if (val < 0) + val = -1; + + if (data.val == val) + return; + + data.val = val; + + it.value() = data; + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's list of enum names to \a + enumNames. The \a property's current value is reset to 0 + indicating the first item of the list. + + If the specified \a enumNames list is empty, the \a property's + current value is set to -1. + + \sa enumNames(), enumNamesChanged() +*/ +void QtEnumPropertyManager::setEnumNames(QtProperty *property, const QStringList &enumNames) +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtEnumPropertyManagerPrivate::Data data = it.value(); + + if (data.enumNames == enumNames) + return; + + data.enumNames = enumNames; + + data.val = -1; + + if (enumNames.count() > 0) + data.val = 0; + + it.value() = data; + + emit enumNamesChanged(property, data.enumNames); + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's map of enum values to their icons to \a + enumIcons. + + Each enum value can have associated icon. This association is represented with passed \a enumIcons map. + + \sa enumNames(), enumNamesChanged() +*/ +void QtEnumPropertyManager::setEnumIcons(QtProperty *property, const QMap &enumIcons) +{ + const QtEnumPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + it.value().enumIcons = enumIcons; + + emit enumIconsChanged(property, it.value().enumIcons); + + emit propertyChanged(property); +} + +/*! + \reimp +*/ +void QtEnumPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtEnumPropertyManagerPrivate::Data(); +} + +/*! + \reimp +*/ +void QtEnumPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +// QtFlagPropertyManager + +class QtFlagPropertyManagerPrivate +{ + QtFlagPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtFlagPropertyManager) +public: + + void slotBoolChanged(QtProperty *property, bool value); + void slotPropertyDestroyed(QtProperty *property); + + struct Data + { + Data() : val(-1) {} + int val; + QStringList flagNames; + }; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtBoolPropertyManager *m_boolPropertyManager; + + QMap > m_propertyToFlags; + + QMap m_flagToProperty; +}; + +void QtFlagPropertyManagerPrivate::slotBoolChanged(QtProperty *property, bool value) +{ + QtProperty *prop = m_flagToProperty.value(property, 0); + if (prop == 0) + return; + + QListIterator itProp(m_propertyToFlags[prop]); + int level = 0; + while (itProp.hasNext()) { + QtProperty *p = itProp.next(); + if (p == property) { + int v = m_values[prop].val; + if (value) { + v |= (1 << level); + } else { + v &= ~(1 << level); + } + q_ptr->setValue(prop, v); + return; + } + level++; + } +} + +void QtFlagPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + QtProperty *flagProperty = m_flagToProperty.value(property, 0); + if (flagProperty == 0) + return; + + m_propertyToFlags[flagProperty].replace(m_propertyToFlags[flagProperty].indexOf(property), 0); + m_flagToProperty.remove(property); +} + +/*! + \class QtFlagPropertyManager + + \brief The QtFlagPropertyManager provides and manages flag properties. + + Each flag property has an associated list of flag names which can + be retrieved using the flagNames() function, and set using the + corresponding setFlagNames() function. + + The flag manager provides properties with nested boolean + subproperties representing each flag, i.e. a flag property's value + is the binary combination of the subproperties' values. A + property's value can be retrieved and set using the value() and + setValue() slots respectively. The combination of flags is represented + by single int value - that's why it's possible to store up to + 32 independent flags in one flag property. + + The subproperties are created by a QtBoolPropertyManager object. This + manager can be retrieved using the subBoolPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + In addition, QtFlagPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes, and the flagNamesChanged() signal which is emitted + whenever the list of flag names is altered. + + \sa QtAbstractPropertyManager, QtBoolPropertyManager +*/ + +/*! + \fn void QtFlagPropertyManager::valueChanged(QtProperty *property, int value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtFlagPropertyManager::flagNamesChanged(QtProperty *property, const QStringList &names) + + This signal is emitted whenever a property created by this manager + changes its flag names, passing a pointer to the \a property and the + new \a names as parameters. + + \sa setFlagNames() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtFlagPropertyManager::QtFlagPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtFlagPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_boolPropertyManager = new QtBoolPropertyManager(this); + connect(d_ptr->m_boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotBoolChanged(QtProperty *, bool))); + connect(d_ptr->m_boolPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtFlagPropertyManager::~QtFlagPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that produces the nested boolean subproperties + representing each flag. + + In order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtBoolPropertyManager *QtFlagPropertyManager::subBoolPropertyManager() const +{ + return d_ptr->m_boolPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns 0. + + \sa flagNames(), setValue() +*/ +int QtFlagPropertyManager::value(const QtProperty *property) const +{ + return getValue(d_ptr->m_values, property, 0); +} + +/*! + Returns the given \a property's list of flag names. + + \sa value(), setFlagNames() +*/ +QStringList QtFlagPropertyManager::flagNames(const QtProperty *property) const +{ + return getData(d_ptr->m_values, &QtFlagPropertyManagerPrivate::Data::flagNames, property, QStringList()); +} + +/*! + \reimp +*/ +QString QtFlagPropertyManager::valueText(const QtProperty *property) const +{ + const QtFlagPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + const QtFlagPropertyManagerPrivate::Data &data = it.value(); + + QString str; + int level = 0; + const QChar bar = QLatin1Char('|'); + const QStringList::const_iterator fncend = data.flagNames.constEnd(); + for (QStringList::const_iterator it = data.flagNames.constBegin(); it != fncend; ++it) { + if (data.val & (1 << level)) { + if (!str.isEmpty()) + str += bar; + str += *it; + } + + level++; + } + return str; +} + +/*! + \fn void QtFlagPropertyManager::setValue(QtProperty *property, int value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + The specified \a value must be less than the binary combination of + the property's flagNames() list size (i.e. less than 2\sup n, + where \c n is the size of the list) and larger than (or equal to) + 0. + + \sa value(), valueChanged() +*/ +void QtFlagPropertyManager::setValue(QtProperty *property, int val) +{ + const QtFlagPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtFlagPropertyManagerPrivate::Data data = it.value(); + + if (data.val == val) + return; + + if (val > (1 << data.flagNames.count()) - 1) + return; + + if (val < 0) + return; + + data.val = val; + + it.value() = data; + + QListIterator itProp(d_ptr->m_propertyToFlags[property]); + int level = 0; + while (itProp.hasNext()) { + QtProperty *prop = itProp.next(); + if (prop) + d_ptr->m_boolPropertyManager->setValue(prop, val & (1 << level)); + level++; + } + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + Sets the given \a property's list of flag names to \a flagNames. The + property's current value is reset to 0 indicating the first item + of the list. + + \sa flagNames(), flagNamesChanged() +*/ +void QtFlagPropertyManager::setFlagNames(QtProperty *property, const QStringList &flagNames) +{ + const QtFlagPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + QtFlagPropertyManagerPrivate::Data data = it.value(); + + if (data.flagNames == flagNames) + return; + + data.flagNames = flagNames; + data.val = 0; + + it.value() = data; + + QListIterator itProp(d_ptr->m_propertyToFlags[property]); + while (itProp.hasNext()) { + QtProperty *prop = itProp.next(); + if (prop) { + delete prop; + d_ptr->m_flagToProperty.remove(prop); + } + } + d_ptr->m_propertyToFlags[property].clear(); + + QStringListIterator itFlag(flagNames); + while (itFlag.hasNext()) { + const QString flagName = itFlag.next(); + QtProperty *prop = d_ptr->m_boolPropertyManager->addProperty(); + prop->setPropertyName(flagName); + property->addSubProperty(prop); + d_ptr->m_propertyToFlags[property].append(prop); + d_ptr->m_flagToProperty[prop] = property; + } + + emit flagNamesChanged(property, data.flagNames); + + emit propertyChanged(property); + emit valueChanged(property, data.val); +} + +/*! + \reimp +*/ +void QtFlagPropertyManager::initializeProperty(QtProperty *property) +{ + d_ptr->m_values[property] = QtFlagPropertyManagerPrivate::Data(); + + d_ptr->m_propertyToFlags[property] = QList(); +} + +/*! + \reimp +*/ +void QtFlagPropertyManager::uninitializeProperty(QtProperty *property) +{ + QListIterator itProp(d_ptr->m_propertyToFlags[property]); + while (itProp.hasNext()) { + QtProperty *prop = itProp.next(); + if (prop) { + delete prop; + d_ptr->m_flagToProperty.remove(prop); + } + } + d_ptr->m_propertyToFlags.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtSizePolicyPropertyManager + +class QtSizePolicyPropertyManagerPrivate +{ + QtSizePolicyPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtSizePolicyPropertyManager) +public: + + QtSizePolicyPropertyManagerPrivate(); + + void slotIntChanged(QtProperty *property, int value); + void slotEnumChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + QtEnumPropertyManager *m_enumPropertyManager; + + QMap m_propertyToHPolicy; + QMap m_propertyToVPolicy; + QMap m_propertyToHStretch; + QMap m_propertyToVStretch; + + QMap m_hPolicyToProperty; + QMap m_vPolicyToProperty; + QMap m_hStretchToProperty; + QMap m_vStretchToProperty; +}; + +QtSizePolicyPropertyManagerPrivate::QtSizePolicyPropertyManagerPrivate() +{ +} + +void QtSizePolicyPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_hStretchToProperty.value(property, 0)) { + QSizePolicy sp = m_values[prop]; + sp.setHorizontalStretch(value); + q_ptr->setValue(prop, sp); + } else if (QtProperty *prop = m_vStretchToProperty.value(property, 0)) { + QSizePolicy sp = m_values[prop]; + sp.setVerticalStretch(value); + q_ptr->setValue(prop, sp); + } +} + +void QtSizePolicyPropertyManagerPrivate::slotEnumChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_hPolicyToProperty.value(property, 0)) { + QSizePolicy sp = m_values[prop]; + sp.setHorizontalPolicy(metaEnumProvider()->indexToSizePolicy(value)); + q_ptr->setValue(prop, sp); + } else if (QtProperty *prop = m_vPolicyToProperty.value(property, 0)) { + QSizePolicy sp = m_values[prop]; + sp.setVerticalPolicy(metaEnumProvider()->indexToSizePolicy(value)); + q_ptr->setValue(prop, sp); + } +} + +void QtSizePolicyPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_hStretchToProperty.value(property, 0)) { + m_propertyToHStretch[pointProp] = 0; + m_hStretchToProperty.remove(property); + } else if (QtProperty *pointProp = m_vStretchToProperty.value(property, 0)) { + m_propertyToVStretch[pointProp] = 0; + m_vStretchToProperty.remove(property); + } else if (QtProperty *pointProp = m_hPolicyToProperty.value(property, 0)) { + m_propertyToHPolicy[pointProp] = 0; + m_hPolicyToProperty.remove(property); + } else if (QtProperty *pointProp = m_vPolicyToProperty.value(property, 0)) { + m_propertyToVPolicy[pointProp] = 0; + m_vPolicyToProperty.remove(property); + } +} + +/*! + \class QtSizePolicyPropertyManager + + \brief The QtSizePolicyPropertyManager provides and manages QSizePolicy properties. + + A size policy property has nested \e horizontalPolicy, \e + verticalPolicy, \e horizontalStretch and \e verticalStretch + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by QtIntPropertyManager and QtEnumPropertyManager + objects. These managers can be retrieved using the subIntPropertyManager() + and subEnumPropertyManager() functions respectively. In order to provide + editing widgets for the subproperties in a property browser widget, + these managers must be associated with editor factories. + + In addition, QtSizePolicyPropertyManager provides the valueChanged() + signal which is emitted whenever a property created by this + manager changes. + + \sa QtAbstractPropertyManager, QtIntPropertyManager, QtEnumPropertyManager +*/ + +/*! + \fn void QtSizePolicyPropertyManager::valueChanged(QtProperty *property, const QSizePolicy &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtSizePolicyPropertyManager::QtSizePolicyPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtSizePolicyPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); + connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotEnumChanged(QtProperty *, int))); + + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); + connect(d_ptr->m_enumPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtSizePolicyPropertyManager::~QtSizePolicyPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the nested \e horizontalStretch + and \e verticalStretch subproperties. + + In order to provide editing widgets for the mentioned subproperties + in a property browser widget, this manager must be associated with + an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtSizePolicyPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the manager that creates the nested \e horizontalPolicy + and \e verticalPolicy subproperties. + + In order to provide editing widgets for the mentioned subproperties + in a property browser widget, this manager must be associated with + an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtEnumPropertyManager *QtSizePolicyPropertyManager::subEnumPropertyManager() const +{ + return d_ptr->m_enumPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns the default size policy. + + \sa setValue() +*/ +QSizePolicy QtSizePolicyPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QSizePolicy()); +} + +/*! + \reimp +*/ +QString QtSizePolicyPropertyManager::valueText(const QtProperty *property) const +{ + const QtSizePolicyPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + const QSizePolicy sp = it.value(); + const QtMetaEnumProvider *mep = metaEnumProvider(); + const int hIndex = mep->sizePolicyToIndex(sp.horizontalPolicy()); + const int vIndex = mep->sizePolicyToIndex(sp.verticalPolicy()); + //! Unknown size policy on reading invalid uic3 files + const QString hPolicy = hIndex != -1 ? mep->policyEnumNames().at(hIndex) : tr(""); + const QString vPolicy = vIndex != -1 ? mep->policyEnumNames().at(vIndex) : tr(""); + const QString str = tr("[%1, %2, %3, %4]").arg(hPolicy, vPolicy).arg(sp.horizontalStretch()).arg(sp.verticalStretch()); + return str; +} + +/*! + \fn void QtSizePolicyPropertyManager::setValue(QtProperty *property, const QSizePolicy &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtSizePolicyPropertyManager::setValue(QtProperty *property, const QSizePolicy &val) +{ + const QtSizePolicyPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value() == val) + return; + + it.value() = val; + + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToHPolicy[property], + metaEnumProvider()->sizePolicyToIndex(val.horizontalPolicy())); + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToVPolicy[property], + metaEnumProvider()->sizePolicyToIndex(val.verticalPolicy())); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToHStretch[property], + val.horizontalStretch()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToVStretch[property], + val.verticalStretch()); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtSizePolicyPropertyManager::initializeProperty(QtProperty *property) +{ + QSizePolicy val; + d_ptr->m_values[property] = val; + + QtProperty *hPolicyProp = d_ptr->m_enumPropertyManager->addProperty(); + hPolicyProp->setPropertyName(tr("Horizontal Policy")); + d_ptr->m_enumPropertyManager->setEnumNames(hPolicyProp, metaEnumProvider()->policyEnumNames()); + d_ptr->m_enumPropertyManager->setValue(hPolicyProp, + metaEnumProvider()->sizePolicyToIndex(val.horizontalPolicy())); + d_ptr->m_propertyToHPolicy[property] = hPolicyProp; + d_ptr->m_hPolicyToProperty[hPolicyProp] = property; + property->addSubProperty(hPolicyProp); + + QtProperty *vPolicyProp = d_ptr->m_enumPropertyManager->addProperty(); + vPolicyProp->setPropertyName(tr("Vertical Policy")); + d_ptr->m_enumPropertyManager->setEnumNames(vPolicyProp, metaEnumProvider()->policyEnumNames()); + d_ptr->m_enumPropertyManager->setValue(vPolicyProp, + metaEnumProvider()->sizePolicyToIndex(val.verticalPolicy())); + d_ptr->m_propertyToVPolicy[property] = vPolicyProp; + d_ptr->m_vPolicyToProperty[vPolicyProp] = property; + property->addSubProperty(vPolicyProp); + + QtProperty *hStretchProp = d_ptr->m_intPropertyManager->addProperty(); + hStretchProp->setPropertyName(tr("Horizontal Stretch")); + d_ptr->m_intPropertyManager->setValue(hStretchProp, val.horizontalStretch()); + d_ptr->m_intPropertyManager->setRange(hStretchProp, 0, 0xff); + d_ptr->m_propertyToHStretch[property] = hStretchProp; + d_ptr->m_hStretchToProperty[hStretchProp] = property; + property->addSubProperty(hStretchProp); + + QtProperty *vStretchProp = d_ptr->m_intPropertyManager->addProperty(); + vStretchProp->setPropertyName(tr("Vertical Stretch")); + d_ptr->m_intPropertyManager->setValue(vStretchProp, val.verticalStretch()); + d_ptr->m_intPropertyManager->setRange(vStretchProp, 0, 0xff); + d_ptr->m_propertyToVStretch[property] = vStretchProp; + d_ptr->m_vStretchToProperty[vStretchProp] = property; + property->addSubProperty(vStretchProp); + +} + +/*! + \reimp +*/ +void QtSizePolicyPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *hPolicyProp = d_ptr->m_propertyToHPolicy[property]; + if (hPolicyProp) { + d_ptr->m_hPolicyToProperty.remove(hPolicyProp); + delete hPolicyProp; + } + d_ptr->m_propertyToHPolicy.remove(property); + + QtProperty *vPolicyProp = d_ptr->m_propertyToVPolicy[property]; + if (vPolicyProp) { + d_ptr->m_vPolicyToProperty.remove(vPolicyProp); + delete vPolicyProp; + } + d_ptr->m_propertyToVPolicy.remove(property); + + QtProperty *hStretchProp = d_ptr->m_propertyToHStretch[property]; + if (hStretchProp) { + d_ptr->m_hStretchToProperty.remove(hStretchProp); + delete hStretchProp; + } + d_ptr->m_propertyToHStretch.remove(property); + + QtProperty *vStretchProp = d_ptr->m_propertyToVStretch[property]; + if (vStretchProp) { + d_ptr->m_vStretchToProperty.remove(vStretchProp); + delete vStretchProp; + } + d_ptr->m_propertyToVStretch.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtFontPropertyManager: +// QtFontPropertyManagerPrivate has a mechanism for reacting +// to QApplication::fontDatabaseChanged() [4.5], which is emitted +// when someone loads an application font. The signals are compressed +// using a timer with interval 0, which then causes the family +// enumeration manager to re-set its strings and index values +// for each property. + +Q_GLOBAL_STATIC(QFontDatabase, fontDatabase) + +class QtFontPropertyManagerPrivate +{ + QtFontPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtFontPropertyManager) +public: + + QtFontPropertyManagerPrivate(); + + void slotIntChanged(QtProperty *property, int value); + void slotEnumChanged(QtProperty *property, int value); + void slotBoolChanged(QtProperty *property, bool value); + void slotPropertyDestroyed(QtProperty *property); + void slotFontDatabaseChanged(); + void slotFontDatabaseDelayedChange(); + + QStringList m_familyNames; + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + QtEnumPropertyManager *m_enumPropertyManager; + QtBoolPropertyManager *m_boolPropertyManager; + + QMap m_propertyToFamily; + QMap m_propertyToPointSize; + QMap m_propertyToBold; + QMap m_propertyToItalic; + QMap m_propertyToUnderline; + QMap m_propertyToStrikeOut; + QMap m_propertyToKerning; + + QMap m_familyToProperty; + QMap m_pointSizeToProperty; + QMap m_boldToProperty; + QMap m_italicToProperty; + QMap m_underlineToProperty; + QMap m_strikeOutToProperty; + QMap m_kerningToProperty; + + bool m_settingValue; + QTimer *m_fontDatabaseChangeTimer; +}; + +QtFontPropertyManagerPrivate::QtFontPropertyManagerPrivate() : + m_settingValue(false), + m_fontDatabaseChangeTimer(0) +{ +} + +void QtFontPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (m_settingValue) + return; + if (QtProperty *prop = m_pointSizeToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setPointSize(value); + q_ptr->setValue(prop, f); + } +} + +void QtFontPropertyManagerPrivate::slotEnumChanged(QtProperty *property, int value) +{ + if (m_settingValue) + return; + if (QtProperty *prop = m_familyToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setFamily(m_familyNames.at(value)); + q_ptr->setValue(prop, f); + } +} + +void QtFontPropertyManagerPrivate::slotBoolChanged(QtProperty *property, bool value) +{ + if (m_settingValue) + return; + if (QtProperty *prop = m_boldToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setBold(value); + q_ptr->setValue(prop, f); + } else if (QtProperty *prop = m_italicToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setItalic(value); + q_ptr->setValue(prop, f); + } else if (QtProperty *prop = m_underlineToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setUnderline(value); + q_ptr->setValue(prop, f); + } else if (QtProperty *prop = m_strikeOutToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setStrikeOut(value); + q_ptr->setValue(prop, f); + } else if (QtProperty *prop = m_kerningToProperty.value(property, 0)) { + QFont f = m_values[prop]; + f.setKerning(value); + q_ptr->setValue(prop, f); + } +} + +void QtFontPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_pointSizeToProperty.value(property, 0)) { + m_propertyToPointSize[pointProp] = 0; + m_pointSizeToProperty.remove(property); + } else if (QtProperty *pointProp = m_familyToProperty.value(property, 0)) { + m_propertyToFamily[pointProp] = 0; + m_familyToProperty.remove(property); + } else if (QtProperty *pointProp = m_boldToProperty.value(property, 0)) { + m_propertyToBold[pointProp] = 0; + m_boldToProperty.remove(property); + } else if (QtProperty *pointProp = m_italicToProperty.value(property, 0)) { + m_propertyToItalic[pointProp] = 0; + m_italicToProperty.remove(property); + } else if (QtProperty *pointProp = m_underlineToProperty.value(property, 0)) { + m_propertyToUnderline[pointProp] = 0; + m_underlineToProperty.remove(property); + } else if (QtProperty *pointProp = m_strikeOutToProperty.value(property, 0)) { + m_propertyToStrikeOut[pointProp] = 0; + m_strikeOutToProperty.remove(property); + } else if (QtProperty *pointProp = m_kerningToProperty.value(property, 0)) { + m_propertyToKerning[pointProp] = 0; + m_kerningToProperty.remove(property); + } +} + +void QtFontPropertyManagerPrivate::slotFontDatabaseChanged() +{ + if (!m_fontDatabaseChangeTimer) { + m_fontDatabaseChangeTimer = new QTimer(q_ptr); + m_fontDatabaseChangeTimer->setInterval(0); + m_fontDatabaseChangeTimer->setSingleShot(true); + QObject::connect(m_fontDatabaseChangeTimer, SIGNAL(timeout()), q_ptr, SLOT(slotFontDatabaseDelayedChange())); + } + if (!m_fontDatabaseChangeTimer->isActive()) + m_fontDatabaseChangeTimer->start(); +} + +void QtFontPropertyManagerPrivate::slotFontDatabaseDelayedChange() +{ + typedef QMap PropertyPropertyMap; + // rescan available font names + const QStringList oldFamilies = m_familyNames; + m_familyNames = fontDatabase()->families(); + + // Adapt all existing properties + if (!m_propertyToFamily.empty()) { + PropertyPropertyMap::const_iterator cend = m_propertyToFamily.constEnd(); + for (PropertyPropertyMap::const_iterator it = m_propertyToFamily.constBegin(); it != cend; ++it) { + QtProperty *familyProp = it.value(); + const int oldIdx = m_enumPropertyManager->value(familyProp); + int newIdx = m_familyNames.indexOf(oldFamilies.at(oldIdx)); + if (newIdx < 0) + newIdx = 0; + m_enumPropertyManager->setEnumNames(familyProp, m_familyNames); + m_enumPropertyManager->setValue(familyProp, newIdx); + } + } +} + +/*! + \class QtFontPropertyManager + + \brief The QtFontPropertyManager provides and manages QFont properties. + + A font property has nested \e family, \e pointSize, \e bold, \e + italic, \e underline, \e strikeOut and \e kerning subproperties. The top-level + property's value can be retrieved using the value() function, and + set using the setValue() slot. + + The subproperties are created by QtIntPropertyManager, QtEnumPropertyManager and + QtBoolPropertyManager objects. These managers can be retrieved using the + corresponding subIntPropertyManager(), subEnumPropertyManager() and + subBoolPropertyManager() functions. In order to provide editing widgets + for the subproperties in a property browser widget, these managers + must be associated with editor factories. + + In addition, QtFontPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager, QtEnumPropertyManager, QtIntPropertyManager, QtBoolPropertyManager +*/ + +/*! + \fn void QtFontPropertyManager::valueChanged(QtProperty *property, const QFont &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtFontPropertyManager::QtFontPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtFontPropertyManagerPrivate; + d_ptr->q_ptr = this; +#if QT_VERSION >= 0x040500 + QObject::connect(qApp, SIGNAL(fontDatabaseChanged()), this, SLOT(slotFontDatabaseChanged())); +#endif + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + d_ptr->m_enumPropertyManager = new QtEnumPropertyManager(this); + connect(d_ptr->m_enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotEnumChanged(QtProperty *, int))); + d_ptr->m_boolPropertyManager = new QtBoolPropertyManager(this); + connect(d_ptr->m_boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotBoolChanged(QtProperty *, bool))); + + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); + connect(d_ptr->m_enumPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); + connect(d_ptr->m_boolPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtFontPropertyManager::~QtFontPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that creates the \e pointSize subproperty. + + In order to provide editing widgets for the \e pointSize property + in a property browser widget, this manager must be associated + with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtFontPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the manager that create the \e family subproperty. + + In order to provide editing widgets for the \e family property + in a property browser widget, this manager must be associated + with an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtEnumPropertyManager *QtFontPropertyManager::subEnumPropertyManager() const +{ + return d_ptr->m_enumPropertyManager; +} + +/*! + Returns the manager that creates the \e bold, \e italic, \e underline, + \e strikeOut and \e kerning subproperties. + + In order to provide editing widgets for the mentioned properties + in a property browser widget, this manager must be associated with + an editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtBoolPropertyManager *QtFontPropertyManager::subBoolPropertyManager() const +{ + return d_ptr->m_boolPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given property is not managed by this manager, this + function returns a font object that uses the application's default + font. + + \sa setValue() +*/ +QFont QtFontPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QFont()); +} + +/*! + \reimp +*/ +QString QtFontPropertyManager::valueText(const QtProperty *property) const +{ + const QtFontPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + return QtPropertyBrowserUtils::fontValueText(it.value()); +} + +/*! + \reimp +*/ +QIcon QtFontPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtFontPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + + return QtPropertyBrowserUtils::fontValueIcon(it.value()); +} + +/*! + \fn void QtFontPropertyManager::setValue(QtProperty *property, const QFont &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtFontPropertyManager::setValue(QtProperty *property, const QFont &val) +{ + const QtFontPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + const QFont oldVal = it.value(); + if (oldVal == val && oldVal.resolve() == val.resolve()) + return; + + it.value() = val; + + int idx = d_ptr->m_familyNames.indexOf(val.family()); + if (idx == -1) + idx = 0; + bool settingValue = d_ptr->m_settingValue; + d_ptr->m_settingValue = true; + d_ptr->m_enumPropertyManager->setValue(d_ptr->m_propertyToFamily[property], idx); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToPointSize[property], val.pointSize()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToBold[property], val.bold()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToItalic[property], val.italic()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToUnderline[property], val.underline()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToStrikeOut[property], val.strikeOut()); + d_ptr->m_boolPropertyManager->setValue(d_ptr->m_propertyToKerning[property], val.kerning()); + d_ptr->m_settingValue = settingValue; + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtFontPropertyManager::initializeProperty(QtProperty *property) +{ + QFont val; + d_ptr->m_values[property] = val; + + QtProperty *familyProp = d_ptr->m_enumPropertyManager->addProperty(); + familyProp->setPropertyName(tr("Family")); + if (d_ptr->m_familyNames.empty()) + d_ptr->m_familyNames = fontDatabase()->families(); + d_ptr->m_enumPropertyManager->setEnumNames(familyProp, d_ptr->m_familyNames); + int idx = d_ptr->m_familyNames.indexOf(val.family()); + if (idx == -1) + idx = 0; + d_ptr->m_enumPropertyManager->setValue(familyProp, idx); + d_ptr->m_propertyToFamily[property] = familyProp; + d_ptr->m_familyToProperty[familyProp] = property; + property->addSubProperty(familyProp); + + QtProperty *pointSizeProp = d_ptr->m_intPropertyManager->addProperty(); + pointSizeProp->setPropertyName(tr("Point Size")); + d_ptr->m_intPropertyManager->setValue(pointSizeProp, val.pointSize()); + d_ptr->m_intPropertyManager->setMinimum(pointSizeProp, 1); + d_ptr->m_propertyToPointSize[property] = pointSizeProp; + d_ptr->m_pointSizeToProperty[pointSizeProp] = property; + property->addSubProperty(pointSizeProp); + + QtProperty *boldProp = d_ptr->m_boolPropertyManager->addProperty(); + boldProp->setPropertyName(tr("Bold")); + d_ptr->m_boolPropertyManager->setValue(boldProp, val.bold()); + d_ptr->m_propertyToBold[property] = boldProp; + d_ptr->m_boldToProperty[boldProp] = property; + property->addSubProperty(boldProp); + + QtProperty *italicProp = d_ptr->m_boolPropertyManager->addProperty(); + italicProp->setPropertyName(tr("Italic")); + d_ptr->m_boolPropertyManager->setValue(italicProp, val.italic()); + d_ptr->m_propertyToItalic[property] = italicProp; + d_ptr->m_italicToProperty[italicProp] = property; + property->addSubProperty(italicProp); + + QtProperty *underlineProp = d_ptr->m_boolPropertyManager->addProperty(); + underlineProp->setPropertyName(tr("Underline")); + d_ptr->m_boolPropertyManager->setValue(underlineProp, val.underline()); + d_ptr->m_propertyToUnderline[property] = underlineProp; + d_ptr->m_underlineToProperty[underlineProp] = property; + property->addSubProperty(underlineProp); + + QtProperty *strikeOutProp = d_ptr->m_boolPropertyManager->addProperty(); + strikeOutProp->setPropertyName(tr("Strikeout")); + d_ptr->m_boolPropertyManager->setValue(strikeOutProp, val.strikeOut()); + d_ptr->m_propertyToStrikeOut[property] = strikeOutProp; + d_ptr->m_strikeOutToProperty[strikeOutProp] = property; + property->addSubProperty(strikeOutProp); + + QtProperty *kerningProp = d_ptr->m_boolPropertyManager->addProperty(); + kerningProp->setPropertyName(tr("Kerning")); + d_ptr->m_boolPropertyManager->setValue(kerningProp, val.kerning()); + d_ptr->m_propertyToKerning[property] = kerningProp; + d_ptr->m_kerningToProperty[kerningProp] = property; + property->addSubProperty(kerningProp); +} + +/*! + \reimp +*/ +void QtFontPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *familyProp = d_ptr->m_propertyToFamily[property]; + if (familyProp) { + d_ptr->m_familyToProperty.remove(familyProp); + delete familyProp; + } + d_ptr->m_propertyToFamily.remove(property); + + QtProperty *pointSizeProp = d_ptr->m_propertyToPointSize[property]; + if (pointSizeProp) { + d_ptr->m_pointSizeToProperty.remove(pointSizeProp); + delete pointSizeProp; + } + d_ptr->m_propertyToPointSize.remove(property); + + QtProperty *boldProp = d_ptr->m_propertyToBold[property]; + if (boldProp) { + d_ptr->m_boldToProperty.remove(boldProp); + delete boldProp; + } + d_ptr->m_propertyToBold.remove(property); + + QtProperty *italicProp = d_ptr->m_propertyToItalic[property]; + if (italicProp) { + d_ptr->m_italicToProperty.remove(italicProp); + delete italicProp; + } + d_ptr->m_propertyToItalic.remove(property); + + QtProperty *underlineProp = d_ptr->m_propertyToUnderline[property]; + if (underlineProp) { + d_ptr->m_underlineToProperty.remove(underlineProp); + delete underlineProp; + } + d_ptr->m_propertyToUnderline.remove(property); + + QtProperty *strikeOutProp = d_ptr->m_propertyToStrikeOut[property]; + if (strikeOutProp) { + d_ptr->m_strikeOutToProperty.remove(strikeOutProp); + delete strikeOutProp; + } + d_ptr->m_propertyToStrikeOut.remove(property); + + QtProperty *kerningProp = d_ptr->m_propertyToKerning[property]; + if (kerningProp) { + d_ptr->m_kerningToProperty.remove(kerningProp); + delete kerningProp; + } + d_ptr->m_propertyToKerning.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtColorPropertyManager + +class QtColorPropertyManagerPrivate +{ + QtColorPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtColorPropertyManager) +public: + + void slotIntChanged(QtProperty *property, int value); + void slotPropertyDestroyed(QtProperty *property); + + typedef QMap PropertyValueMap; + PropertyValueMap m_values; + + QtIntPropertyManager *m_intPropertyManager; + + QMap m_propertyToR; + QMap m_propertyToG; + QMap m_propertyToB; + QMap m_propertyToA; + + QMap m_rToProperty; + QMap m_gToProperty; + QMap m_bToProperty; + QMap m_aToProperty; +}; + +void QtColorPropertyManagerPrivate::slotIntChanged(QtProperty *property, int value) +{ + if (QtProperty *prop = m_rToProperty.value(property, 0)) { + QColor c = m_values[prop]; + c.setRed(value); + q_ptr->setValue(prop, c); + } else if (QtProperty *prop = m_gToProperty.value(property, 0)) { + QColor c = m_values[prop]; + c.setGreen(value); + q_ptr->setValue(prop, c); + } else if (QtProperty *prop = m_bToProperty.value(property, 0)) { + QColor c = m_values[prop]; + c.setBlue(value); + q_ptr->setValue(prop, c); + } else if (QtProperty *prop = m_aToProperty.value(property, 0)) { + QColor c = m_values[prop]; + c.setAlpha(value); + q_ptr->setValue(prop, c); + } +} + +void QtColorPropertyManagerPrivate::slotPropertyDestroyed(QtProperty *property) +{ + if (QtProperty *pointProp = m_rToProperty.value(property, 0)) { + m_propertyToR[pointProp] = 0; + m_rToProperty.remove(property); + } else if (QtProperty *pointProp = m_gToProperty.value(property, 0)) { + m_propertyToG[pointProp] = 0; + m_gToProperty.remove(property); + } else if (QtProperty *pointProp = m_bToProperty.value(property, 0)) { + m_propertyToB[pointProp] = 0; + m_bToProperty.remove(property); + } else if (QtProperty *pointProp = m_aToProperty.value(property, 0)) { + m_propertyToA[pointProp] = 0; + m_aToProperty.remove(property); + } +} + +/*! + \class QtColorPropertyManager + + \brief The QtColorPropertyManager provides and manages QColor properties. + + A color property has nested \e red, \e green and \e blue + subproperties. The top-level property's value can be retrieved + using the value() function, and set using the setValue() slot. + + The subproperties are created by a QtIntPropertyManager object. This + manager can be retrieved using the subIntPropertyManager() function. In + order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + In addition, QtColorPropertyManager provides the valueChanged() signal + which is emitted whenever a property created by this manager + changes. + + \sa QtAbstractPropertyManager, QtAbstractPropertyBrowser, QtIntPropertyManager +*/ + +/*! + \fn void QtColorPropertyManager::valueChanged(QtProperty *property, const QColor &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtColorPropertyManager::QtColorPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtColorPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_intPropertyManager = new QtIntPropertyManager(this); + connect(d_ptr->m_intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotIntChanged(QtProperty *, int))); + + connect(d_ptr->m_intPropertyManager, SIGNAL(propertyDestroyed(QtProperty *)), + this, SLOT(slotPropertyDestroyed(QtProperty *))); +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtColorPropertyManager::~QtColorPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the manager that produces the nested \e red, \e green and + \e blue subproperties. + + In order to provide editing widgets for the subproperties in a + property browser widget, this manager must be associated with an + editor factory. + + \sa QtAbstractPropertyBrowser::setFactoryForManager() +*/ +QtIntPropertyManager *QtColorPropertyManager::subIntPropertyManager() const +{ + return d_ptr->m_intPropertyManager; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by \e this manager, this + function returns an invalid color. + + \sa setValue() +*/ +QColor QtColorPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QColor()); +} + +/*! + \reimp +*/ + +QString QtColorPropertyManager::valueText(const QtProperty *property) const +{ + const QtColorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + return QtPropertyBrowserUtils::colorValueText(it.value()); +} + +/*! + \reimp +*/ + +QIcon QtColorPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtColorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + return QtPropertyBrowserUtils::brushValueIcon(QBrush(it.value())); +} + +/*! + \fn void QtColorPropertyManager::setValue(QtProperty *property, const QColor &value) + + Sets the value of the given \a property to \a value. Nested + properties are updated automatically. + + \sa value(), valueChanged() +*/ +void QtColorPropertyManager::setValue(QtProperty *property, const QColor &val) +{ + const QtColorPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value() == val) + return; + + it.value() = val; + + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToR[property], val.red()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToG[property], val.green()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToB[property], val.blue()); + d_ptr->m_intPropertyManager->setValue(d_ptr->m_propertyToA[property], val.alpha()); + + emit propertyChanged(property); + emit valueChanged(property, val); +} + +/*! + \reimp +*/ +void QtColorPropertyManager::initializeProperty(QtProperty *property) +{ + QColor val; + d_ptr->m_values[property] = val; + + QtProperty *rProp = d_ptr->m_intPropertyManager->addProperty(); + rProp->setPropertyName(tr("Red")); + d_ptr->m_intPropertyManager->setValue(rProp, val.red()); + d_ptr->m_intPropertyManager->setRange(rProp, 0, 0xFF); + d_ptr->m_propertyToR[property] = rProp; + d_ptr->m_rToProperty[rProp] = property; + property->addSubProperty(rProp); + + QtProperty *gProp = d_ptr->m_intPropertyManager->addProperty(); + gProp->setPropertyName(tr("Green")); + d_ptr->m_intPropertyManager->setValue(gProp, val.green()); + d_ptr->m_intPropertyManager->setRange(gProp, 0, 0xFF); + d_ptr->m_propertyToG[property] = gProp; + d_ptr->m_gToProperty[gProp] = property; + property->addSubProperty(gProp); + + QtProperty *bProp = d_ptr->m_intPropertyManager->addProperty(); + bProp->setPropertyName(tr("Blue")); + d_ptr->m_intPropertyManager->setValue(bProp, val.blue()); + d_ptr->m_intPropertyManager->setRange(bProp, 0, 0xFF); + d_ptr->m_propertyToB[property] = bProp; + d_ptr->m_bToProperty[bProp] = property; + property->addSubProperty(bProp); + + QtProperty *aProp = d_ptr->m_intPropertyManager->addProperty(); + aProp->setPropertyName(tr("Alpha")); + d_ptr->m_intPropertyManager->setValue(aProp, val.alpha()); + d_ptr->m_intPropertyManager->setRange(aProp, 0, 0xFF); + d_ptr->m_propertyToA[property] = aProp; + d_ptr->m_aToProperty[aProp] = property; + property->addSubProperty(aProp); +} + +/*! + \reimp +*/ +void QtColorPropertyManager::uninitializeProperty(QtProperty *property) +{ + QtProperty *rProp = d_ptr->m_propertyToR[property]; + if (rProp) { + d_ptr->m_rToProperty.remove(rProp); + delete rProp; + } + d_ptr->m_propertyToR.remove(property); + + QtProperty *gProp = d_ptr->m_propertyToG[property]; + if (gProp) { + d_ptr->m_gToProperty.remove(gProp); + delete gProp; + } + d_ptr->m_propertyToG.remove(property); + + QtProperty *bProp = d_ptr->m_propertyToB[property]; + if (bProp) { + d_ptr->m_bToProperty.remove(bProp); + delete bProp; + } + d_ptr->m_propertyToB.remove(property); + + QtProperty *aProp = d_ptr->m_propertyToA[property]; + if (aProp) { + d_ptr->m_aToProperty.remove(aProp); + delete aProp; + } + d_ptr->m_propertyToA.remove(property); + + d_ptr->m_values.remove(property); +} + +// QtCursorPropertyManager + +Q_GLOBAL_STATIC(QtCursorDatabase, cursorDatabase) + +class QtCursorPropertyManagerPrivate +{ + QtCursorPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtCursorPropertyManager) +public: + typedef QMap PropertyValueMap; + PropertyValueMap m_values; +}; + +/*! + \class QtCursorPropertyManager + + \brief The QtCursorPropertyManager provides and manages QCursor properties. + + A cursor property has a current value which can be + retrieved using the value() function, and set using the setValue() + slot. In addition, QtCursorPropertyManager provides the + valueChanged() signal which is emitted whenever a property created + by this manager changes. + + \sa QtAbstractPropertyManager +*/ + +/*! + \fn void QtCursorPropertyManager::valueChanged(QtProperty *property, const QCursor &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the new + \a value as parameters. + + \sa setValue() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtCursorPropertyManager::QtCursorPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtCursorPropertyManagerPrivate; + d_ptr->q_ptr = this; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtCursorPropertyManager::~QtCursorPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns a default QCursor object. + + \sa setValue() +*/ +#ifndef QT_NO_CURSOR +QCursor QtCursorPropertyManager::value(const QtProperty *property) const +{ + return d_ptr->m_values.value(property, QCursor()); +} +#endif + +/*! + \reimp +*/ +QString QtCursorPropertyManager::valueText(const QtProperty *property) const +{ + const QtCursorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QString(); + + return cursorDatabase()->cursorToShapeName(it.value()); +} + +/*! + \reimp +*/ +QIcon QtCursorPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtCursorPropertyManagerPrivate::PropertyValueMap::const_iterator it = d_ptr->m_values.constFind(property); + if (it == d_ptr->m_values.constEnd()) + return QIcon(); + + return cursorDatabase()->cursorToShapeIcon(it.value()); +} + +/*! + \fn void QtCursorPropertyManager::setValue(QtProperty *property, const QCursor &value) + + Sets the value of the given \a property to \a value. + + \sa value(), valueChanged() +*/ +void QtCursorPropertyManager::setValue(QtProperty *property, const QCursor &value) +{ +#ifndef QT_NO_CURSOR + const QtCursorPropertyManagerPrivate::PropertyValueMap::iterator it = d_ptr->m_values.find(property); + if (it == d_ptr->m_values.end()) + return; + + if (it.value().shape() == value.shape() && value.shape() != Qt::BitmapCursor) + return; + + it.value() = value; + + emit propertyChanged(property); + emit valueChanged(property, value); +#endif +} + +/*! + \reimp +*/ +void QtCursorPropertyManager::initializeProperty(QtProperty *property) +{ +#ifndef QT_NO_CURSOR + d_ptr->m_values[property] = QCursor(); +#endif +} + +/*! + \reimp +*/ +void QtCursorPropertyManager::uninitializeProperty(QtProperty *property) +{ + d_ptr->m_values.remove(property); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtpropertymanager.cxx" +#include "qtpropertymanager.moc" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertymanager.h b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertymanager.h new file mode 100644 index 000000000..709f2abf7 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtpropertymanager.h @@ -0,0 +1,796 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTPROPERTYMANAGER_H +#define QTPROPERTYMANAGER_H + +#include "qtpropertybrowser.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QDate; +class QTime; +class QDateTime; +class QLocale; + +class QT_QTPROPERTYBROWSER_EXPORT QtGroupPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtGroupPropertyManager(QObject *parent = 0); + ~QtGroupPropertyManager(); + +protected: + virtual bool hasValue(const QtProperty *property) const; + + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +}; + +class QtIntPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtIntPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtIntPropertyManager(QObject *parent = 0); + ~QtIntPropertyManager(); + + int value(const QtProperty *property) const; + int minimum(const QtProperty *property) const; + int maximum(const QtProperty *property) const; + int singleStep(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, int val); + void setMinimum(QtProperty *property, int minVal); + void setMaximum(QtProperty *property, int maxVal); + void setRange(QtProperty *property, int minVal, int maxVal); + void setSingleStep(QtProperty *property, int step); +Q_SIGNALS: + void valueChanged(QtProperty *property, int val); + void rangeChanged(QtProperty *property, int minVal, int maxVal); + void singleStepChanged(QtProperty *property, int step); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtIntPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtIntPropertyManager) + Q_DISABLE_COPY(QtIntPropertyManager) +}; + +class QtBoolPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtBoolPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtBoolPropertyManager(QObject *parent = 0); + ~QtBoolPropertyManager(); + + bool value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, bool val); +Q_SIGNALS: + void valueChanged(QtProperty *property, bool val); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtBoolPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtBoolPropertyManager) + Q_DISABLE_COPY(QtBoolPropertyManager) +}; + +class QtDoublePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDoublePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtDoublePropertyManager(QObject *parent = 0); + ~QtDoublePropertyManager(); + + double value(const QtProperty *property) const; + double minimum(const QtProperty *property) const; + double maximum(const QtProperty *property) const; + double singleStep(const QtProperty *property) const; + int decimals(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, double val); + void setMinimum(QtProperty *property, double minVal); + void setMaximum(QtProperty *property, double maxVal); + void setRange(QtProperty *property, double minVal, double maxVal); + void setSingleStep(QtProperty *property, double step); + void setDecimals(QtProperty *property, int prec); +Q_SIGNALS: + void valueChanged(QtProperty *property, double val); + void rangeChanged(QtProperty *property, double minVal, double maxVal); + void singleStepChanged(QtProperty *property, double step); + void decimalsChanged(QtProperty *property, int prec); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtDoublePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDoublePropertyManager) + Q_DISABLE_COPY(QtDoublePropertyManager) +}; + +class QtStringPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtStringPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtStringPropertyManager(QObject *parent = 0); + ~QtStringPropertyManager(); + + QString value(const QtProperty *property) const; + QRegExp regExp(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QString &val); + void setRegExp(QtProperty *property, const QRegExp ®Exp); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QString &val); + void regExpChanged(QtProperty *property, const QRegExp ®Exp); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtStringPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtStringPropertyManager) + Q_DISABLE_COPY(QtStringPropertyManager) +}; + +class QtDatePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDatePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtDatePropertyManager(QObject *parent = 0); + ~QtDatePropertyManager(); + + QDate value(const QtProperty *property) const; + QDate minimum(const QtProperty *property) const; + QDate maximum(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QDate &val); + void setMinimum(QtProperty *property, const QDate &minVal); + void setMaximum(QtProperty *property, const QDate &maxVal); + void setRange(QtProperty *property, const QDate &minVal, const QDate &maxVal); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QDate &val); + void rangeChanged(QtProperty *property, const QDate &minVal, const QDate &maxVal); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtDatePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDatePropertyManager) + Q_DISABLE_COPY(QtDatePropertyManager) +}; + +class QtTimePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtTimePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtTimePropertyManager(QObject *parent = 0); + ~QtTimePropertyManager(); + + QTime value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QTime &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QTime &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtTimePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtTimePropertyManager) + Q_DISABLE_COPY(QtTimePropertyManager) +}; + +class QtDateTimePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtDateTimePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtDateTimePropertyManager(QObject *parent = 0); + ~QtDateTimePropertyManager(); + + QDateTime value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QDateTime &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QDateTime &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtDateTimePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtDateTimePropertyManager) + Q_DISABLE_COPY(QtDateTimePropertyManager) +}; + +class QtKeySequencePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtKeySequencePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtKeySequencePropertyManager(QObject *parent = 0); + ~QtKeySequencePropertyManager(); + + QKeySequence value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QKeySequence &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QKeySequence &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtKeySequencePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtKeySequencePropertyManager) + Q_DISABLE_COPY(QtKeySequencePropertyManager) +}; + +class QtCharPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCharPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtCharPropertyManager(QObject *parent = 0); + ~QtCharPropertyManager(); + + QChar value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QChar &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QChar &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtCharPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCharPropertyManager) + Q_DISABLE_COPY(QtCharPropertyManager) +}; + +class QtEnumPropertyManager; +class QtLocalePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtLocalePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtLocalePropertyManager(QObject *parent = 0); + ~QtLocalePropertyManager(); + + QtEnumPropertyManager *subEnumPropertyManager() const; + + QLocale value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QLocale &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QLocale &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtLocalePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtLocalePropertyManager) + Q_DISABLE_COPY(QtLocalePropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtPointPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtPointPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtPointPropertyManager(QObject *parent = 0); + ~QtPointPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + + QPoint value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QPoint &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QPoint &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtPointPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtPointPropertyManager) + Q_DISABLE_COPY(QtPointPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtPointFPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtPointFPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtPointFPropertyManager(QObject *parent = 0); + ~QtPointFPropertyManager(); + + QtDoublePropertyManager *subDoublePropertyManager() const; + + QPointF value(const QtProperty *property) const; + int decimals(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QPointF &val); + void setDecimals(QtProperty *property, int prec); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QPointF &val); + void decimalsChanged(QtProperty *property, int prec); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtPointFPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtPointFPropertyManager) + Q_DISABLE_COPY(QtPointFPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtSizePropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSizePropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtSizePropertyManager(QObject *parent = 0); + ~QtSizePropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + + QSize value(const QtProperty *property) const; + QSize minimum(const QtProperty *property) const; + QSize maximum(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QSize &val); + void setMinimum(QtProperty *property, const QSize &minVal); + void setMaximum(QtProperty *property, const QSize &maxVal); + void setRange(QtProperty *property, const QSize &minVal, const QSize &maxVal); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QSize &val); + void rangeChanged(QtProperty *property, const QSize &minVal, const QSize &maxVal); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtSizePropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSizePropertyManager) + Q_DISABLE_COPY(QtSizePropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtSizeFPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSizeFPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtSizeFPropertyManager(QObject *parent = 0); + ~QtSizeFPropertyManager(); + + QtDoublePropertyManager *subDoublePropertyManager() const; + + QSizeF value(const QtProperty *property) const; + QSizeF minimum(const QtProperty *property) const; + QSizeF maximum(const QtProperty *property) const; + int decimals(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QSizeF &val); + void setMinimum(QtProperty *property, const QSizeF &minVal); + void setMaximum(QtProperty *property, const QSizeF &maxVal); + void setRange(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal); + void setDecimals(QtProperty *property, int prec); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QSizeF &val); + void rangeChanged(QtProperty *property, const QSizeF &minVal, const QSizeF &maxVal); + void decimalsChanged(QtProperty *property, int prec); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtSizeFPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSizeFPropertyManager) + Q_DISABLE_COPY(QtSizeFPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtRectPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtRectPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtRectPropertyManager(QObject *parent = 0); + ~QtRectPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + + QRect value(const QtProperty *property) const; + QRect constraint(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QRect &val); + void setConstraint(QtProperty *property, const QRect &constraint); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QRect &val); + void constraintChanged(QtProperty *property, const QRect &constraint); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtRectPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtRectPropertyManager) + Q_DISABLE_COPY(QtRectPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtRectFPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtRectFPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtRectFPropertyManager(QObject *parent = 0); + ~QtRectFPropertyManager(); + + QtDoublePropertyManager *subDoublePropertyManager() const; + + QRectF value(const QtProperty *property) const; + QRectF constraint(const QtProperty *property) const; + int decimals(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QRectF &val); + void setConstraint(QtProperty *property, const QRectF &constraint); + void setDecimals(QtProperty *property, int prec); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QRectF &val); + void constraintChanged(QtProperty *property, const QRectF &constraint); + void decimalsChanged(QtProperty *property, int prec); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtRectFPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtRectFPropertyManager) + Q_DISABLE_COPY(QtRectFPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotDoubleChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtEnumPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtEnumPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtEnumPropertyManager(QObject *parent = 0); + ~QtEnumPropertyManager(); + + int value(const QtProperty *property) const; + QStringList enumNames(const QtProperty *property) const; + QMap enumIcons(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, int val); + void setEnumNames(QtProperty *property, const QStringList &names); + void setEnumIcons(QtProperty *property, const QMap &icons); +Q_SIGNALS: + void valueChanged(QtProperty *property, int val); + void enumNamesChanged(QtProperty *property, const QStringList &names); + void enumIconsChanged(QtProperty *property, const QMap &icons); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtEnumPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtEnumPropertyManager) + Q_DISABLE_COPY(QtEnumPropertyManager) +}; + +class QtFlagPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtFlagPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtFlagPropertyManager(QObject *parent = 0); + ~QtFlagPropertyManager(); + + QtBoolPropertyManager *subBoolPropertyManager() const; + + int value(const QtProperty *property) const; + QStringList flagNames(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, int val); + void setFlagNames(QtProperty *property, const QStringList &names); +Q_SIGNALS: + void valueChanged(QtProperty *property, int val); + void flagNamesChanged(QtProperty *property, const QStringList &names); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtFlagPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtFlagPropertyManager) + Q_DISABLE_COPY(QtFlagPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotBoolChanged(QtProperty *, bool)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtSizePolicyPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtSizePolicyPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtSizePolicyPropertyManager(QObject *parent = 0); + ~QtSizePolicyPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + QtEnumPropertyManager *subEnumPropertyManager() const; + + QSizePolicy value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QSizePolicy &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QSizePolicy &val); +protected: + QString valueText(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtSizePolicyPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtSizePolicyPropertyManager) + Q_DISABLE_COPY(QtSizePolicyPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtFontPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtFontPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtFontPropertyManager(QObject *parent = 0); + ~QtFontPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + QtEnumPropertyManager *subEnumPropertyManager() const; + QtBoolPropertyManager *subBoolPropertyManager() const; + + QFont value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QFont &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QFont &val); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtFontPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtFontPropertyManager) + Q_DISABLE_COPY(QtFontPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotEnumChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotBoolChanged(QtProperty *, bool)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotFontDatabaseChanged()) + Q_PRIVATE_SLOT(d_func(), void slotFontDatabaseDelayedChange()) +}; + +class QtColorPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtColorPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtColorPropertyManager(QObject *parent = 0); + ~QtColorPropertyManager(); + + QtIntPropertyManager *subIntPropertyManager() const; + + QColor value(const QtProperty *property) const; + +public Q_SLOTS: + void setValue(QtProperty *property, const QColor &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QColor &val); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtColorPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtColorPropertyManager) + Q_DISABLE_COPY(QtColorPropertyManager) + Q_PRIVATE_SLOT(d_func(), void slotIntChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty *)) +}; + +class QtCursorPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtCursorPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtCursorPropertyManager(QObject *parent = 0); + ~QtCursorPropertyManager(); + +#ifndef QT_NO_CURSOR + QCursor value(const QtProperty *property) const; +#endif + +public Q_SLOTS: + void setValue(QtProperty *property, const QCursor &val); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QCursor &val); +protected: + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); +private: + QtCursorPropertyManagerPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtCursorPropertyManager) + Q_DISABLE_COPY(QtCursorPropertyManager) +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qttreepropertybrowser.cpp b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qttreepropertybrowser.cpp new file mode 100644 index 000000000..eb9b37515 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qttreepropertybrowser.cpp @@ -0,0 +1,1095 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qttreepropertybrowser.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtPropertyEditorView; + +class QtTreePropertyBrowserPrivate +{ + QtTreePropertyBrowser *q_ptr; + Q_DECLARE_PUBLIC(QtTreePropertyBrowser) + +public: + QtTreePropertyBrowserPrivate(); + void init(QWidget *parent); + + void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex); + void propertyRemoved(QtBrowserItem *index); + void propertyChanged(QtBrowserItem *index); + QWidget *createEditor(QtProperty *property, QWidget *parent) const + { return q_ptr->createEditor(property, parent); } + QtProperty *indexToProperty(const QModelIndex &index) const; + QTreeWidgetItem *indexToItem(const QModelIndex &index) const; + QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const; + bool lastColumn(int column) const; + void disableItem(QTreeWidgetItem *item) const; + void enableItem(QTreeWidgetItem *item) const; + bool hasValue(QTreeWidgetItem *item) const; + + void slotCollapsed(const QModelIndex &index); + void slotExpanded(const QModelIndex &index); + + QColor calculatedBackgroundColor(QtBrowserItem *item) const; + + QtPropertyEditorView *treeWidget() const { return m_treeWidget; } + bool markPropertiesWithoutValue() const { return m_markPropertiesWithoutValue; } + + QtBrowserItem *currentItem() const; + void setCurrentItem(QtBrowserItem *browserItem, bool block); + void editItem(QtBrowserItem *browserItem); + + void slotCurrentBrowserItemChanged(QtBrowserItem *item); + void slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *); + + QTreeWidgetItem *editedItem() const; + +private: + void updateItem(QTreeWidgetItem *item); + + QMap m_indexToItem; + QMap m_itemToIndex; + + QMap m_indexToBackgroundColor; + + QtPropertyEditorView *m_treeWidget; + + bool m_headerVisible; + QtTreePropertyBrowser::ResizeMode m_resizeMode; + class QtPropertyEditorDelegate *m_delegate; + bool m_markPropertiesWithoutValue; + bool m_browserChangedBlocked; + QIcon m_expandIcon; +}; + +// ------------ QtPropertyEditorView +class QtPropertyEditorView : public QTreeWidget +{ + Q_OBJECT +public: + QtPropertyEditorView(QWidget *parent = 0); + + void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate) + { m_editorPrivate = editorPrivate; } + + QTreeWidgetItem *indexToItem(const QModelIndex &index) const + { return itemFromIndex(index); } + +protected: + void keyPressEvent(QKeyEvent *event); + void mousePressEvent(QMouseEvent *event); + void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + QtTreePropertyBrowserPrivate *m_editorPrivate; +}; + +QtPropertyEditorView::QtPropertyEditorView(QWidget *parent) : + QTreeWidget(parent), + m_editorPrivate(0) +{ + connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int))); +} + +void QtPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QStyleOptionViewItemV3 opt = option; + bool hasValue = true; + if (m_editorPrivate) { + QtProperty *property = m_editorPrivate->indexToProperty(index); + if (property) + { + hasValue = property->hasValue(); + opt.font.setBold(property->isBold()); + } + } + if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) { + const QColor c = option.palette.color(QPalette::Dark); + painter->fillRect(option.rect, c); + opt.palette.setColor(QPalette::AlternateBase, c); + } else { + const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index)); + if (c.isValid()) { + painter->fillRect(option.rect, c); + opt.palette.setColor(QPalette::AlternateBase, c.lighter(112)); + } + } + + QTreeWidget::drawRow(painter, opt, index); + QColor color = static_cast(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt)); + painter->save(); + painter->setPen(QPen(color)); + painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom()); + painter->restore(); +} + +void QtPropertyEditorView::keyPressEvent(QKeyEvent *event) +{ + switch (event->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: + case Qt::Key_Space: // Trigger Edit + if (!m_editorPrivate->editedItem()) + if (const QTreeWidgetItem *item = currentItem()) + if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) { + event->accept(); + // If the current position is at column 0, move to 1. + QModelIndex index = currentIndex(); + if (index.column() == 0) { + index = index.sibling(index.row(), 1); + setCurrentIndex(index); + } + edit(index); + return; + } + break; + default: + break; + } + QTreeWidget::keyPressEvent(event); +} + +void QtPropertyEditorView::mousePressEvent(QMouseEvent *event) +{ + QTreeWidget::mousePressEvent(event); + QTreeWidgetItem *item = itemAt(event->pos()); + + if (item) { + if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton) + && (header()->logicalIndexAt(event->pos().x()) == 1) + && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) { + editItem(item, 1); + } else if (!m_editorPrivate->hasValue(item) && m_editorPrivate->markPropertiesWithoutValue() && !rootIsDecorated()) { + if (event->pos().x() + header()->offset() < 20) + item->setExpanded(!item->isExpanded()); + } + } +} + +// ------------ QtPropertyEditorDelegate +class QtPropertyEditorDelegate : public QItemDelegate +{ + Q_OBJECT +public: + QtPropertyEditorDelegate(QObject *parent = 0) + : QItemDelegate(parent), m_editorPrivate(0), m_editedItem(0), m_editedWidget(0) + {} + + void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate) + { m_editorPrivate = editorPrivate; } + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + + void setModelData(QWidget *, QAbstractItemModel *, + const QModelIndex &) const {} + + void setEditorData(QWidget *, const QModelIndex &) const {} + + bool eventFilter(QObject *object, QEvent *event); + void closeEditor(QtProperty *property); + + QTreeWidgetItem *editedItem() const { return m_editedItem; } + +private slots: + void slotEditorDestroyed(QObject *object); + +private: + int indentation(const QModelIndex &index) const; + + typedef QMap EditorToPropertyMap; + mutable EditorToPropertyMap m_editorToProperty; + + typedef QMap PropertyToEditorMap; + mutable PropertyToEditorMap m_propertyToEditor; + QtTreePropertyBrowserPrivate *m_editorPrivate; + mutable QTreeWidgetItem *m_editedItem; + mutable QWidget *m_editedWidget; +}; + +int QtPropertyEditorDelegate::indentation(const QModelIndex &index) const +{ + if (!m_editorPrivate) + return 0; + + QTreeWidgetItem *item = m_editorPrivate->indexToItem(index); + int indent = 0; + while (item->parent()) { + item = item->parent(); + ++indent; + } + if (m_editorPrivate->treeWidget()->rootIsDecorated()) + ++indent; + return indent * m_editorPrivate->treeWidget()->indentation(); +} + +void QtPropertyEditorDelegate::slotEditorDestroyed(QObject *object) +{ + if (QWidget *w = qobject_cast(object)) { + const EditorToPropertyMap::iterator it = m_editorToProperty.find(w); + if (it != m_editorToProperty.end()) { + m_propertyToEditor.remove(it.value()); + m_editorToProperty.erase(it); + } + if (m_editedWidget == w) { + m_editedWidget = 0; + m_editedItem = 0; + } + } +} + +void QtPropertyEditorDelegate::closeEditor(QtProperty *property) +{ + if (QWidget *w = m_propertyToEditor.value(property, 0)) + w->deleteLater(); +} + +QWidget *QtPropertyEditorDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem &, const QModelIndex &index) const +{ + if (index.column() == 1 && m_editorPrivate) { + QtProperty *property = m_editorPrivate->indexToProperty(index); + QTreeWidgetItem *item = m_editorPrivate->indexToItem(index); + if (property && item && (item->flags() & Qt::ItemIsEnabled)) { + QWidget *editor = m_editorPrivate->createEditor(property, parent); + if (editor) { + editor->setAutoFillBackground(true); + editor->installEventFilter(const_cast(this)); + connect(editor, SIGNAL(destroyed(QObject *)), this, SLOT(slotEditorDestroyed(QObject *))); + m_propertyToEditor[property] = editor; + m_editorToProperty[editor] = property; + m_editedItem = item; + m_editedWidget = editor; + } + return editor; + } + } + return 0; +} + +void QtPropertyEditorDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + Q_UNUSED(index) + editor->setGeometry(option.rect.adjusted(0, 0, 0, -1)); +} + +void QtPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + bool hasValue = true; + if (m_editorPrivate) { + QtProperty *property = m_editorPrivate->indexToProperty(index); + if (property) + hasValue = property->hasValue(); + } + QStyleOptionViewItemV3 opt = option; + if ((m_editorPrivate && index.column() == 0) || !hasValue) { + QtProperty *property = m_editorPrivate->indexToProperty(index); + if (property && property->isModified()) { + opt.font.setBold(true); + opt.fontMetrics = QFontMetrics(opt.font); + } + } + QColor c; + if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) { + c = opt.palette.color(QPalette::Dark); + opt.palette.setColor(QPalette::Text, opt.palette.color(QPalette::BrightText)); + } else { + c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index)); + if (c.isValid() && (opt.features & QStyleOptionViewItemV2::Alternate)) + c = c.lighter(112); + } + if (c.isValid()) + painter->fillRect(option.rect, c); + opt.state &= ~QStyle::State_HasFocus; + QItemDelegate::paint(painter, opt, index); + + opt.palette.setCurrentColorGroup(QPalette::Active); + QColor color = static_cast(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt)); + painter->save(); + painter->setPen(QPen(color)); + if (!m_editorPrivate || (!m_editorPrivate->lastColumn(index.column()) && hasValue)) { + int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left(); + painter->drawLine(right, option.rect.y(), right, option.rect.bottom()); + } + painter->restore(); +} + +QSize QtPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + return QItemDelegate::sizeHint(option, index) + QSize(3, 4); +} + +bool QtPropertyEditorDelegate::eventFilter(QObject *object, QEvent *event) +{ + if (event->type() == QEvent::FocusOut) { + QFocusEvent *fe = static_cast(event); + if (fe->reason() == Qt::ActiveWindowFocusReason) + return false; + } + return QItemDelegate::eventFilter(object, event); +} + +// -------- QtTreePropertyBrowserPrivate implementation +QtTreePropertyBrowserPrivate::QtTreePropertyBrowserPrivate() : + m_treeWidget(0), + m_headerVisible(true), + m_resizeMode(QtTreePropertyBrowser::Stretch), + m_delegate(0), + m_markPropertiesWithoutValue(false), + m_browserChangedBlocked(false) +{ +} + +// Draw an icon indicating opened/closing branches +static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style) +{ + QPixmap pix(14, 14); + pix.fill(Qt::transparent); + QStyleOption branchOption; + QRect r(QPoint(0, 0), pix.size()); + branchOption.rect = QRect(2, 2, 9, 9); // ### hardcoded in qcommonstyle.cpp + branchOption.palette = palette; + branchOption.state = QStyle::State_Children; + + QPainter p; + // Draw closed state + p.begin(&pix); + style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p); + p.end(); + QIcon rc = pix; + rc.addPixmap(pix, QIcon::Selected, QIcon::Off); + // Draw opened state + branchOption.state |= QStyle::State_Open; + pix.fill(Qt::transparent); + p.begin(&pix); + style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p); + p.end(); + + rc.addPixmap(pix, QIcon::Normal, QIcon::On); + rc.addPixmap(pix, QIcon::Selected, QIcon::On); + return rc; +} + +void QtTreePropertyBrowserPrivate::init(QWidget *parent) +{ + QHBoxLayout *layout = new QHBoxLayout(parent); + layout->setMargin(0); + m_treeWidget = new QtPropertyEditorView(parent); + m_treeWidget->setEditorPrivate(this); + m_treeWidget->setIconSize(QSize(18, 18)); + layout->addWidget(m_treeWidget); + + m_treeWidget->setColumnCount(2); + QStringList labels; + labels.append(QApplication::translate("QtTreePropertyBrowser", "Property", 0, QApplication::UnicodeUTF8)); + labels.append(QApplication::translate("QtTreePropertyBrowser", "Value", 0, QApplication::UnicodeUTF8)); + m_treeWidget->setHeaderLabels(labels); + m_treeWidget->setAlternatingRowColors(true); + m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed); + m_delegate = new QtPropertyEditorDelegate(parent); + m_delegate->setEditorPrivate(this); + m_treeWidget->setItemDelegate(m_delegate); + m_treeWidget->header()->setMovable(false); + m_treeWidget->header()->setResizeMode(QHeaderView::Stretch); + + m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style()); + + QObject::connect(m_treeWidget, SIGNAL(collapsed(const QModelIndex &)), q_ptr, SLOT(slotCollapsed(const QModelIndex &))); + QObject::connect(m_treeWidget, SIGNAL(expanded(const QModelIndex &)), q_ptr, SLOT(slotExpanded(const QModelIndex &))); + QObject::connect(m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), q_ptr, SLOT(slotCurrentTreeItemChanged(QTreeWidgetItem*,QTreeWidgetItem*))); +} + +QtBrowserItem *QtTreePropertyBrowserPrivate::currentItem() const +{ + if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem()) + return m_itemToIndex.value(treeItem); + return 0; +} + +void QtTreePropertyBrowserPrivate::setCurrentItem(QtBrowserItem *browserItem, bool block) +{ + const bool blocked = block ? m_treeWidget->blockSignals(true) : false; + if (browserItem == 0) + m_treeWidget->setCurrentItem(0); + else + m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem)); + if (block) + m_treeWidget->blockSignals(blocked); +} + +QtProperty *QtTreePropertyBrowserPrivate::indexToProperty(const QModelIndex &index) const +{ + QTreeWidgetItem *item = m_treeWidget->indexToItem(index); + QtBrowserItem *idx = m_itemToIndex.value(item); + if (idx) + return idx->property(); + return 0; +} + +QtBrowserItem *QtTreePropertyBrowserPrivate::indexToBrowserItem(const QModelIndex &index) const +{ + QTreeWidgetItem *item = m_treeWidget->indexToItem(index); + return m_itemToIndex.value(item); +} + +QTreeWidgetItem *QtTreePropertyBrowserPrivate::indexToItem(const QModelIndex &index) const +{ + return m_treeWidget->indexToItem(index); +} + +bool QtTreePropertyBrowserPrivate::lastColumn(int column) const +{ + return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1; +} + +void QtTreePropertyBrowserPrivate::disableItem(QTreeWidgetItem *item) const +{ + Qt::ItemFlags flags = item->flags(); + if (flags & Qt::ItemIsEnabled) { + flags &= ~Qt::ItemIsEnabled; + item->setFlags(flags); + m_delegate->closeEditor(m_itemToIndex[item]->property()); + const int childCount = item->childCount(); + for (int i = 0; i < childCount; i++) { + QTreeWidgetItem *child = item->child(i); + disableItem(child); + } + } +} + +void QtTreePropertyBrowserPrivate::enableItem(QTreeWidgetItem *item) const +{ + Qt::ItemFlags flags = item->flags(); + flags |= Qt::ItemIsEnabled; + item->setFlags(flags); + const int childCount = item->childCount(); + for (int i = 0; i < childCount; i++) { + QTreeWidgetItem *child = item->child(i); + QtProperty *property = m_itemToIndex[child]->property(); + if (property->isEnabled()) { + enableItem(child); + } + } +} + +bool QtTreePropertyBrowserPrivate::hasValue(QTreeWidgetItem *item) const +{ + QtBrowserItem *browserItem = m_itemToIndex.value(item); + if (browserItem) + return browserItem->property()->hasValue(); + return false; +} + +void QtTreePropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex) +{ + QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex); + QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent()); + + QTreeWidgetItem *newItem = 0; + if (parentItem) { + newItem = new QTreeWidgetItem(parentItem, afterItem); + } else { + newItem = new QTreeWidgetItem(m_treeWidget, afterItem); + } + m_itemToIndex[newItem] = index; + m_indexToItem[index] = newItem; + + newItem->setFlags(newItem->flags() | Qt::ItemIsEditable); + m_treeWidget->setItemExpanded(newItem, true); + + updateItem(newItem); +} + +void QtTreePropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index) +{ + QTreeWidgetItem *item = m_indexToItem.value(index); + + if (m_treeWidget->currentItem() == item) { + m_treeWidget->setCurrentItem(0); + } + + delete item; + + m_indexToItem.remove(index); + m_itemToIndex.remove(item); + m_indexToBackgroundColor.remove(index); +} + +void QtTreePropertyBrowserPrivate::propertyChanged(QtBrowserItem *index) +{ + QTreeWidgetItem *item = m_indexToItem.value(index); + + updateItem(item); +} + +void QtTreePropertyBrowserPrivate::updateItem(QTreeWidgetItem *item) +{ + QtProperty *property = m_itemToIndex[item]->property(); + QIcon expandIcon; + if (property->hasValue()) { + QString toolTip = property->toolTip(); + if (toolTip.isEmpty()) + toolTip = property->valueText(); + item->setToolTip(1, toolTip); + item->setIcon(1, property->valueIcon()); + item->setText(1, property->valueText()); + } else if (markPropertiesWithoutValue() && !m_treeWidget->rootIsDecorated()) { + expandIcon = m_expandIcon; + } + item->setIcon(0, expandIcon); + item->setFirstColumnSpanned(!property->hasValue()); + item->setToolTip(0, property->propertyName()); + item->setStatusTip(0, property->statusTip()); + item->setWhatsThis(0, property->whatsThis()); + item->setText(0, property->propertyName()); + bool wasEnabled = item->flags() & Qt::ItemIsEnabled; + bool isEnabled = wasEnabled; + if (property->isEnabled()) { + QTreeWidgetItem *parent = item->parent(); + if (!parent || (parent->flags() & Qt::ItemIsEnabled)) + isEnabled = true; + else + isEnabled = false; + } else { + isEnabled = false; + } + if (wasEnabled != isEnabled) { + if (isEnabled) + enableItem(item); + else + disableItem(item); + } + m_treeWidget->viewport()->update(); +} + +QColor QtTreePropertyBrowserPrivate::calculatedBackgroundColor(QtBrowserItem *item) const +{ + QtBrowserItem *i = item; + const QMap::const_iterator itEnd = m_indexToBackgroundColor.constEnd(); + while (i) { + QMap::const_iterator it = m_indexToBackgroundColor.constFind(i); + if (it != itEnd) + return it.value(); + i = i->parent(); + } + return QColor(); +} + +void QtTreePropertyBrowserPrivate::slotCollapsed(const QModelIndex &index) +{ + QTreeWidgetItem *item = indexToItem(index); + QtBrowserItem *idx = m_itemToIndex.value(item); + if (item) + emit q_ptr->collapsed(idx); +} + +void QtTreePropertyBrowserPrivate::slotExpanded(const QModelIndex &index) +{ + QTreeWidgetItem *item = indexToItem(index); + QtBrowserItem *idx = m_itemToIndex.value(item); + if (item) + emit q_ptr->expanded(idx); +} + +void QtTreePropertyBrowserPrivate::slotCurrentBrowserItemChanged(QtBrowserItem *item) +{ + if (!m_browserChangedBlocked && item != currentItem()) + setCurrentItem(item, true); +} + +void QtTreePropertyBrowserPrivate::slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *) +{ + QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0; + m_browserChangedBlocked = true; + q_ptr->setCurrentItem(browserItem); + m_browserChangedBlocked = false; +} + +QTreeWidgetItem *QtTreePropertyBrowserPrivate::editedItem() const +{ + return m_delegate->editedItem(); +} + +void QtTreePropertyBrowserPrivate::editItem(QtBrowserItem *browserItem) +{ + if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem, 0)) { + m_treeWidget->setCurrentItem (treeItem, 1); + m_treeWidget->editItem(treeItem, 1); + } +} + +/*! + \class QtTreePropertyBrowser + + \brief The QtTreePropertyBrowser class provides QTreeWidget based + property browser. + + A property browser is a widget that enables the user to edit a + given set of properties. Each property is represented by a label + specifying the property's name, and an editing widget (e.g. a line + edit or a combobox) holding its value. A property can have zero or + more subproperties. + + QtTreePropertyBrowser provides a tree based view for all nested + properties, i.e. properties that have subproperties can be in an + expanded (subproperties are visible) or collapsed (subproperties + are hidden) state. For example: + + \image qttreepropertybrowser.png + + Use the QtAbstractPropertyBrowser API to add, insert and remove + properties from an instance of the QtTreePropertyBrowser class. + The properties themselves are created and managed by + implementations of the QtAbstractPropertyManager class. + + \sa QtGroupBoxPropertyBrowser, QtAbstractPropertyBrowser +*/ + +/*! + \fn void QtTreePropertyBrowser::collapsed(QtBrowserItem *item) + + This signal is emitted when the \a item is collapsed. + + \sa expanded(), setExpanded() +*/ + +/*! + \fn void QtTreePropertyBrowser::expanded(QtBrowserItem *item) + + This signal is emitted when the \a item is expanded. + + \sa collapsed(), setExpanded() +*/ + +/*! + Creates a property browser with the given \a parent. +*/ +QtTreePropertyBrowser::QtTreePropertyBrowser(QWidget *parent) + : QtAbstractPropertyBrowser(parent) +{ + d_ptr = new QtTreePropertyBrowserPrivate; + d_ptr->q_ptr = this; + + d_ptr->init(this); + connect(this, SIGNAL(currentItemChanged(QtBrowserItem*)), this, SLOT(slotCurrentBrowserItemChanged(QtBrowserItem*))); +} + +/*! + Destroys this property browser. + + Note that the properties that were inserted into this browser are + \e not destroyed since they may still be used in other + browsers. The properties are owned by the manager that created + them. + + \sa QtProperty, QtAbstractPropertyManager +*/ +QtTreePropertyBrowser::~QtTreePropertyBrowser() +{ + delete d_ptr; +} + +/*! + \property QtTreePropertyBrowser::indentation + \brief indentation of the items in the tree view. +*/ +int QtTreePropertyBrowser::indentation() const +{ + return d_ptr->m_treeWidget->indentation(); +} + +void QtTreePropertyBrowser::setIndentation(int i) +{ + d_ptr->m_treeWidget->setIndentation(i); +} + +/*! + \property QtTreePropertyBrowser::rootIsDecorated + \brief whether to show controls for expanding and collapsing root items. +*/ +bool QtTreePropertyBrowser::rootIsDecorated() const +{ + return d_ptr->m_treeWidget->rootIsDecorated(); +} + +void QtTreePropertyBrowser::setRootIsDecorated(bool show) +{ + d_ptr->m_treeWidget->setRootIsDecorated(show); + QMapIterator it(d_ptr->m_itemToIndex); + while (it.hasNext()) { + QtProperty *property = it.next().value()->property(); + if (!property->hasValue()) + d_ptr->updateItem(it.key()); + } +} + +/*! + \property QtTreePropertyBrowser::alternatingRowColors + \brief whether to draw the background using alternating colors. + By default this property is set to true. +*/ +bool QtTreePropertyBrowser::alternatingRowColors() const +{ + return d_ptr->m_treeWidget->alternatingRowColors(); +} + +void QtTreePropertyBrowser::setAlternatingRowColors(bool enable) +{ + d_ptr->m_treeWidget->setAlternatingRowColors(enable); + QMapIterator it(d_ptr->m_itemToIndex); +} + +/*! + \property QtTreePropertyBrowser::headerVisible + \brief whether to show the header. +*/ +bool QtTreePropertyBrowser::isHeaderVisible() const +{ + return d_ptr->m_headerVisible; +} + +void QtTreePropertyBrowser::setHeaderVisible(bool visible) +{ + if (d_ptr->m_headerVisible == visible) + return; + + d_ptr->m_headerVisible = visible; + d_ptr->m_treeWidget->header()->setVisible(visible); +} + +/*! + \enum QtTreePropertyBrowser::ResizeMode + + The resize mode specifies the behavior of the header sections. + + \value Interactive The user can resize the sections. + The sections can also be resized programmatically using setSplitterPosition(). + + \value Fixed The user cannot resize the section. + The section can only be resized programmatically using setSplitterPosition(). + + \value Stretch QHeaderView will automatically resize the section to fill the available space. + The size cannot be changed by the user or programmatically. + + \value ResizeToContents QHeaderView will automatically resize the section to its optimal + size based on the contents of the entire column. + The size cannot be changed by the user or programmatically. + + \sa setResizeMode() +*/ + +/*! + \property QtTreePropertyBrowser::resizeMode + \brief the resize mode of setions in the header. +*/ + +QtTreePropertyBrowser::ResizeMode QtTreePropertyBrowser::resizeMode() const +{ + return d_ptr->m_resizeMode; +} + +void QtTreePropertyBrowser::setResizeMode(QtTreePropertyBrowser::ResizeMode mode) +{ + if (d_ptr->m_resizeMode == mode) + return; + + d_ptr->m_resizeMode = mode; + QHeaderView::ResizeMode m = QHeaderView::Stretch; + switch (mode) { + case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive; break; + case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed; break; + case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents; break; + case QtTreePropertyBrowser::Stretch: + default: m = QHeaderView::Stretch; break; + } + d_ptr->m_treeWidget->header()->setResizeMode(m); +} + +/*! + \property QtTreePropertyBrowser::splitterPosition + \brief the position of the splitter between the colunms. +*/ + +int QtTreePropertyBrowser::splitterPosition() const +{ + return d_ptr->m_treeWidget->header()->sectionSize(0); +} + +void QtTreePropertyBrowser::setSplitterPosition(int position) +{ + d_ptr->m_treeWidget->header()->resizeSection(0, position); +} + +/*! + Sets the \a item to either collapse or expanded, depending on the value of \a expanded. + + \sa isExpanded(), expanded(), collapsed() +*/ + +void QtTreePropertyBrowser::setExpanded(QtBrowserItem *item, bool expanded) +{ + QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item); + if (treeItem) + treeItem->setExpanded(expanded); +} + +/*! + Returns true if the \a item is expanded; otherwise returns false. + + \sa setExpanded() +*/ + +bool QtTreePropertyBrowser::isExpanded(QtBrowserItem *item) const +{ + QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item); + if (treeItem) + return treeItem->isExpanded(); + return false; +} + +/*! + Returns true if the \a item is visible; otherwise returns false. + + \sa setItemVisible() + \since 4.5 +*/ + +bool QtTreePropertyBrowser::isItemVisible(QtBrowserItem *item) const +{ + if (const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item)) + return !treeItem->isHidden(); + return false; +} + +/*! + Sets the \a item to be visible, depending on the value of \a visible. + + \sa isItemVisible() + \since 4.5 +*/ + +void QtTreePropertyBrowser::setItemVisible(QtBrowserItem *item, bool visible) +{ + if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item)) + treeItem->setHidden(!visible); +} + +/*! + Sets the \a item's background color to \a color. Note that while item's background + is rendered every second row is being drawn with alternate color (which is a bit lighter than items \a color) + + \sa backgroundColor(), calculatedBackgroundColor() +*/ + +void QtTreePropertyBrowser::setBackgroundColor(QtBrowserItem *item, const QColor &color) +{ + if (!d_ptr->m_indexToItem.contains(item)) + return; + if (color.isValid()) + d_ptr->m_indexToBackgroundColor[item] = color; + else + d_ptr->m_indexToBackgroundColor.remove(item); + d_ptr->m_treeWidget->viewport()->update(); +} + +/*! + Returns the \a item's color. If there is no color set for item it returns invalid color. + + \sa calculatedBackgroundColor(), setBackgroundColor() +*/ + +QColor QtTreePropertyBrowser::backgroundColor(QtBrowserItem *item) const +{ + return d_ptr->m_indexToBackgroundColor.value(item); +} + +/*! + Returns the \a item's color. If there is no color set for item it returns parent \a item's + color (if there is no color set for parent it returns grandparent's color and so on). In case + the color is not set for \a item and it's top level item it returns invalid color. + + \sa backgroundColor(), setBackgroundColor() +*/ + +QColor QtTreePropertyBrowser::calculatedBackgroundColor(QtBrowserItem *item) const +{ + return d_ptr->calculatedBackgroundColor(item); +} + +/*! + \property QtTreePropertyBrowser::propertiesWithoutValueMarked + \brief whether to enable or disable marking properties without value. + + When marking is enabled the item's background is rendered in dark color and item's + foreground is rendered with light color. + + \sa propertiesWithoutValueMarked() +*/ +void QtTreePropertyBrowser::setPropertiesWithoutValueMarked(bool mark) +{ + if (d_ptr->m_markPropertiesWithoutValue == mark) + return; + + d_ptr->m_markPropertiesWithoutValue = mark; + QMapIterator it(d_ptr->m_itemToIndex); + while (it.hasNext()) { + QtProperty *property = it.next().value()->property(); + if (!property->hasValue()) + d_ptr->updateItem(it.key()); + } + d_ptr->m_treeWidget->viewport()->update(); +} + +bool QtTreePropertyBrowser::propertiesWithoutValueMarked() const +{ + return d_ptr->m_markPropertiesWithoutValue; +} + +/*! + \reimp +*/ +void QtTreePropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) +{ + d_ptr->propertyInserted(item, afterItem); +} + +/*! + \reimp +*/ +void QtTreePropertyBrowser::itemRemoved(QtBrowserItem *item) +{ + d_ptr->propertyRemoved(item); +} + +/*! + \reimp +*/ +void QtTreePropertyBrowser::itemChanged(QtBrowserItem *item) +{ + d_ptr->propertyChanged(item); +} + +/*! + Sets the current item to \a item and opens the relevant editor for it. +*/ +void QtTreePropertyBrowser::editItem(QtBrowserItem *item) +{ + d_ptr->editItem(item); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qttreepropertybrowser.cxx" +#include "qttreepropertybrowser.moc" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qttreepropertybrowser.h b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qttreepropertybrowser.h new file mode 100644 index 000000000..f664609d7 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qttreepropertybrowser.h @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTTREEPROPERTYBROWSER_H +#define QTTREEPROPERTYBROWSER_H + +#include "qtpropertybrowser.h" + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QTreeWidgetItem; +class QtTreePropertyBrowserPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtTreePropertyBrowser : public QtAbstractPropertyBrowser +{ + Q_OBJECT + Q_ENUMS(ResizeMode) + Q_PROPERTY(int indentation READ indentation WRITE setIndentation) + Q_PROPERTY(bool rootIsDecorated READ rootIsDecorated WRITE setRootIsDecorated) + Q_PROPERTY(bool alternatingRowColors READ alternatingRowColors WRITE setAlternatingRowColors) + Q_PROPERTY(bool headerVisible READ isHeaderVisible WRITE setHeaderVisible) + Q_PROPERTY(ResizeMode resizeMode READ resizeMode WRITE setResizeMode) + Q_PROPERTY(int splitterPosition READ splitterPosition WRITE setSplitterPosition) + Q_PROPERTY(bool propertiesWithoutValueMarked READ propertiesWithoutValueMarked WRITE setPropertiesWithoutValueMarked) +public: + + enum ResizeMode + { + Interactive, + Stretch, + Fixed, + ResizeToContents + }; + + QtTreePropertyBrowser(QWidget *parent = 0); + ~QtTreePropertyBrowser(); + + int indentation() const; + void setIndentation(int i); + + bool rootIsDecorated() const; + void setRootIsDecorated(bool show); + + bool alternatingRowColors() const; + void setAlternatingRowColors(bool enable); + + bool isHeaderVisible() const; + void setHeaderVisible(bool visible); + + ResizeMode resizeMode() const; + void setResizeMode(ResizeMode mode); + + int splitterPosition() const; + void setSplitterPosition(int position); + + void setExpanded(QtBrowserItem *item, bool expanded); + bool isExpanded(QtBrowserItem *item) const; + + bool isItemVisible(QtBrowserItem *item) const; + void setItemVisible(QtBrowserItem *item, bool visible); + + void setBackgroundColor(QtBrowserItem *item, const QColor &color); + QColor backgroundColor(QtBrowserItem *item) const; + QColor calculatedBackgroundColor(QtBrowserItem *item) const; + + void setPropertiesWithoutValueMarked(bool mark); + bool propertiesWithoutValueMarked() const; + + void editItem(QtBrowserItem *item); + +Q_SIGNALS: + + void collapsed(QtBrowserItem *item); + void expanded(QtBrowserItem *item); + +protected: + virtual void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem); + virtual void itemRemoved(QtBrowserItem *item); + virtual void itemChanged(QtBrowserItem *item); + +private: + + QtTreePropertyBrowserPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtTreePropertyBrowser) + Q_DISABLE_COPY(QtTreePropertyBrowser) + + Q_PRIVATE_SLOT(d_func(), void slotCollapsed(const QModelIndex &)) + Q_PRIVATE_SLOT(d_func(), void slotExpanded(const QModelIndex &)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentBrowserItemChanged(QtBrowserItem *)) + Q_PRIVATE_SLOT(d_func(), void slotCurrentTreeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)) + +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtvariantproperty.cpp b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtvariantproperty.cpp new file mode 100644 index 000000000..820e6e2f1 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtvariantproperty.cpp @@ -0,0 +1,2329 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qtvariantproperty.h" +#include "qtpropertymanager.h" +#include "qteditorfactory.h" +#include +#include +#include +#include + +#if defined(Q_CC_MSVC) +# pragma warning(disable: 4786) /* MS VS 6: truncating debug info after 255 characters */ +#endif + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +class QtEnumPropertyType +{ +}; + + +class QtFlagPropertyType +{ +}; + + +class QtGroupPropertyType +{ +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +Q_DECLARE_METATYPE(QtEnumPropertyType) +Q_DECLARE_METATYPE(QtFlagPropertyType) +Q_DECLARE_METATYPE(QtGroupPropertyType) + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +/*! + Returns the type id for an enum property. + + Note that the property's value type can be retrieved using the + valueType() function (which is QVariant::Int for the enum property + type). + + \sa propertyType(), valueType() +*/ +int QtVariantPropertyManager::enumTypeId() +{ + return qMetaTypeId(); +} + +/*! + Returns the type id for a flag property. + + Note that the property's value type can be retrieved using the + valueType() function (which is QVariant::Int for the flag property + type). + + \sa propertyType(), valueType() +*/ +int QtVariantPropertyManager::flagTypeId() +{ + return qMetaTypeId(); +} + +/*! + Returns the type id for a group property. + + Note that the property's value type can be retrieved using the + valueType() function (which is QVariant::Invalid for the group + property type, since it doesn't provide any value). + + \sa propertyType(), valueType() +*/ +int QtVariantPropertyManager::groupTypeId() +{ + return qMetaTypeId(); +} + +/*! + Returns the type id for a icon map attribute. + + Note that the property's attribute type can be retrieved using the + attributeType() function. + + \sa attributeType(), QtEnumPropertyManager::enumIcons() +*/ +int QtVariantPropertyManager::iconMapTypeId() +{ + return qMetaTypeId(); +} + +typedef QMap PropertyMap; +Q_GLOBAL_STATIC(PropertyMap, propertyToWrappedProperty) + +static QtProperty *wrappedProperty(QtProperty *property) +{ + return propertyToWrappedProperty()->value(property, 0); +} + +class QtVariantPropertyPrivate +{ + QtVariantProperty *q_ptr; +public: + QtVariantPropertyPrivate(QtVariantPropertyManager *m) : manager(m) {} + + QtVariantPropertyManager *manager; +}; + +/*! + \class QtVariantProperty + + \brief The QtVariantProperty class is a convenience class handling + QVariant based properties. + + QtVariantProperty provides additional API: A property's type, + value type, attribute values and current value can easily be + retrieved using the propertyType(), valueType(), attributeValue() + and value() functions respectively. In addition, the attribute + values and the current value can be set using the corresponding + setValue() and setAttribute() functions. + + For example, instead of writing: + + \code + QtVariantPropertyManager *variantPropertyManager; + QtProperty *property; + + variantPropertyManager->setValue(property, 10); + \endcode + + you can write: + + \code + QtVariantPropertyManager *variantPropertyManager; + QtVariantProperty *property; + + property->setValue(10); + \endcode + + QtVariantProperty instances can only be created by the + QtVariantPropertyManager class. + + \sa QtProperty, QtVariantPropertyManager, QtVariantEditorFactory +*/ + +/*! + Creates a variant property using the given \a manager. + + Do not use this constructor to create variant property instances; + use the QtVariantPropertyManager::addProperty() function + instead. This constructor is used internally by the + QtVariantPropertyManager::createProperty() function. + + \sa QtVariantPropertyManager +*/ +QtVariantProperty::QtVariantProperty(QtVariantPropertyManager *manager) + : QtProperty(manager), d_ptr(new QtVariantPropertyPrivate(manager)) +{ + +} + +/*! + Destroys this property. + + \sa QtProperty::~QtProperty() +*/ +QtVariantProperty::~QtVariantProperty() +{ + delete d_ptr; +} + +/*! + Returns the property's current value. + + \sa valueType(), setValue() +*/ +QVariant QtVariantProperty::value() const +{ + return d_ptr->manager->value(this); +} + +/*! + Returns this property's value for the specified \a attribute. + + QtVariantPropertyManager provides a couple of related functions: + \l{QtVariantPropertyManager::attributes()}{attributes()} and + \l{QtVariantPropertyManager::attributeType()}{attributeType()}. + + \sa setAttribute() +*/ +QVariant QtVariantProperty::attributeValue(const QString &attribute) const +{ + return d_ptr->manager->attributeValue(this, attribute); +} + +/*! + Returns the type of this property's value. + + \sa propertyType() +*/ +int QtVariantProperty::valueType() const +{ + return d_ptr->manager->valueType(this); +} + +/*! + Returns this property's type. + + QtVariantPropertyManager provides several related functions: + \l{QtVariantPropertyManager::enumTypeId()}{enumTypeId()}, + \l{QtVariantPropertyManager::flagTypeId()}{flagTypeId()} and + \l{QtVariantPropertyManager::groupTypeId()}{groupTypeId()}. + + \sa valueType() +*/ +int QtVariantProperty::propertyType() const +{ + return d_ptr->manager->propertyType(this); +} + +/*! + Sets the value of this property to \a value. + + The specified \a value must be of the type returned by + valueType(), or of a type that can be converted to valueType() + using the QVariant::canConvert() function; otherwise this function + does nothing. + + \sa value() +*/ +void QtVariantProperty::setValue(const QVariant &value) +{ + d_ptr->manager->setValue(this, value); +} + +/*! + Sets the \a attribute of property to \a value. + + QtVariantPropertyManager provides the related + \l{QtVariantPropertyManager::setAttribute()}{setAttribute()} + function. + + \sa attributeValue() +*/ +void QtVariantProperty::setAttribute(const QString &attribute, const QVariant &value) +{ + d_ptr->manager->setAttribute(this, attribute, value); +} + +class QtVariantPropertyManagerPrivate +{ + QtVariantPropertyManager *q_ptr; + Q_DECLARE_PUBLIC(QtVariantPropertyManager) +public: + QtVariantPropertyManagerPrivate(); + + bool m_creatingProperty; + bool m_creatingSubProperties; + bool m_destroyingSubProperties; + int m_propertyType; + + void slotValueChanged(QtProperty *property, int val); + void slotRangeChanged(QtProperty *property, int min, int max); + void slotSingleStepChanged(QtProperty *property, int step); + void slotValueChanged(QtProperty *property, double val); + void slotRangeChanged(QtProperty *property, double min, double max); + void slotSingleStepChanged(QtProperty *property, double step); + void slotDecimalsChanged(QtProperty *property, int prec); + void slotValueChanged(QtProperty *property, bool val); + void slotValueChanged(QtProperty *property, const QString &val); + void slotRegExpChanged(QtProperty *property, const QRegExp ®Exp); + void slotValueChanged(QtProperty *property, const QDate &val); + void slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max); + void slotValueChanged(QtProperty *property, const QTime &val); + void slotValueChanged(QtProperty *property, const QDateTime &val); + void slotValueChanged(QtProperty *property, const QKeySequence &val); + void slotValueChanged(QtProperty *property, const QChar &val); + void slotValueChanged(QtProperty *property, const QLocale &val); + void slotValueChanged(QtProperty *property, const QPoint &val); + void slotValueChanged(QtProperty *property, const QPointF &val); + void slotValueChanged(QtProperty *property, const QSize &val); + void slotRangeChanged(QtProperty *property, const QSize &min, const QSize &max); + void slotValueChanged(QtProperty *property, const QSizeF &val); + void slotRangeChanged(QtProperty *property, const QSizeF &min, const QSizeF &max); + void slotValueChanged(QtProperty *property, const QRect &val); + void slotConstraintChanged(QtProperty *property, const QRect &val); + void slotValueChanged(QtProperty *property, const QRectF &val); + void slotConstraintChanged(QtProperty *property, const QRectF &val); + void slotValueChanged(QtProperty *property, const QColor &val); + void slotEnumChanged(QtProperty *property, int val); + void slotEnumNamesChanged(QtProperty *property, const QStringList &enumNames); + void slotEnumIconsChanged(QtProperty *property, const QMap &enumIcons); + void slotValueChanged(QtProperty *property, const QSizePolicy &val); + void slotValueChanged(QtProperty *property, const QFont &val); + void slotValueChanged(QtProperty *property, const QCursor &val); + void slotFlagChanged(QtProperty *property, int val); + void slotFlagNamesChanged(QtProperty *property, const QStringList &flagNames); + void slotPropertyInserted(QtProperty *property, QtProperty *parent, QtProperty *after); + void slotPropertyRemoved(QtProperty *property, QtProperty *parent); + + void valueChanged(QtProperty *property, const QVariant &val); + + int internalPropertyToType(QtProperty *property) const; + QtVariantProperty *createSubProperty(QtVariantProperty *parent, QtVariantProperty *after, + QtProperty *internal); + void removeSubProperty(QtVariantProperty *property); + + QMap m_typeToPropertyManager; + QMap > m_typeToAttributeToAttributeType; + + QMap > m_propertyToType; + + QMap m_typeToValueType; + + + QMap m_internalToProperty; + + const QString m_constraintAttribute; + const QString m_singleStepAttribute; + const QString m_decimalsAttribute; + const QString m_enumIconsAttribute; + const QString m_enumNamesAttribute; + const QString m_flagNamesAttribute; + const QString m_maximumAttribute; + const QString m_minimumAttribute; + const QString m_regExpAttribute; +}; + +QtVariantPropertyManagerPrivate::QtVariantPropertyManagerPrivate() : + m_constraintAttribute(QLatin1String("constraint")), + m_singleStepAttribute(QLatin1String("singleStep")), + m_decimalsAttribute(QLatin1String("decimals")), + m_enumIconsAttribute(QLatin1String("enumIcons")), + m_enumNamesAttribute(QLatin1String("enumNames")), + m_flagNamesAttribute(QLatin1String("flagNames")), + m_maximumAttribute(QLatin1String("maximum")), + m_minimumAttribute(QLatin1String("minimum")), + m_regExpAttribute(QLatin1String("regExp")) +{ +} + +int QtVariantPropertyManagerPrivate::internalPropertyToType(QtProperty *property) const +{ + int type = 0; + QtAbstractPropertyManager *internPropertyManager = property->propertyManager(); + if (qobject_cast(internPropertyManager)) + type = QVariant::Int; + else if (qobject_cast(internPropertyManager)) + type = QtVariantPropertyManager::enumTypeId(); + else if (qobject_cast(internPropertyManager)) + type = QVariant::Bool; + else if (qobject_cast(internPropertyManager)) + type = QVariant::Double; + return type; +} + +QtVariantProperty *QtVariantPropertyManagerPrivate::createSubProperty(QtVariantProperty *parent, + QtVariantProperty *after, QtProperty *internal) +{ + int type = internalPropertyToType(internal); + if (!type) + return 0; + + bool wasCreatingSubProperties = m_creatingSubProperties; + m_creatingSubProperties = true; + + QtVariantProperty *varChild = q_ptr->addProperty(type, internal->propertyName()); + + m_creatingSubProperties = wasCreatingSubProperties; + + varChild->setPropertyName(internal->propertyName()); + varChild->setToolTip(internal->toolTip()); + varChild->setStatusTip(internal->statusTip()); + varChild->setWhatsThis(internal->whatsThis()); + + parent->insertSubProperty(varChild, after); + + m_internalToProperty[internal] = varChild; + propertyToWrappedProperty()->insert(varChild, internal); + return varChild; +} + +void QtVariantPropertyManagerPrivate::removeSubProperty(QtVariantProperty *property) +{ + QtProperty *internChild = wrappedProperty(property); + bool wasDestroyingSubProperties = m_destroyingSubProperties; + m_destroyingSubProperties = true; + delete property; + m_destroyingSubProperties = wasDestroyingSubProperties; + m_internalToProperty.remove(internChild); + propertyToWrappedProperty()->remove(property); +} + +void QtVariantPropertyManagerPrivate::slotPropertyInserted(QtProperty *property, + QtProperty *parent, QtProperty *after) +{ + if (m_creatingProperty) + return; + + QtVariantProperty *varParent = m_internalToProperty.value(parent, 0); + if (!varParent) + return; + + QtVariantProperty *varAfter = 0; + if (after) { + varAfter = m_internalToProperty.value(after, 0); + if (!varAfter) + return; + } + + createSubProperty(varParent, varAfter, property); +} + +void QtVariantPropertyManagerPrivate::slotPropertyRemoved(QtProperty *property, QtProperty *parent) +{ + Q_UNUSED(parent) + + QtVariantProperty *varProperty = m_internalToProperty.value(property, 0); + if (!varProperty) + return; + + removeSubProperty(varProperty); +} + +void QtVariantPropertyManagerPrivate::valueChanged(QtProperty *property, const QVariant &val) +{ + QtVariantProperty *varProp = m_internalToProperty.value(property, 0); + if (!varProp) + return; + emit q_ptr->valueChanged(varProp, val); + emit q_ptr->propertyChanged(varProp); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, int val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, int min, int max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotSingleStepChanged(QtProperty *property, int step) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_singleStepAttribute, QVariant(step)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, double val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, double min, double max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotSingleStepChanged(QtProperty *property, double step) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_singleStepAttribute, QVariant(step)); +} + +void QtVariantPropertyManagerPrivate::slotDecimalsChanged(QtProperty *property, int prec) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_decimalsAttribute, QVariant(prec)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, bool val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QString &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRegExpChanged(QtProperty *property, const QRegExp ®Exp) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_regExpAttribute, QVariant(regExp)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QDate &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QDate &min, const QDate &max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QTime &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QDateTime &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QKeySequence &val) +{ + QVariant v; + qVariantSetValue(v, val); + valueChanged(property, v); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QChar &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QLocale &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QPoint &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QPointF &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSize &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QSize &min, const QSize &max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSizeF &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotRangeChanged(QtProperty *property, const QSizeF &min, const QSizeF &max) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + emit q_ptr->attributeChanged(varProp, m_minimumAttribute, QVariant(min)); + emit q_ptr->attributeChanged(varProp, m_maximumAttribute, QVariant(max)); + } +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QRect &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotConstraintChanged(QtProperty *property, const QRect &constraint) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_constraintAttribute, QVariant(constraint)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QRectF &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotConstraintChanged(QtProperty *property, const QRectF &constraint) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_constraintAttribute, QVariant(constraint)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QColor &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotEnumNamesChanged(QtProperty *property, const QStringList &enumNames) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_enumNamesAttribute, QVariant(enumNames)); +} + +void QtVariantPropertyManagerPrivate::slotEnumIconsChanged(QtProperty *property, const QMap &enumIcons) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) { + QVariant v; + qVariantSetValue(v, enumIcons); + emit q_ptr->attributeChanged(varProp, m_enumIconsAttribute, v); + } +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QSizePolicy &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QFont &val) +{ + valueChanged(property, QVariant(val)); +} + +void QtVariantPropertyManagerPrivate::slotValueChanged(QtProperty *property, const QCursor &val) +{ +#ifndef QT_NO_CURSOR + valueChanged(property, QVariant(val)); +#endif +} + +void QtVariantPropertyManagerPrivate::slotFlagNamesChanged(QtProperty *property, const QStringList &flagNames) +{ + if (QtVariantProperty *varProp = m_internalToProperty.value(property, 0)) + emit q_ptr->attributeChanged(varProp, m_flagNamesAttribute, QVariant(flagNames)); +} + +/*! + \class QtVariantPropertyManager + + \brief The QtVariantPropertyManager class provides and manages QVariant based properties. + + QtVariantPropertyManager provides the addProperty() function which + creates QtVariantProperty objects. The QtVariantProperty class is + a convenience class handling QVariant based properties inheriting + QtProperty. A QtProperty object created by a + QtVariantPropertyManager instance can be converted into a + QtVariantProperty object using the variantProperty() function. + + The property's value can be retrieved using the value(), and set + using the setValue() slot. In addition the property's type, and + the type of its value, can be retrieved using the propertyType() + and valueType() functions respectively. + + A property's type is a QVariant::Type enumerator value, and + usually a property's type is the same as its value type. But for + some properties the types differ, for example for enums, flags and + group types in which case QtVariantPropertyManager provides the + enumTypeId(), flagTypeId() and groupTypeId() functions, + respectively, to identify their property type (the value types are + QVariant::Int for the enum and flag types, and QVariant::Invalid + for the group type). + + Use the isPropertyTypeSupported() function to check if a particular + property type is supported. The currently supported property types + are: + + \table + \header + \o Property Type + \o Property Type Id + \row + \o int + \o QVariant::Int + \row + \o double + \o QVariant::Double + \row + \o bool + \o QVariant::Bool + \row + \o QString + \o QVariant::String + \row + \o QDate + \o QVariant::Date + \row + \o QTime + \o QVariant::Time + \row + \o QDateTime + \o QVariant::DateTime + \row + \o QKeySequence + \o QVariant::KeySequence + \row + \o QChar + \o QVariant::Char + \row + \o QLocale + \o QVariant::Locale + \row + \o QPoint + \o QVariant::Point + \row + \o QPointF + \o QVariant::PointF + \row + \o QSize + \o QVariant::Size + \row + \o QSizeF + \o QVariant::SizeF + \row + \o QRect + \o QVariant::Rect + \row + \o QRectF + \o QVariant::RectF + \row + \o QColor + \o QVariant::Color + \row + \o QSizePolicy + \o QVariant::SizePolicy + \row + \o QFont + \o QVariant::Font + \row + \o QCursor + \o QVariant::Cursor + \row + \o enum + \o enumTypeId() + \row + \o flag + \o flagTypeId() + \row + \o group + \o groupTypeId() + \endtable + + Each property type can provide additional attributes, + e.g. QVariant::Int and QVariant::Double provides minimum and + maximum values. The currently supported attributes are: + + \table + \header + \o Property Type + \o Attribute Name + \o Attribute Type + \row + \o \c int + \o minimum + \o QVariant::Int + \row + \o + \o maximum + \o QVariant::Int + \row + \o + \o singleStep + \o QVariant::Int + \row + \o \c double + \o minimum + \o QVariant::Double + \row + \o + \o maximum + \o QVariant::Double + \row + \o + \o singleStep + \o QVariant::Double + \row + \o + \o decimals + \o QVariant::Int + \row + \o QString + \o regExp + \o QVariant::RegExp + \row + \o QDate + \o minimum + \o QVariant::Date + \row + \o + \o maximum + \o QVariant::Date + \row + \o QPointF + \o decimals + \o QVariant::Int + \row + \o QSize + \o minimum + \o QVariant::Size + \row + \o + \o maximum + \o QVariant::Size + \row + \o QSizeF + \o minimum + \o QVariant::SizeF + \row + \o + \o maximum + \o QVariant::SizeF + \row + \o + \o decimals + \o QVariant::Int + \row + \o QRect + \o constraint + \o QVariant::Rect + \row + \o QRectF + \o constraint + \o QVariant::RectF + \row + \o + \o decimals + \o QVariant::Int + \row + \o \c enum + \o enumNames + \o QVariant::StringList + \row + \o + \o enumIcons + \o iconMapTypeId() + \row + \o \c flag + \o flagNames + \o QVariant::StringList + \endtable + + The attributes for a given property type can be retrieved using + the attributes() function. Each attribute has a value type which + can be retrieved using the attributeType() function, and a value + accessible through the attributeValue() function. In addition, the + value can be set using the setAttribute() slot. + + QtVariantManager also provides the valueChanged() signal which is + emitted whenever a property created by this manager change, and + the attributeChanged() signal which is emitted whenever an + attribute of such a property changes. + + \sa QtVariantProperty, QtVariantEditorFactory +*/ + +/*! + \fn void QtVariantPropertyManager::valueChanged(QtProperty *property, const QVariant &value) + + This signal is emitted whenever a property created by this manager + changes its value, passing a pointer to the \a property and the + new \a value as parameters. + + \sa setValue() +*/ + +/*! + \fn void QtVariantPropertyManager::attributeChanged(QtProperty *property, + const QString &attribute, const QVariant &value) + + This signal is emitted whenever an attribute of a property created + by this manager changes its value, passing a pointer to the \a + property, the \a attribute and the new \a value as parameters. + + \sa setAttribute() +*/ + +/*! + Creates a manager with the given \a parent. +*/ +QtVariantPropertyManager::QtVariantPropertyManager(QObject *parent) + : QtAbstractPropertyManager(parent) +{ + d_ptr = new QtVariantPropertyManagerPrivate; + d_ptr->q_ptr = this; + + d_ptr->m_creatingProperty = false; + d_ptr->m_creatingSubProperties = false; + d_ptr->m_destroyingSubProperties = false; + d_ptr->m_propertyType = 0; + + // IntPropertyManager + QtIntPropertyManager *intPropertyManager = new QtIntPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Int] = intPropertyManager; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_minimumAttribute] = QVariant::Int; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_maximumAttribute] = QVariant::Int; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Int][d_ptr->m_singleStepAttribute] = QVariant::Int; + d_ptr->m_typeToValueType[QVariant::Int] = QVariant::Int; + connect(intPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(intPropertyManager, SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(intPropertyManager, SIGNAL(singleStepChanged(QtProperty *, int)), + this, SLOT(slotSingleStepChanged(QtProperty *, int))); + // DoublePropertyManager + QtDoublePropertyManager *doublePropertyManager = new QtDoublePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Double] = doublePropertyManager; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_minimumAttribute] = + QVariant::Double; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_maximumAttribute] = + QVariant::Double; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_singleStepAttribute] = + QVariant::Double; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Double][d_ptr->m_decimalsAttribute] = + QVariant::Int; + d_ptr->m_typeToValueType[QVariant::Double] = QVariant::Double; + connect(doublePropertyManager, SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotValueChanged(QtProperty *, double))); + connect(doublePropertyManager, SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + connect(doublePropertyManager, SIGNAL(singleStepChanged(QtProperty *, double)), + this, SLOT(slotSingleStepChanged(QtProperty *, double))); + connect(doublePropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); + // BoolPropertyManager + QtBoolPropertyManager *boolPropertyManager = new QtBoolPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Bool] = boolPropertyManager; + d_ptr->m_typeToValueType[QVariant::Bool] = QVariant::Bool; + connect(boolPropertyManager, SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotValueChanged(QtProperty *, bool))); + // StringPropertyManager + QtStringPropertyManager *stringPropertyManager = new QtStringPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::String] = stringPropertyManager; + d_ptr->m_typeToValueType[QVariant::String] = QVariant::String; + d_ptr->m_typeToAttributeToAttributeType[QVariant::String][d_ptr->m_regExpAttribute] = + QVariant::RegExp; + connect(stringPropertyManager, SIGNAL(valueChanged(QtProperty *, const QString &)), + this, SLOT(slotValueChanged(QtProperty *, const QString &))); + connect(stringPropertyManager, SIGNAL(regExpChanged(QtProperty *, const QRegExp &)), + this, SLOT(slotRegExpChanged(QtProperty *, const QRegExp &))); + // DatePropertyManager + QtDatePropertyManager *datePropertyManager = new QtDatePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Date] = datePropertyManager; + d_ptr->m_typeToValueType[QVariant::Date] = QVariant::Date; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Date][d_ptr->m_minimumAttribute] = + QVariant::Date; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Date][d_ptr->m_maximumAttribute] = + QVariant::Date; + connect(datePropertyManager, SIGNAL(valueChanged(QtProperty *, const QDate &)), + this, SLOT(slotValueChanged(QtProperty *, const QDate &))); + connect(datePropertyManager, SIGNAL(rangeChanged(QtProperty *, const QDate &, const QDate &)), + this, SLOT(slotRangeChanged(QtProperty *, const QDate &, const QDate &))); + // TimePropertyManager + QtTimePropertyManager *timePropertyManager = new QtTimePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Time] = timePropertyManager; + d_ptr->m_typeToValueType[QVariant::Time] = QVariant::Time; + connect(timePropertyManager, SIGNAL(valueChanged(QtProperty *, const QTime &)), + this, SLOT(slotValueChanged(QtProperty *, const QTime &))); + // DateTimePropertyManager + QtDateTimePropertyManager *dateTimePropertyManager = new QtDateTimePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::DateTime] = dateTimePropertyManager; + d_ptr->m_typeToValueType[QVariant::DateTime] = QVariant::DateTime; + connect(dateTimePropertyManager, SIGNAL(valueChanged(QtProperty *, const QDateTime &)), + this, SLOT(slotValueChanged(QtProperty *, const QDateTime &))); + // KeySequencePropertyManager + QtKeySequencePropertyManager *keySequencePropertyManager = new QtKeySequencePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::KeySequence] = keySequencePropertyManager; + d_ptr->m_typeToValueType[QVariant::KeySequence] = QVariant::KeySequence; + connect(keySequencePropertyManager, SIGNAL(valueChanged(QtProperty *, const QKeySequence &)), + this, SLOT(slotValueChanged(QtProperty *, const QKeySequence &))); + // CharPropertyManager + QtCharPropertyManager *charPropertyManager = new QtCharPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Char] = charPropertyManager; + d_ptr->m_typeToValueType[QVariant::Char] = QVariant::Char; + connect(charPropertyManager, SIGNAL(valueChanged(QtProperty *, const QChar &)), + this, SLOT(slotValueChanged(QtProperty *, const QChar &))); + // LocalePropertyManager + QtLocalePropertyManager *localePropertyManager = new QtLocalePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Locale] = localePropertyManager; + d_ptr->m_typeToValueType[QVariant::Locale] = QVariant::Locale; + connect(localePropertyManager, SIGNAL(valueChanged(QtProperty *, const QLocale &)), + this, SLOT(slotValueChanged(QtProperty *, const QLocale &))); + connect(localePropertyManager->subEnumPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(localePropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(localePropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // PointPropertyManager + QtPointPropertyManager *pointPropertyManager = new QtPointPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Point] = pointPropertyManager; + d_ptr->m_typeToValueType[QVariant::Point] = QVariant::Point; + connect(pointPropertyManager, SIGNAL(valueChanged(QtProperty *, const QPoint &)), + this, SLOT(slotValueChanged(QtProperty *, const QPoint &))); + connect(pointPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(pointPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(pointPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // PointFPropertyManager + QtPointFPropertyManager *pointFPropertyManager = new QtPointFPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::PointF] = pointFPropertyManager; + d_ptr->m_typeToValueType[QVariant::PointF] = QVariant::PointF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::PointF][d_ptr->m_decimalsAttribute] = + QVariant::Int; + connect(pointFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QPointF &)), + this, SLOT(slotValueChanged(QtProperty *, const QPointF &))); + connect(pointFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); + connect(pointFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotValueChanged(QtProperty *, double))); + connect(pointFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(pointFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // SizePropertyManager + QtSizePropertyManager *sizePropertyManager = new QtSizePropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Size] = sizePropertyManager; + d_ptr->m_typeToValueType[QVariant::Size] = QVariant::Size; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Size][d_ptr->m_minimumAttribute] = + QVariant::Size; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Size][d_ptr->m_maximumAttribute] = + QVariant::Size; + connect(sizePropertyManager, SIGNAL(valueChanged(QtProperty *, const QSize &)), + this, SLOT(slotValueChanged(QtProperty *, const QSize &))); + connect(sizePropertyManager, SIGNAL(rangeChanged(QtProperty *, const QSize &, const QSize &)), + this, SLOT(slotRangeChanged(QtProperty *, const QSize &, const QSize &))); + connect(sizePropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(sizePropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(sizePropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(sizePropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // SizeFPropertyManager + QtSizeFPropertyManager *sizeFPropertyManager = new QtSizeFPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::SizeF] = sizeFPropertyManager; + d_ptr->m_typeToValueType[QVariant::SizeF] = QVariant::SizeF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_minimumAttribute] = + QVariant::SizeF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_maximumAttribute] = + QVariant::SizeF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::SizeF][d_ptr->m_decimalsAttribute] = + QVariant::Int; + connect(sizeFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QSizeF &)), + this, SLOT(slotValueChanged(QtProperty *, const QSizeF &))); + connect(sizeFPropertyManager, SIGNAL(rangeChanged(QtProperty *, const QSizeF &, const QSizeF &)), + this, SLOT(slotRangeChanged(QtProperty *, const QSizeF &, const QSizeF &))); + connect(sizeFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); + connect(sizeFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotValueChanged(QtProperty *, double))); + connect(sizeFPropertyManager->subDoublePropertyManager(), SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + connect(sizeFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(sizeFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // RectPropertyManager + QtRectPropertyManager *rectPropertyManager = new QtRectPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Rect] = rectPropertyManager; + d_ptr->m_typeToValueType[QVariant::Rect] = QVariant::Rect; + d_ptr->m_typeToAttributeToAttributeType[QVariant::Rect][d_ptr->m_constraintAttribute] = + QVariant::Rect; + connect(rectPropertyManager, SIGNAL(valueChanged(QtProperty *, const QRect &)), + this, SLOT(slotValueChanged(QtProperty *, const QRect &))); + connect(rectPropertyManager, SIGNAL(constraintChanged(QtProperty *, const QRect &)), + this, SLOT(slotConstraintChanged(QtProperty *, const QRect &))); + connect(rectPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(rectPropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(rectPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(rectPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // RectFPropertyManager + QtRectFPropertyManager *rectFPropertyManager = new QtRectFPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::RectF] = rectFPropertyManager; + d_ptr->m_typeToValueType[QVariant::RectF] = QVariant::RectF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::RectF][d_ptr->m_constraintAttribute] = + QVariant::RectF; + d_ptr->m_typeToAttributeToAttributeType[QVariant::RectF][d_ptr->m_decimalsAttribute] = + QVariant::Int; + connect(rectFPropertyManager, SIGNAL(valueChanged(QtProperty *, const QRectF &)), + this, SLOT(slotValueChanged(QtProperty *, const QRectF &))); + connect(rectFPropertyManager, SIGNAL(constraintChanged(QtProperty *, const QRectF &)), + this, SLOT(slotConstraintChanged(QtProperty *, const QRectF &))); + connect(rectFPropertyManager, SIGNAL(decimalsChanged(QtProperty *, int)), + this, SLOT(slotDecimalsChanged(QtProperty *, int))); + connect(rectFPropertyManager->subDoublePropertyManager(), SIGNAL(valueChanged(QtProperty *, double)), + this, SLOT(slotValueChanged(QtProperty *, double))); + connect(rectFPropertyManager->subDoublePropertyManager(), SIGNAL(rangeChanged(QtProperty *, double, double)), + this, SLOT(slotRangeChanged(QtProperty *, double, double))); + connect(rectFPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(rectFPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // ColorPropertyManager + QtColorPropertyManager *colorPropertyManager = new QtColorPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Color] = colorPropertyManager; + d_ptr->m_typeToValueType[QVariant::Color] = QVariant::Color; + connect(colorPropertyManager, SIGNAL(valueChanged(QtProperty *, const QColor &)), + this, SLOT(slotValueChanged(QtProperty *, const QColor &))); + connect(colorPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(colorPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(colorPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // EnumPropertyManager + int enumId = enumTypeId(); + QtEnumPropertyManager *enumPropertyManager = new QtEnumPropertyManager(this); + d_ptr->m_typeToPropertyManager[enumId] = enumPropertyManager; + d_ptr->m_typeToValueType[enumId] = QVariant::Int; + d_ptr->m_typeToAttributeToAttributeType[enumId][d_ptr->m_enumNamesAttribute] = + QVariant::StringList; + d_ptr->m_typeToAttributeToAttributeType[enumId][d_ptr->m_enumIconsAttribute] = + iconMapTypeId(); + connect(enumPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(enumPropertyManager, SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); + connect(enumPropertyManager, SIGNAL(enumIconsChanged(QtProperty *, const QMap &)), + this, SLOT(slotEnumIconsChanged(QtProperty *, const QMap &))); + // SizePolicyPropertyManager + QtSizePolicyPropertyManager *sizePolicyPropertyManager = new QtSizePolicyPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::SizePolicy] = sizePolicyPropertyManager; + d_ptr->m_typeToValueType[QVariant::SizePolicy] = QVariant::SizePolicy; + connect(sizePolicyPropertyManager, SIGNAL(valueChanged(QtProperty *, const QSizePolicy &)), + this, SLOT(slotValueChanged(QtProperty *, const QSizePolicy &))); + connect(sizePolicyPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(sizePolicyPropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(sizePolicyPropertyManager->subEnumPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(sizePolicyPropertyManager->subEnumPropertyManager(), + SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); + connect(sizePolicyPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(sizePolicyPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // FontPropertyManager + QtFontPropertyManager *fontPropertyManager = new QtFontPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Font] = fontPropertyManager; + d_ptr->m_typeToValueType[QVariant::Font] = QVariant::Font; + connect(fontPropertyManager, SIGNAL(valueChanged(QtProperty *, const QFont &)), + this, SLOT(slotValueChanged(QtProperty *, const QFont &))); + connect(fontPropertyManager->subIntPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(fontPropertyManager->subIntPropertyManager(), SIGNAL(rangeChanged(QtProperty *, int, int)), + this, SLOT(slotRangeChanged(QtProperty *, int, int))); + connect(fontPropertyManager->subEnumPropertyManager(), SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(fontPropertyManager->subEnumPropertyManager(), + SIGNAL(enumNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotEnumNamesChanged(QtProperty *, const QStringList &))); + connect(fontPropertyManager->subBoolPropertyManager(), SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotValueChanged(QtProperty *, bool))); + connect(fontPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(fontPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // CursorPropertyManager + QtCursorPropertyManager *cursorPropertyManager = new QtCursorPropertyManager(this); + d_ptr->m_typeToPropertyManager[QVariant::Cursor] = cursorPropertyManager; + d_ptr->m_typeToValueType[QVariant::Cursor] = QVariant::Cursor; + connect(cursorPropertyManager, SIGNAL(valueChanged(QtProperty *, const QCursor &)), + this, SLOT(slotValueChanged(QtProperty *, const QCursor &))); + // FlagPropertyManager + int flagId = flagTypeId(); + QtFlagPropertyManager *flagPropertyManager = new QtFlagPropertyManager(this); + d_ptr->m_typeToPropertyManager[flagId] = flagPropertyManager; + d_ptr->m_typeToValueType[flagId] = QVariant::Int; + d_ptr->m_typeToAttributeToAttributeType[flagId][d_ptr->m_flagNamesAttribute] = + QVariant::StringList; + connect(flagPropertyManager, SIGNAL(valueChanged(QtProperty *, int)), + this, SLOT(slotValueChanged(QtProperty *, int))); + connect(flagPropertyManager, SIGNAL(flagNamesChanged(QtProperty *, const QStringList &)), + this, SLOT(slotFlagNamesChanged(QtProperty *, const QStringList &))); + connect(flagPropertyManager->subBoolPropertyManager(), SIGNAL(valueChanged(QtProperty *, bool)), + this, SLOT(slotValueChanged(QtProperty *, bool))); + connect(flagPropertyManager, SIGNAL(propertyInserted(QtProperty *, QtProperty *, QtProperty *)), + this, SLOT(slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *))); + connect(flagPropertyManager, SIGNAL(propertyRemoved(QtProperty *, QtProperty *)), + this, SLOT(slotPropertyRemoved(QtProperty *, QtProperty *))); + // FlagPropertyManager + int groupId = groupTypeId(); + QtGroupPropertyManager *groupPropertyManager = new QtGroupPropertyManager(this); + d_ptr->m_typeToPropertyManager[groupId] = groupPropertyManager; + d_ptr->m_typeToValueType[groupId] = QVariant::Invalid; +} + +/*! + Destroys this manager, and all the properties it has created. +*/ +QtVariantPropertyManager::~QtVariantPropertyManager() +{ + clear(); + delete d_ptr; +} + +/*! + Returns the given \a property converted into a QtVariantProperty. + + If the \a property was not created by this variant manager, the + function returns 0. + + \sa createProperty() +*/ +QtVariantProperty *QtVariantPropertyManager::variantProperty(const QtProperty *property) const +{ + const QMap >::const_iterator it = d_ptr->m_propertyToType.constFind(property); + if (it == d_ptr->m_propertyToType.constEnd()) + return 0; + return it.value().first; +} + +/*! + Returns true if the given \a propertyType is supported by this + variant manager; otherwise false. + + \sa propertyType() +*/ +bool QtVariantPropertyManager::isPropertyTypeSupported(int propertyType) const +{ + if (d_ptr->m_typeToValueType.contains(propertyType)) + return true; + return false; +} + +/*! + Creates and returns a variant property of the given \a propertyType + with the given \a name. + + If the specified \a propertyType is not supported by this variant + manager, this function returns 0. + + Do not use the inherited + QtAbstractPropertyManager::addProperty() function to create a + variant property (that function will always return 0 since it will + not be clear what type the property should have). + + \sa isPropertyTypeSupported() +*/ +QtVariantProperty *QtVariantPropertyManager::addProperty(int propertyType, const QString &name) +{ + if (!isPropertyTypeSupported(propertyType)) + return 0; + + bool wasCreating = d_ptr->m_creatingProperty; + d_ptr->m_creatingProperty = true; + d_ptr->m_propertyType = propertyType; + QtProperty *property = QtAbstractPropertyManager::addProperty(name); + d_ptr->m_creatingProperty = wasCreating; + d_ptr->m_propertyType = 0; + + if (!property) + return 0; + + return variantProperty(property); +} + +/*! + Returns the given \a property's value. + + If the given \a property is not managed by this manager, this + function returns an invalid variant. + + \sa setValue() +*/ +QVariant QtVariantPropertyManager::value(const QtProperty *property) const +{ + QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + if (internProp == 0) + return QVariant(); + + QtAbstractPropertyManager *manager = internProp->propertyManager(); + if (QtIntPropertyManager *intManager = qobject_cast(manager)) { + return intManager->value(internProp); + } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { + return doubleManager->value(internProp); + } else if (QtBoolPropertyManager *boolManager = qobject_cast(manager)) { + return boolManager->value(internProp); + } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { + return stringManager->value(internProp); + } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { + return dateManager->value(internProp); + } else if (QtTimePropertyManager *timeManager = qobject_cast(manager)) { + return timeManager->value(internProp); + } else if (QtDateTimePropertyManager *dateTimeManager = qobject_cast(manager)) { + return dateTimeManager->value(internProp); + } else if (QtKeySequencePropertyManager *keySequenceManager = qobject_cast(manager)) { + return keySequenceManager->value(internProp); + } else if (QtCharPropertyManager *charManager = qobject_cast(manager)) { + return charManager->value(internProp); + } else if (QtLocalePropertyManager *localeManager = qobject_cast(manager)) { + return localeManager->value(internProp); + } else if (QtPointPropertyManager *pointManager = qobject_cast(manager)) { + return pointManager->value(internProp); + } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { + return pointFManager->value(internProp); + } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { + return sizeManager->value(internProp); + } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { + return sizeFManager->value(internProp); + } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { + return rectManager->value(internProp); + } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { + return rectFManager->value(internProp); + } else if (QtColorPropertyManager *colorManager = qobject_cast(manager)) { + return colorManager->value(internProp); + } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { + return enumManager->value(internProp); + } else if (QtSizePolicyPropertyManager *sizePolicyManager = + qobject_cast(manager)) { + return sizePolicyManager->value(internProp); + } else if (QtFontPropertyManager *fontManager = qobject_cast(manager)) { + return fontManager->value(internProp); +#ifndef QT_NO_CURSOR + } else if (QtCursorPropertyManager *cursorManager = qobject_cast(manager)) { + return cursorManager->value(internProp); +#endif + } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { + return flagManager->value(internProp); + } + return QVariant(); +} + +/*! + Returns the given \a property's value type. + + \sa propertyType() +*/ +int QtVariantPropertyManager::valueType(const QtProperty *property) const +{ + int propType = propertyType(property); + return valueType(propType); +} + +/*! + \overload + + Returns the value type associated with the given \a propertyType. +*/ +int QtVariantPropertyManager::valueType(int propertyType) const +{ + if (d_ptr->m_typeToValueType.contains(propertyType)) + return d_ptr->m_typeToValueType[propertyType]; + return 0; +} + +/*! + Returns the given \a property's type. + + \sa valueType() +*/ +int QtVariantPropertyManager::propertyType(const QtProperty *property) const +{ + const QMap >::const_iterator it = d_ptr->m_propertyToType.constFind(property); + if (it == d_ptr->m_propertyToType.constEnd()) + return 0; + return it.value().second; +} + +/*! + Returns the given \a property's value for the specified \a + attribute + + If the given \a property was not created by \e this manager, or if + the specified \a attribute does not exist, this function returns + an invalid variant. + + \sa attributes(), attributeType(), setAttribute() +*/ +QVariant QtVariantPropertyManager::attributeValue(const QtProperty *property, const QString &attribute) const +{ + int propType = propertyType(property); + if (!propType) + return QVariant(); + + QMap >::ConstIterator it = + d_ptr->m_typeToAttributeToAttributeType.find(propType); + if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) + return QVariant(); + + QMap attributes = it.value(); + QMap::ConstIterator itAttr = attributes.find(attribute); + if (itAttr == attributes.constEnd()) + return QVariant(); + + QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + if (internProp == 0) + return QVariant(); + + QtAbstractPropertyManager *manager = internProp->propertyManager(); + if (QtIntPropertyManager *intManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return intManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return intManager->minimum(internProp); + if (attribute == d_ptr->m_singleStepAttribute) + return intManager->singleStep(internProp); + return QVariant(); + } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return doubleManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return doubleManager->minimum(internProp); + if (attribute == d_ptr->m_singleStepAttribute) + return doubleManager->singleStep(internProp); + if (attribute == d_ptr->m_decimalsAttribute) + return doubleManager->decimals(internProp); + return QVariant(); + } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_regExpAttribute) + return stringManager->regExp(internProp); + return QVariant(); + } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return dateManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return dateManager->minimum(internProp); + return QVariant(); + } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_decimalsAttribute) + return pointFManager->decimals(internProp); + return QVariant(); + } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return sizeManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return sizeManager->minimum(internProp); + return QVariant(); + } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + return sizeFManager->maximum(internProp); + if (attribute == d_ptr->m_minimumAttribute) + return sizeFManager->minimum(internProp); + if (attribute == d_ptr->m_decimalsAttribute) + return sizeFManager->decimals(internProp); + return QVariant(); + } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_constraintAttribute) + return rectManager->constraint(internProp); + return QVariant(); + } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_constraintAttribute) + return rectFManager->constraint(internProp); + if (attribute == d_ptr->m_decimalsAttribute) + return rectFManager->decimals(internProp); + return QVariant(); + } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_enumNamesAttribute) + return enumManager->enumNames(internProp); + if (attribute == d_ptr->m_enumIconsAttribute) { + QVariant v; + qVariantSetValue(v, enumManager->enumIcons(internProp)); + return v; + } + return QVariant(); + } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_flagNamesAttribute) + return flagManager->flagNames(internProp); + return QVariant(); + } + return QVariant(); +} + +/*! + Returns a list of the given \a propertyType 's attributes. + + \sa attributeValue(), attributeType() +*/ +QStringList QtVariantPropertyManager::attributes(int propertyType) const +{ + QMap >::ConstIterator it = + d_ptr->m_typeToAttributeToAttributeType.find(propertyType); + if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) + return QStringList(); + return it.value().keys(); +} + +/*! + Returns the type of the specified \a attribute of the given \a + propertyType. + + If the given \a propertyType is not supported by \e this manager, + or if the given \a propertyType does not possess the specified \a + attribute, this function returns QVariant::Invalid. + + \sa attributes(), valueType() +*/ +int QtVariantPropertyManager::attributeType(int propertyType, const QString &attribute) const +{ + QMap >::ConstIterator it = + d_ptr->m_typeToAttributeToAttributeType.find(propertyType); + if (it == d_ptr->m_typeToAttributeToAttributeType.constEnd()) + return 0; + + QMap attributes = it.value(); + QMap::ConstIterator itAttr = attributes.find(attribute); + if (itAttr == attributes.constEnd()) + return 0; + return itAttr.value(); +} + +/*! + \fn void QtVariantPropertyManager::setValue(QtProperty *property, const QVariant &value) + + Sets the value of the given \a property to \a value. + + The specified \a value must be of a type returned by valueType(), + or of type that can be converted to valueType() using the + QVariant::canConvert() function, otherwise this function does + nothing. + + \sa value(), QtVariantProperty::setValue(), valueChanged() +*/ +void QtVariantPropertyManager::setValue(QtProperty *property, const QVariant &val) +{ + int propType = val.userType(); + if (!propType) + return; + + int valType = valueType(property); + + if (propType != valType && !val.canConvert(static_cast(valType))) + return; + + QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + if (internProp == 0) + return; + + + QtAbstractPropertyManager *manager = internProp->propertyManager(); + if (QtIntPropertyManager *intManager = qobject_cast(manager)) { + intManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { + doubleManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtBoolPropertyManager *boolManager = qobject_cast(manager)) { + boolManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { + stringManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { + dateManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtTimePropertyManager *timeManager = qobject_cast(manager)) { + timeManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtDateTimePropertyManager *dateTimeManager = qobject_cast(manager)) { + dateTimeManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtKeySequencePropertyManager *keySequenceManager = qobject_cast(manager)) { + keySequenceManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtCharPropertyManager *charManager = qobject_cast(manager)) { + charManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtLocalePropertyManager *localeManager = qobject_cast(manager)) { + localeManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtPointPropertyManager *pointManager = qobject_cast(manager)) { + pointManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { + pointFManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { + sizeManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { + sizeFManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { + rectManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { + rectFManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtColorPropertyManager *colorManager = qobject_cast(manager)) { + colorManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { + enumManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtSizePolicyPropertyManager *sizePolicyManager = + qobject_cast(manager)) { + sizePolicyManager->setValue(internProp, qVariantValue(val)); + return; + } else if (QtFontPropertyManager *fontManager = qobject_cast(manager)) { + fontManager->setValue(internProp, qVariantValue(val)); + return; +#ifndef QT_NO_CURSOR + } else if (QtCursorPropertyManager *cursorManager = qobject_cast(manager)) { + cursorManager->setValue(internProp, qVariantValue(val)); + return; +#endif + } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { + flagManager->setValue(internProp, qVariantValue(val)); + return; + } +} + +/*! + Sets the value of the specified \a attribute of the given \a + property, to \a value. + + The new \a value's type must be of the type returned by + attributeType(), or of a type that can be converted to + attributeType() using the QVariant::canConvert() function, + otherwise this function does nothing. + + \sa attributeValue(), QtVariantProperty::setAttribute(), attributeChanged() +*/ +void QtVariantPropertyManager::setAttribute(QtProperty *property, + const QString &attribute, const QVariant &value) +{ + QVariant oldAttr = attributeValue(property, attribute); + if (!oldAttr.isValid()) + return; + + int attrType = value.userType(); + if (!attrType) + return; + + if (attrType != attributeType(propertyType(property), attribute) && + !value.canConvert((QVariant::Type)attrType)) + return; + + QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + if (internProp == 0) + return; + + QtAbstractPropertyManager *manager = internProp->propertyManager(); + if (QtIntPropertyManager *intManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + intManager->setMaximum(internProp, qVariantValue(value)); + else if (attribute == d_ptr->m_minimumAttribute) + intManager->setMinimum(internProp, qVariantValue(value)); + else if (attribute == d_ptr->m_singleStepAttribute) + intManager->setSingleStep(internProp, qVariantValue(value)); + return; + } else if (QtDoublePropertyManager *doubleManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + doubleManager->setMaximum(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_minimumAttribute) + doubleManager->setMinimum(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_singleStepAttribute) + doubleManager->setSingleStep(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_decimalsAttribute) + doubleManager->setDecimals(internProp, qVariantValue(value)); + return; + } else if (QtStringPropertyManager *stringManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_regExpAttribute) + stringManager->setRegExp(internProp, qVariantValue(value)); + return; + } else if (QtDatePropertyManager *dateManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + dateManager->setMaximum(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_minimumAttribute) + dateManager->setMinimum(internProp, qVariantValue(value)); + return; + } else if (QtPointFPropertyManager *pointFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_decimalsAttribute) + pointFManager->setDecimals(internProp, qVariantValue(value)); + return; + } else if (QtSizePropertyManager *sizeManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + sizeManager->setMaximum(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_minimumAttribute) + sizeManager->setMinimum(internProp, qVariantValue(value)); + return; + } else if (QtSizeFPropertyManager *sizeFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_maximumAttribute) + sizeFManager->setMaximum(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_minimumAttribute) + sizeFManager->setMinimum(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_decimalsAttribute) + sizeFManager->setDecimals(internProp, qVariantValue(value)); + return; + } else if (QtRectPropertyManager *rectManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_constraintAttribute) + rectManager->setConstraint(internProp, qVariantValue(value)); + return; + } else if (QtRectFPropertyManager *rectFManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_constraintAttribute) + rectFManager->setConstraint(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_decimalsAttribute) + rectFManager->setDecimals(internProp, qVariantValue(value)); + return; + } else if (QtEnumPropertyManager *enumManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_enumNamesAttribute) + enumManager->setEnumNames(internProp, qVariantValue(value)); + if (attribute == d_ptr->m_enumIconsAttribute) + enumManager->setEnumIcons(internProp, qVariantValue(value)); + return; + } else if (QtFlagPropertyManager *flagManager = qobject_cast(manager)) { + if (attribute == d_ptr->m_flagNamesAttribute) + flagManager->setFlagNames(internProp, qVariantValue(value)); + return; + } +} + +/*! + \reimp +*/ +bool QtVariantPropertyManager::hasValue(const QtProperty *property) const +{ + if (propertyType(property) == groupTypeId()) + return false; + return true; +} + +/*! + \reimp +*/ +QString QtVariantPropertyManager::valueText(const QtProperty *property) const +{ + const QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + return internProp ? internProp->valueText() : QString(); +} + +/*! + \reimp +*/ +QIcon QtVariantPropertyManager::valueIcon(const QtProperty *property) const +{ + const QtProperty *internProp = propertyToWrappedProperty()->value(property, 0); + return internProp ? internProp->valueIcon() : QIcon(); +} + +/*! + \reimp +*/ +void QtVariantPropertyManager::initializeProperty(QtProperty *property) +{ + QtVariantProperty *varProp = variantProperty(property); + if (!varProp) + return; + + QMap::ConstIterator it = + d_ptr->m_typeToPropertyManager.find(d_ptr->m_propertyType); + if (it != d_ptr->m_typeToPropertyManager.constEnd()) { + QtProperty *internProp = 0; + if (!d_ptr->m_creatingSubProperties) { + QtAbstractPropertyManager *manager = it.value(); + internProp = manager->addProperty(); + d_ptr->m_internalToProperty[internProp] = varProp; + } + propertyToWrappedProperty()->insert(varProp, internProp); + if (internProp) { + QList children = internProp->subProperties(); + QListIterator itChild(children); + QtVariantProperty *lastProperty = 0; + while (itChild.hasNext()) { + QtVariantProperty *prop = d_ptr->createSubProperty(varProp, lastProperty, itChild.next()); + lastProperty = prop ? prop : lastProperty; + } + } + } +} + +/*! + \reimp +*/ +void QtVariantPropertyManager::uninitializeProperty(QtProperty *property) +{ + const QMap >::iterator type_it = d_ptr->m_propertyToType.find(property); + if (type_it == d_ptr->m_propertyToType.end()) + return; + + PropertyMap::iterator it = propertyToWrappedProperty()->find(property); + if (it != propertyToWrappedProperty()->end()) { + QtProperty *internProp = it.value(); + if (internProp) { + d_ptr->m_internalToProperty.remove(internProp); + if (!d_ptr->m_destroyingSubProperties) { + delete internProp; + } + } + propertyToWrappedProperty()->erase(it); + } + d_ptr->m_propertyToType.erase(type_it); +} + +/*! + \reimp +*/ +QtProperty *QtVariantPropertyManager::createProperty() +{ + if (!d_ptr->m_creatingProperty) + return 0; + + QtVariantProperty *property = new QtVariantProperty(this); + d_ptr->m_propertyToType.insert(property, qMakePair(property, d_ptr->m_propertyType)); + + return property; +} + +///////////////////////////// + +class QtVariantEditorFactoryPrivate +{ + QtVariantEditorFactory *q_ptr; + Q_DECLARE_PUBLIC(QtVariantEditorFactory) +public: + + QtSpinBoxFactory *m_spinBoxFactory; + QtDoubleSpinBoxFactory *m_doubleSpinBoxFactory; + QtCheckBoxFactory *m_checkBoxFactory; + QtLineEditFactory *m_lineEditFactory; + QtDateEditFactory *m_dateEditFactory; + QtTimeEditFactory *m_timeEditFactory; + QtDateTimeEditFactory *m_dateTimeEditFactory; + QtKeySequenceEditorFactory *m_keySequenceEditorFactory; + QtCharEditorFactory *m_charEditorFactory; + QtEnumEditorFactory *m_comboBoxFactory; + QtCursorEditorFactory *m_cursorEditorFactory; + QtColorEditorFactory *m_colorEditorFactory; + QtFontEditorFactory *m_fontEditorFactory; + + QMap m_factoryToType; + QMap m_typeToFactory; +}; + +/*! + \class QtVariantEditorFactory + + \brief The QtVariantEditorFactory class provides widgets for properties + created by QtVariantPropertyManager objects. + + The variant factory provides the following widgets for the + specified property types: + + \table + \header + \o Property Type + \o Widget + \row + \o \c int + \o QSpinBox + \row + \o \c double + \o QDoubleSpinBox + \row + \o \c bool + \o QCheckBox + \row + \o QString + \o QLineEdit + \row + \o QDate + \o QDateEdit + \row + \o QTime + \o QTimeEdit + \row + \o QDateTime + \o QDateTimeEdit + \row + \o QKeySequence + \o customized editor + \row + \o QChar + \o customized editor + \row + \o \c enum + \o QComboBox + \row + \o QCursor + \o QComboBox + \endtable + + Note that QtVariantPropertyManager supports several additional property + types for which the QtVariantEditorFactory class does not provide + editing widgets, e.g. QPoint and QSize. To provide widgets for other + types using the variant approach, derive from the QtVariantEditorFactory + class. + + \sa QtAbstractEditorFactory, QtVariantPropertyManager +*/ + +/*! + Creates a factory with the given \a parent. +*/ +QtVariantEditorFactory::QtVariantEditorFactory(QObject *parent) + : QtAbstractEditorFactory(parent) +{ + d_ptr = new QtVariantEditorFactoryPrivate(); + d_ptr->q_ptr = this; + + d_ptr->m_spinBoxFactory = new QtSpinBoxFactory(this); + d_ptr->m_factoryToType[d_ptr->m_spinBoxFactory] = QVariant::Int; + d_ptr->m_typeToFactory[QVariant::Int] = d_ptr->m_spinBoxFactory; + + d_ptr->m_doubleSpinBoxFactory = new QtDoubleSpinBoxFactory(this); + d_ptr->m_factoryToType[d_ptr->m_doubleSpinBoxFactory] = QVariant::Double; + d_ptr->m_typeToFactory[QVariant::Double] = d_ptr->m_doubleSpinBoxFactory; + + d_ptr->m_checkBoxFactory = new QtCheckBoxFactory(this); + d_ptr->m_factoryToType[d_ptr->m_checkBoxFactory] = QVariant::Bool; + d_ptr->m_typeToFactory[QVariant::Bool] = d_ptr->m_checkBoxFactory; + + d_ptr->m_lineEditFactory = new QtLineEditFactory(this); + d_ptr->m_factoryToType[d_ptr->m_lineEditFactory] = QVariant::String; + d_ptr->m_typeToFactory[QVariant::String] = d_ptr->m_lineEditFactory; + + d_ptr->m_dateEditFactory = new QtDateEditFactory(this); + d_ptr->m_factoryToType[d_ptr->m_dateEditFactory] = QVariant::Date; + d_ptr->m_typeToFactory[QVariant::Date] = d_ptr->m_dateEditFactory; + + d_ptr->m_timeEditFactory = new QtTimeEditFactory(this); + d_ptr->m_factoryToType[d_ptr->m_timeEditFactory] = QVariant::Time; + d_ptr->m_typeToFactory[QVariant::Time] = d_ptr->m_timeEditFactory; + + d_ptr->m_dateTimeEditFactory = new QtDateTimeEditFactory(this); + d_ptr->m_factoryToType[d_ptr->m_dateTimeEditFactory] = QVariant::DateTime; + d_ptr->m_typeToFactory[QVariant::DateTime] = d_ptr->m_dateTimeEditFactory; + + d_ptr->m_keySequenceEditorFactory = new QtKeySequenceEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_keySequenceEditorFactory] = QVariant::KeySequence; + d_ptr->m_typeToFactory[QVariant::KeySequence] = d_ptr->m_keySequenceEditorFactory; + + d_ptr->m_charEditorFactory = new QtCharEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_charEditorFactory] = QVariant::Char; + d_ptr->m_typeToFactory[QVariant::Char] = d_ptr->m_charEditorFactory; + + d_ptr->m_cursorEditorFactory = new QtCursorEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_cursorEditorFactory] = QVariant::Cursor; + d_ptr->m_typeToFactory[QVariant::Cursor] = d_ptr->m_cursorEditorFactory; + + d_ptr->m_colorEditorFactory = new QtColorEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_colorEditorFactory] = QVariant::Color; + d_ptr->m_typeToFactory[QVariant::Color] = d_ptr->m_colorEditorFactory; + + d_ptr->m_fontEditorFactory = new QtFontEditorFactory(this); + d_ptr->m_factoryToType[d_ptr->m_fontEditorFactory] = QVariant::Font; + d_ptr->m_typeToFactory[QVariant::Font] = d_ptr->m_fontEditorFactory; + + d_ptr->m_comboBoxFactory = new QtEnumEditorFactory(this); + const int enumId = QtVariantPropertyManager::enumTypeId(); + d_ptr->m_factoryToType[d_ptr->m_comboBoxFactory] = enumId; + d_ptr->m_typeToFactory[enumId] = d_ptr->m_comboBoxFactory; +} + +/*! + Destroys this factory, and all the widgets it has created. +*/ +QtVariantEditorFactory::~QtVariantEditorFactory() +{ + delete d_ptr; +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtVariantEditorFactory::connectPropertyManager(QtVariantPropertyManager *manager) +{ + QList intPropertyManagers = qFindChildren(manager); + QListIterator itInt(intPropertyManagers); + while (itInt.hasNext()) + d_ptr->m_spinBoxFactory->addPropertyManager(itInt.next()); + + QList doublePropertyManagers = qFindChildren(manager); + QListIterator itDouble(doublePropertyManagers); + while (itDouble.hasNext()) + d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itDouble.next()); + + QList boolPropertyManagers = qFindChildren(manager); + QListIterator itBool(boolPropertyManagers); + while (itBool.hasNext()) + d_ptr->m_checkBoxFactory->addPropertyManager(itBool.next()); + + QList stringPropertyManagers = qFindChildren(manager); + QListIterator itString(stringPropertyManagers); + while (itString.hasNext()) + d_ptr->m_lineEditFactory->addPropertyManager(itString.next()); + + QList datePropertyManagers = qFindChildren(manager); + QListIterator itDate(datePropertyManagers); + while (itDate.hasNext()) + d_ptr->m_dateEditFactory->addPropertyManager(itDate.next()); + + QList timePropertyManagers = qFindChildren(manager); + QListIterator itTime(timePropertyManagers); + while (itTime.hasNext()) + d_ptr->m_timeEditFactory->addPropertyManager(itTime.next()); + + QList dateTimePropertyManagers = qFindChildren(manager); + QListIterator itDateTime(dateTimePropertyManagers); + while (itDateTime.hasNext()) + d_ptr->m_dateTimeEditFactory->addPropertyManager(itDateTime.next()); + + QList keySequencePropertyManagers = qFindChildren(manager); + QListIterator itKeySequence(keySequencePropertyManagers); + while (itKeySequence.hasNext()) + d_ptr->m_keySequenceEditorFactory->addPropertyManager(itKeySequence.next()); + + QList charPropertyManagers = qFindChildren(manager); + QListIterator itChar(charPropertyManagers); + while (itChar.hasNext()) + d_ptr->m_charEditorFactory->addPropertyManager(itChar.next()); + + QList localePropertyManagers = qFindChildren(manager); + QListIterator itLocale(localePropertyManagers); + while (itLocale.hasNext()) + d_ptr->m_comboBoxFactory->addPropertyManager(itLocale.next()->subEnumPropertyManager()); + + QList pointPropertyManagers = qFindChildren(manager); + QListIterator itPoint(pointPropertyManagers); + while (itPoint.hasNext()) + d_ptr->m_spinBoxFactory->addPropertyManager(itPoint.next()->subIntPropertyManager()); + + QList pointFPropertyManagers = qFindChildren(manager); + QListIterator itPointF(pointFPropertyManagers); + while (itPointF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itPointF.next()->subDoublePropertyManager()); + + QList sizePropertyManagers = qFindChildren(manager); + QListIterator itSize(sizePropertyManagers); + while (itSize.hasNext()) + d_ptr->m_spinBoxFactory->addPropertyManager(itSize.next()->subIntPropertyManager()); + + QList sizeFPropertyManagers = qFindChildren(manager); + QListIterator itSizeF(sizeFPropertyManagers); + while (itSizeF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itSizeF.next()->subDoublePropertyManager()); + + QList rectPropertyManagers = qFindChildren(manager); + QListIterator itRect(rectPropertyManagers); + while (itRect.hasNext()) + d_ptr->m_spinBoxFactory->addPropertyManager(itRect.next()->subIntPropertyManager()); + + QList rectFPropertyManagers = qFindChildren(manager); + QListIterator itRectF(rectFPropertyManagers); + while (itRectF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->addPropertyManager(itRectF.next()->subDoublePropertyManager()); + + QList colorPropertyManagers = qFindChildren(manager); + QListIterator itColor(colorPropertyManagers); + while (itColor.hasNext()) { + QtColorPropertyManager *manager = itColor.next(); + d_ptr->m_colorEditorFactory->addPropertyManager(manager); + d_ptr->m_spinBoxFactory->addPropertyManager(manager->subIntPropertyManager()); + } + + QList enumPropertyManagers = qFindChildren(manager); + QListIterator itEnum(enumPropertyManagers); + while (itEnum.hasNext()) + d_ptr->m_comboBoxFactory->addPropertyManager(itEnum.next()); + + QList sizePolicyPropertyManagers = qFindChildren(manager); + QListIterator itSizePolicy(sizePolicyPropertyManagers); + while (itSizePolicy.hasNext()) { + QtSizePolicyPropertyManager *manager = itSizePolicy.next(); + d_ptr->m_spinBoxFactory->addPropertyManager(manager->subIntPropertyManager()); + d_ptr->m_comboBoxFactory->addPropertyManager(manager->subEnumPropertyManager()); + } + + QList fontPropertyManagers = qFindChildren(manager); + QListIterator itFont(fontPropertyManagers); + while (itFont.hasNext()) { + QtFontPropertyManager *manager = itFont.next(); + d_ptr->m_fontEditorFactory->addPropertyManager(manager); + d_ptr->m_spinBoxFactory->addPropertyManager(manager->subIntPropertyManager()); + d_ptr->m_comboBoxFactory->addPropertyManager(manager->subEnumPropertyManager()); + d_ptr->m_checkBoxFactory->addPropertyManager(manager->subBoolPropertyManager()); + } + + QList cursorPropertyManagers = qFindChildren(manager); + QListIterator itCursor(cursorPropertyManagers); + while (itCursor.hasNext()) + d_ptr->m_cursorEditorFactory->addPropertyManager(itCursor.next()); + + QList flagPropertyManagers = qFindChildren(manager); + QListIterator itFlag(flagPropertyManagers); + while (itFlag.hasNext()) + d_ptr->m_checkBoxFactory->addPropertyManager(itFlag.next()->subBoolPropertyManager()); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +QWidget *QtVariantEditorFactory::createEditor(QtVariantPropertyManager *manager, QtProperty *property, + QWidget *parent) +{ + const int propType = manager->propertyType(property); + QtAbstractEditorFactoryBase *factory = d_ptr->m_typeToFactory.value(propType, 0); + if (!factory) + return 0; + return factory->createEditor(wrappedProperty(property), parent); +} + +/*! + \internal + + Reimplemented from the QtAbstractEditorFactory class. +*/ +void QtVariantEditorFactory::disconnectPropertyManager(QtVariantPropertyManager *manager) +{ + QList intPropertyManagers = qFindChildren(manager); + QListIterator itInt(intPropertyManagers); + while (itInt.hasNext()) + d_ptr->m_spinBoxFactory->removePropertyManager(itInt.next()); + + QList doublePropertyManagers = qFindChildren(manager); + QListIterator itDouble(doublePropertyManagers); + while (itDouble.hasNext()) + d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itDouble.next()); + + QList boolPropertyManagers = qFindChildren(manager); + QListIterator itBool(boolPropertyManagers); + while (itBool.hasNext()) + d_ptr->m_checkBoxFactory->removePropertyManager(itBool.next()); + + QList stringPropertyManagers = qFindChildren(manager); + QListIterator itString(stringPropertyManagers); + while (itString.hasNext()) + d_ptr->m_lineEditFactory->removePropertyManager(itString.next()); + + QList datePropertyManagers = qFindChildren(manager); + QListIterator itDate(datePropertyManagers); + while (itDate.hasNext()) + d_ptr->m_dateEditFactory->removePropertyManager(itDate.next()); + + QList timePropertyManagers = qFindChildren(manager); + QListIterator itTime(timePropertyManagers); + while (itTime.hasNext()) + d_ptr->m_timeEditFactory->removePropertyManager(itTime.next()); + + QList dateTimePropertyManagers = qFindChildren(manager); + QListIterator itDateTime(dateTimePropertyManagers); + while (itDateTime.hasNext()) + d_ptr->m_dateTimeEditFactory->removePropertyManager(itDateTime.next()); + + QList keySequencePropertyManagers = qFindChildren(manager); + QListIterator itKeySequence(keySequencePropertyManagers); + while (itKeySequence.hasNext()) + d_ptr->m_keySequenceEditorFactory->removePropertyManager(itKeySequence.next()); + + QList charPropertyManagers = qFindChildren(manager); + QListIterator itChar(charPropertyManagers); + while (itChar.hasNext()) + d_ptr->m_charEditorFactory->removePropertyManager(itChar.next()); + + QList localePropertyManagers = qFindChildren(manager); + QListIterator itLocale(localePropertyManagers); + while (itLocale.hasNext()) + d_ptr->m_comboBoxFactory->removePropertyManager(itLocale.next()->subEnumPropertyManager()); + + QList pointPropertyManagers = qFindChildren(manager); + QListIterator itPoint(pointPropertyManagers); + while (itPoint.hasNext()) + d_ptr->m_spinBoxFactory->removePropertyManager(itPoint.next()->subIntPropertyManager()); + + QList pointFPropertyManagers = qFindChildren(manager); + QListIterator itPointF(pointFPropertyManagers); + while (itPointF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itPointF.next()->subDoublePropertyManager()); + + QList sizePropertyManagers = qFindChildren(manager); + QListIterator itSize(sizePropertyManagers); + while (itSize.hasNext()) + d_ptr->m_spinBoxFactory->removePropertyManager(itSize.next()->subIntPropertyManager()); + + QList sizeFPropertyManagers = qFindChildren(manager); + QListIterator itSizeF(sizeFPropertyManagers); + while (itSizeF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itSizeF.next()->subDoublePropertyManager()); + + QList rectPropertyManagers = qFindChildren(manager); + QListIterator itRect(rectPropertyManagers); + while (itRect.hasNext()) + d_ptr->m_spinBoxFactory->removePropertyManager(itRect.next()->subIntPropertyManager()); + + QList rectFPropertyManagers = qFindChildren(manager); + QListIterator itRectF(rectFPropertyManagers); + while (itRectF.hasNext()) + d_ptr->m_doubleSpinBoxFactory->removePropertyManager(itRectF.next()->subDoublePropertyManager()); + + QList colorPropertyManagers = qFindChildren(manager); + QListIterator itColor(colorPropertyManagers); + while (itColor.hasNext()) { + QtColorPropertyManager *manager = itColor.next(); + d_ptr->m_colorEditorFactory->removePropertyManager(manager); + d_ptr->m_spinBoxFactory->removePropertyManager(manager->subIntPropertyManager()); + } + + QList enumPropertyManagers = qFindChildren(manager); + QListIterator itEnum(enumPropertyManagers); + while (itEnum.hasNext()) + d_ptr->m_comboBoxFactory->removePropertyManager(itEnum.next()); + + QList sizePolicyPropertyManagers = qFindChildren(manager); + QListIterator itSizePolicy(sizePolicyPropertyManagers); + while (itSizePolicy.hasNext()) { + QtSizePolicyPropertyManager *manager = itSizePolicy.next(); + d_ptr->m_spinBoxFactory->removePropertyManager(manager->subIntPropertyManager()); + d_ptr->m_comboBoxFactory->removePropertyManager(manager->subEnumPropertyManager()); + } + + QList fontPropertyManagers = qFindChildren(manager); + QListIterator itFont(fontPropertyManagers); + while (itFont.hasNext()) { + QtFontPropertyManager *manager = itFont.next(); + d_ptr->m_fontEditorFactory->removePropertyManager(manager); + d_ptr->m_spinBoxFactory->removePropertyManager(manager->subIntPropertyManager()); + d_ptr->m_comboBoxFactory->removePropertyManager(manager->subEnumPropertyManager()); + d_ptr->m_checkBoxFactory->removePropertyManager(manager->subBoolPropertyManager()); + } + + QList cursorPropertyManagers = qFindChildren(manager); + QListIterator itCursor(cursorPropertyManagers); + while (itCursor.hasNext()) + d_ptr->m_cursorEditorFactory->removePropertyManager(itCursor.next()); + + QList flagPropertyManagers = qFindChildren(manager); + QListIterator itFlag(flagPropertyManagers); + while (itFlag.hasNext()) + d_ptr->m_checkBoxFactory->removePropertyManager(itFlag.next()->subBoolPropertyManager()); +} + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +#include "moc_qtvariantproperty.cxx" diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtvariantproperty.h b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtvariantproperty.h new file mode 100644 index 000000000..9f20d3dd4 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/qtvariantproperty.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** This file is part of a Qt Solutions component. +** +** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** Commercial Usage +** Licensees holding valid Qt Commercial licenses may use this file in +** accordance with the Qt Solutions Commercial License Agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and Nokia. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** Please note Third Party Software included with Qt Solutions may impose +** additional restrictions and it is the user's responsibility to ensure +** that they have met the licensing requirements of the GPL, LGPL, or Qt +** Solutions Commercial license and the relevant license of the Third +** Party Software they are using. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** +****************************************************************************/ + +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the tools applications of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QTVARIANTPROPERTY_H +#define QTVARIANTPROPERTY_H + +#include "qtpropertybrowser.h" +#include +#include + +#if QT_VERSION >= 0x040400 +QT_BEGIN_NAMESPACE +#endif + +typedef QMap QtIconMap; + +class QtVariantPropertyManager; +class QtVariantPropertyPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtVariantProperty : public QtProperty +{ +public: + ~QtVariantProperty(); + QVariant value() const; + QVariant attributeValue(const QString &attribute) const; + int valueType() const; + int propertyType() const; + + void setValue(const QVariant &value); + void setAttribute(const QString &attribute, const QVariant &value); +protected: + QtVariantProperty(QtVariantPropertyManager *manager); +private: + friend class QtVariantPropertyManager; + QtVariantPropertyPrivate *d_ptr; +}; + +class QtVariantPropertyManagerPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtVariantPropertyManager : public QtAbstractPropertyManager +{ + Q_OBJECT +public: + QtVariantPropertyManager(QObject *parent = 0); + ~QtVariantPropertyManager(); + + virtual QtVariantProperty *addProperty(int propertyType, const QString &name = QString()); + + int propertyType(const QtProperty *property) const; + int valueType(const QtProperty *property) const; + QtVariantProperty *variantProperty(const QtProperty *property) const; + + virtual bool isPropertyTypeSupported(int propertyType) const; + virtual int valueType(int propertyType) const; + virtual QStringList attributes(int propertyType) const; + virtual int attributeType(int propertyType, const QString &attribute) const; + + virtual QVariant value(const QtProperty *property) const; + virtual QVariant attributeValue(const QtProperty *property, const QString &attribute) const; + + static int enumTypeId(); + static int flagTypeId(); + static int groupTypeId(); + static int iconMapTypeId(); +public Q_SLOTS: + virtual void setValue(QtProperty *property, const QVariant &val); + virtual void setAttribute(QtProperty *property, + const QString &attribute, const QVariant &value); +Q_SIGNALS: + void valueChanged(QtProperty *property, const QVariant &val); + void attributeChanged(QtProperty *property, + const QString &attribute, const QVariant &val); +protected: + virtual bool hasValue(const QtProperty *property) const; + QString valueText(const QtProperty *property) const; + QIcon valueIcon(const QtProperty *property) const; + virtual void initializeProperty(QtProperty *property); + virtual void uninitializeProperty(QtProperty *property); + virtual QtProperty *createProperty(); +private: + QtVariantPropertyManagerPrivate *d_ptr; + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, int, int)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, double, double)) + Q_PRIVATE_SLOT(d_func(), void slotSingleStepChanged(QtProperty *, double)) + Q_PRIVATE_SLOT(d_func(), void slotDecimalsChanged(QtProperty *, int)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, bool)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QString &)) + Q_PRIVATE_SLOT(d_func(), void slotRegExpChanged(QtProperty *, const QRegExp &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QDate &, const QDate &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QTime &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QDateTime &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QKeySequence &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QChar &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QLocale &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QPoint &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QPointF &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSize &)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QSize &, const QSize &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSizeF &)) + Q_PRIVATE_SLOT(d_func(), void slotRangeChanged(QtProperty *, const QSizeF &, const QSizeF &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QRect &)) + Q_PRIVATE_SLOT(d_func(), void slotConstraintChanged(QtProperty *, const QRect &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QRectF &)) + Q_PRIVATE_SLOT(d_func(), void slotConstraintChanged(QtProperty *, const QRectF &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QColor &)) + Q_PRIVATE_SLOT(d_func(), void slotEnumNamesChanged(QtProperty *, const QStringList &)) + Q_PRIVATE_SLOT(d_func(), void slotEnumIconsChanged(QtProperty *, const QMap &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QSizePolicy &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QFont &)) + Q_PRIVATE_SLOT(d_func(), void slotValueChanged(QtProperty *, const QCursor &)) + Q_PRIVATE_SLOT(d_func(), void slotFlagNamesChanged(QtProperty *, const QStringList &)) + + Q_PRIVATE_SLOT(d_func(), void slotPropertyInserted(QtProperty *, QtProperty *, QtProperty *)) + Q_PRIVATE_SLOT(d_func(), void slotPropertyRemoved(QtProperty *, QtProperty *)) + Q_DECLARE_PRIVATE(QtVariantPropertyManager) + Q_DISABLE_COPY(QtVariantPropertyManager) +}; + +class QtVariantEditorFactoryPrivate; + +class QT_QTPROPERTYBROWSER_EXPORT QtVariantEditorFactory : public QtAbstractEditorFactory +{ + Q_OBJECT +public: + QtVariantEditorFactory(QObject *parent = 0); + ~QtVariantEditorFactory(); +protected: + void connectPropertyManager(QtVariantPropertyManager *manager); + QWidget *createEditor(QtVariantPropertyManager *manager, QtProperty *property, + QWidget *parent); + void disconnectPropertyManager(QtVariantPropertyManager *manager); +private: + QtVariantEditorFactoryPrivate *d_ptr; + Q_DECLARE_PRIVATE(QtVariantEditorFactory) + Q_DISABLE_COPY(QtVariantEditorFactory) +}; + +#if QT_VERSION >= 0x040400 +QT_END_NAMESPACE +#endif + +Q_DECLARE_METATYPE(QIcon) +Q_DECLARE_METATYPE(QtIconMap) +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/CMakeLists.txt index 3cbe745af..7dc1445bb 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/CMakeLists.txt @@ -1,3 +1,5 @@ +ADD_SUBDIRECTORY(3rdparty) + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${LIBXML2_INCLUDE_DIR} ${NEL_INCLUDE_DIR} ${QT_INCLUDES}) INCLUDE( ${QT_USE_FILE} ) From 23f1309995a522e870c448cfa3acb2f1bad337fe Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 3 Aug 2011 23:08:56 +0300 Subject: [PATCH 063/215] Changed: #1307 Added Phrase editor files and base functions --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_phrase.cpp | 116 ++++++++++++++++++ .../translation_manager/editor_phrase.cpp.txt | 53 -------- .../{editor_phrase.h.txt => editor_phrase.h} | 6 + .../translation_manager/editor_worksheet.cpp | 10 +- .../translation_manager/editor_worksheet.h | 2 - .../translation_manager_editor.h | 10 ++ .../translation_manager_main_window.cpp | 24 +++- .../translation_manager_main_window.h | 3 +- 8 files changed, 158 insertions(+), 66 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp.txt rename code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/{editor_phrase.h.txt => editor_phrase.h} (86%) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp new file mode 100644 index 000000000..4036f8425 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp @@ -0,0 +1,116 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Nel includes +#include "nel/misc/path.h" +#include "nel/misc/diff_tool.h" + +// Qt includes +#include +#include +#include +#include + +// Project includes +#include "editor_phrase.h" +#include "translation_manager_constants.h" + +using namespace std; + +namespace Plugin { + +void CEditorPhrase::open(QString filename) +{ + vector phrases; + if(readPhraseFile(filename.toStdString(), phrases, false)) + { + text_edit = new QTextEdit(); + // read the file content + QFile file(filename); + QTextStream in(&file); + // set the file content to the text edit + QString content = in.readAll(); + text_edit->setText(content); + // window settings + setCurrentFile(filename); + setAttribute(Qt::WA_DeleteOnClose); + setWidget(text_edit); + editor_type = Constants::ED_PHRASE; + current_file = filename; + } else { + QErrorMessage error; + error.showMessage("This file is not a phrase file."); + error.exec(); + } +} + +void CEditorPhrase::activateWindow() +{ + showMaximized(); +} + +void CEditorPhrase::save() +{ + QFile file(current_file); + QTextStream out(&file); + out<toPlainText(); + setCurrentFile(current_file); +} + +void CEditorPhrase::saveAs(QString filename) +{ + QFile file(filename); + QTextStream out(&file); + out<toPlainText(); + current_file = filename; + setCurrentFile(current_file); +} + +void CEditorPhrase::closeEvent(QCloseEvent *event) +{ + if(isWindowModified()) + { + QMessageBox msgBox; + msgBox.setText("The document has been modified."); + msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Save: + save(); + event->accept(); + close(); + break; + case QMessageBox::Discard: + event->accept(); + close(); + break; + case QMessageBox::Cancel: + event->ignore(); + break; + default: + break; + } + } else { + event->accept(); + close(); + } +} + +} \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp.txt deleted file mode 100644 index 83d360d8a..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp.txt +++ /dev/null @@ -1,53 +0,0 @@ -// Translation Manager Plugin - OVQT Plugin -// Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Emanuel Costea -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -// Qt includes -#include -#include -#include -#include - -// Project includes -#include "editor_phrase.h" -#include "translation_manager_constants.h" - -using namespace std; - -namespace Plugin { - -void CEditorPhrase::open(QString filename) -{ - -} - -void CEditorPhrase::activateWindow() -{ - -} - -void CEditorPhrase::save() -{ - -} - -void CEditorPhrase::saveAs(QString filename) -{ - -} - - -} \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h similarity index 86% rename from code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h.txt rename to code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h index 539314eaf..21152ad16 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h @@ -20,11 +20,14 @@ // Qt includes #include +#include +#include #include #include #include #include #include +#include // Project includes #include "translation_manager_editor.h" @@ -34,6 +37,8 @@ namespace Plugin { class CEditorPhrase : public CEditor { Q_OBJECT +private: + QTextEdit *text_edit; public: CEditorPhrase(QMdiArea* parent) : CEditor(parent) {} CEditorPhrase() : CEditor() {} @@ -41,6 +46,7 @@ public: void save(); void saveAs(QString filename); void activateWindow(); + void closeEvent(QCloseEvent *event); }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index 2c7261805..39b86d76a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -202,6 +202,7 @@ void CEditorWorksheet::saveAs(QString filename) } ucstring s = prepareExcelSheet(new_file); NLMISC::CI18N::writeTextFile(filename.toStdString(), s, false); + current_file = filename; setCurrentFile(filename); } @@ -484,15 +485,6 @@ void CEditorWorksheet::mergeWorksheetFile(QString filename) } } -void CEditorWorksheet::setCurrentFile(QString filename) -{ - QFileInfo *file = new QFileInfo(filename); - current_file = file->canonicalFilePath(); - setWindowModified(false); - setWindowTitle(file->fileName() + "[*]"); - setWindowFilePath(current_file); -} - void CEditorWorksheet::closeEvent(QCloseEvent *event) { if(isWindowModified()) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 983dba4bc..a124188bc 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -65,8 +65,6 @@ private Q_SLOTS: void worksheetEditorChanged(QTableWidgetItem * item); void insertRow(); void deleteRow(); -private: - void setCurrentFile(QString filename); }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h index 767dd90de..39ef74f2a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h @@ -23,6 +23,7 @@ #include #include #include +#include namespace Plugin { @@ -52,6 +53,15 @@ public: { current_stack = stack; } + void setCurrentFile(QString filename) + { + QFileInfo *file = new QFileInfo(filename); + current_file = file->canonicalFilePath(); + setWindowModified(false); + setWindowTitle(file->fileName() + "[*]"); + setWindowFilePath(current_file); + } + }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index e6fa9508a..f12c6a830 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -217,6 +217,14 @@ void CMainWindow::open() new_window->open(file_name); new_window->activateWindow(); } + // phrase editor + if(isPhraseEditor(file_name)) + { + CEditorPhrase *new_window = new CEditorPhrase(_ui.mdiArea); + new_window->setUndoStack(m_undoStack); + new_window->open(file_name); + new_window->activateWindow(); + } #ifndef QT_NO_CURSOR QApplication::restoreOverrideCursor(); #endif @@ -559,12 +567,26 @@ bool CMainWindow::isWorksheetEditor(QString filename) STRING_MANAGER::TWorksheet wk_file; if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) { - return true; + if(wk_file.ColCount > 1) + return true; + else + return false; } else { return false; } } +bool CMainWindow::isPhraseEditor(QString filename) +{ + vector phrases; + if(readPhraseFile(filename.toStdString(), phrases, false)) + { + return true; + } else { + return false; + } +} + bool CCoreListener::closeMainWindow() const { return true; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index bd21f698d..c58ce9826 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -45,6 +45,7 @@ #include "ui_translation_manager_main_window.h" #include #include "editor_worksheet.h" +#include "editor_phrase.h" class QWidget; @@ -101,7 +102,7 @@ private: // Worksheet specific functions CEditorWorksheet* getEditorByWorksheetType(const QString &type); bool isWorksheetEditor(QString filename); - + bool isPhraseEditor(QString filename); }; From 44804ea77038136fe074ee6c53ff2b517b9ca372 Mon Sep 17 00:00:00 2001 From: cemycc Date: Thu, 4 Aug 2011 17:19:17 +0300 Subject: [PATCH 064/215] Changed: #1307 Added some features and making little modifications --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_phrase.cpp | 62 +- .../translation_manager/editor_worksheet.cpp | 1145 +++++++++-------- .../translation_manager/editor_worksheet.h | 2 + .../translation_manager_main_window.cpp | 57 +- .../translation_manager_main_window.h | 16 +- .../translation_manager_plugin.cpp | 8 +- .../translation_manager_plugin.h | 4 +- 7 files changed, 681 insertions(+), 613 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp index 4036f8425..b202b1cbb 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp @@ -80,37 +80,37 @@ void CEditorPhrase::saveAs(QString filename) setCurrentFile(current_file); } -void CEditorPhrase::closeEvent(QCloseEvent *event) -{ - if(isWindowModified()) - { - QMessageBox msgBox; - msgBox.setText("The document has been modified."); - msgBox.setInformativeText("Do you want to save your changes?"); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - int ret = msgBox.exec(); - switch (ret) - { - case QMessageBox::Save: - save(); - event->accept(); - close(); - break; - case QMessageBox::Discard: - event->accept(); - close(); - break; - case QMessageBox::Cancel: - event->ignore(); - break; - default: - break; - } - } else { - event->accept(); - close(); - } +void CEditorPhrase::closeEvent(QCloseEvent *event) +{ + if(isWindowModified()) + { + QMessageBox msgBox; + msgBox.setText("The document has been modified."); + msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Save: + save(); + event->accept(); + close(); + break; + case QMessageBox::Discard: + event->accept(); + close(); + break; + case QMessageBox::Cancel: + event->ignore(); + break; + default: + break; + } + } else { + event->accept(); + close(); + } } } \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index 39b86d76a..a6ef28d36 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -1,562 +1,583 @@ -// Translation Manager Plugin - OVQT Plugin -// Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Emanuel Costea -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -// Qt includes -#include -#include -#include -#include -#include - -// Project includes -#include "editor_worksheet.h" -#include "extract_bot_names.h" -#include "translation_manager_constants.h" -#include - -using namespace std; - -namespace Plugin { - - - -void CEditorWorksheet::open(QString filename) -{ - STRING_MANAGER::TWorksheet wk_file; - if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) - { - bool hasHashValue = false; - table_editor = new QTableWidget(); - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - table_editor->setColumnCount(wk_file.ColCount - 1); - hasHashValue = true; - } else { - table_editor->setColumnCount(wk_file.ColCount); - } - table_editor->setRowCount(wk_file.size() - 1); - - // read columns name - for(unsigned int i = 0; i < wk_file.ColCount; i++) - { - if(hasHashValue && i == 0) - { - // we don't show the column with hash value - } else { - QTableWidgetItem *col = new QTableWidgetItem(); - ucstring col_name = wk_file.getData(0, i); - col->setText(tr(col_name.toString().c_str())); - if(hasHashValue) - { - table_editor->setHorizontalHeaderItem(i - 1, col); - } else { - table_editor->setHorizontalHeaderItem(i, col); - } - } - } - - // read rows - for(unsigned int i = 1; i < wk_file.size(); i++) - { - for(unsigned int j = 0; j < wk_file.ColCount; j++) - { - if(hasHashValue && j == 0) - { - // we don't show the column with hash value - } else { - QTableWidgetItem *row = new QTableWidgetItem(); - ucstring row_value = wk_file.getData(i, j); - row->setText(tr(row_value.toString().c_str())); - if(hasHashValue) - { - table_editor->setItem(i - 1, j - 1, row); - } else { - table_editor->setItem(i - 1, j, row); - } - } - } - } - setCurrentFile(filename); - setAttribute(Qt::WA_DeleteOnClose); - setWidget(table_editor); - editor_type = Constants::ED_SHEET; - table_editor->resizeColumnsToContents(); - table_editor->resizeRowsToContents(); - // set editor signals - connect(table_editor, SIGNAL(itemChanged(QTableWidgetItem*) ), this, SLOT(worksheetEditorChanged(QTableWidgetItem*))); - connect(table_editor, SIGNAL(itemDoubleClicked(QTableWidgetItem*) ), this, SLOT(worksheetEditorCellEntered(QTableWidgetItem*))); - } else { - QErrorMessage error; - error.showMessage("This file is not a worksheet file."); - error.exec(); - } - -} - -void CEditorWorksheet::activateWindow() -{ - showMaximized(); -} - -void CEditorWorksheet::save() -{ - STRING_MANAGER::TWorksheet wk_file; - loadExcelSheet(current_file.toStdString(), wk_file, true); - uint rowIdx; - uint colIdx = 0; - bool hasHashValue = false; - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - hasHashValue = true; - colIdx = 1; - } - for(int i = 0; i < table_editor->rowCount(); i++) - { - // maybe extra rows ? - if((unsigned)table_editor->rowCount() > (wk_file.size() - 1)) - { - rowIdx = wk_file.size(); - wk_file.resize(rowIdx + table_editor->rowCount() - wk_file.size() + 1); - } - for(int j = 0; j < table_editor->columnCount(); j++) - { - ucstring tvalue; - ucstring colname; - uint rowIdf; - QString tvalueQt = table_editor->item(i, j)->text(); - tvalue = ucstring(tvalueQt.toStdString()); - colname = wk_file.getData(0, j + colIdx); - - rowIdf = uint(i + 1); - if(wk_file.findRow(j + colIdx, colname, rowIdf)) // search for the row - { - if(wk_file.getData(i + 1, j + colIdx) != tvalue) // verify the current value - { - wk_file.setData(i + 1, j + colIdx, tvalue); // change the value - } - } else { - wk_file.setData(i + 1, j + colIdx, tvalue); // insert the value - } - } - } - if(hasHashValue) - { - // rewrite the hash codes - makeHashCode(wk_file, true); - } - // write to file - ucstring s = prepareExcelSheet(wk_file); - NLMISC::CI18N::writeTextFile(current_file.toStdString(), s, false); - setCurrentFile(current_file); -} - -void CEditorWorksheet::saveAs(QString filename) -{ - STRING_MANAGER::TWorksheet new_file, wk_file; - loadExcelSheet(current_file.toStdString(), wk_file, true); - // set columns - new_file.resize(new_file.size() + 1); - for(unsigned int i = 0; i < wk_file.ColCount; i++) - { - ucstring col_name = wk_file.getData(0, i); - new_file.insertColumn(new_file.ColCount); - new_file.setData(0, new_file.ColCount - 1, col_name); - } - // read all the rows from table - uint rowIdx; - uint colIdx = 0; - bool hasHashValue = false; - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - hasHashValue = true; - colIdx = 1; - } - for(int i = 0; i < table_editor->rowCount(); i++) - { - rowIdx = new_file.size(); - new_file.resize(new_file.size() + 1); - for(int j = 0; j < table_editor->columnCount(); j++) - { - QTableWidgetItem* item = table_editor->item(i, j); - new_file.setData(rowIdx, j + colIdx, ucstring(item->text().toStdString())); - } - } - if(hasHashValue) - { - // rewrite the hash codes - makeHashCode(wk_file, true); - } - ucstring s = prepareExcelSheet(new_file); - NLMISC::CI18N::writeTextFile(filename.toStdString(), s, false); - current_file = filename; - setCurrentFile(filename); -} - -void CEditorWorksheet::insertRow() -{ - int last_row = table_editor->rowCount(); - current_stack->push(new CUndoWorksheetNewCommand(table_editor, last_row)); -} - -void CEditorWorksheet::deleteRow() -{ - int selected_row = table_editor->currentRow(); - QMessageBox msgBox; - msgBox.setText("The row will be deleted."); - msgBox.setInformativeText("Do you want to delete the selected row ?"); - msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); - msgBox.setDefaultButton(QMessageBox::No); - int ret = msgBox.exec(); - if(ret == QMessageBox::Yes) - { - current_stack->push(new CUndoWorksheetDeleteCommand(table_editor, selected_row)); - } - - table_editor->clearFocus(); - table_editor->clearSelection(); - return; -} - -void CEditorWorksheet::worksheetEditorCellEntered(QTableWidgetItem * item) -{ - temp_content = item->text(); - current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); -} - -void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem * item) -{ - if(temp_content != item->text()) - { - //current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); - } - - if(!isWindowModified()) - setWindowModified(true); -} - - -void CEditorWorksheet::extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig) -{ - bool modified = false; - QList new_items; - - ExtractBotNames ebn; - ebn.setRequiredSettings(filters, level_design_path); - ebn.extractBotNamesFromPrimitives(ligoConfig); - // get SimpleNames - { - map SimpleNames = ebn.getSimpleNames(); - map::iterator it(SimpleNames.begin()), last(SimpleNames.end()); - - for (; it != last; ++it) - { - QList search_results = table_editor->findItems(tr(it->first.c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - const int currentRow = table_editor->rowCount(); - table_editor->setRowCount(currentRow + 1); - QTableWidgetItem *bot_name_row = new QTableWidgetItem(); - bot_name_row->setText(tr(it->first.c_str())); - bot_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 0, bot_name_row); - QTableWidgetItem *translation_name_row = new QTableWidgetItem(); - translation_name_row->setBackgroundColor(QColor("#F75D59")); - translation_name_row->setText(tr(it->first.c_str())); - table_editor ->setItem(currentRow , 1, translation_name_row); - QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); - sheet_name_row->setText(tr(it->second.SheetName.c_str())); - sheet_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 2, sheet_name_row); - if(!modified) modified = true; - CTableWidgetItemStore bot_name_row_s(bot_name_row, currentRow, 0); - new_items.push_back(bot_name_row_s); - CTableWidgetItemStore translation_name_row_s(translation_name_row, currentRow, 1); - new_items.push_back(translation_name_row_s); - CTableWidgetItemStore sheet_name_row_s(sheet_name_row, currentRow, 2); - new_items.push_back(sheet_name_row_s); - } - } - ebn.cleanSimpleNames(); - } - // get GenericNames - { - set GenericNames = ebn.getGenericNames(); - set::iterator it(GenericNames.begin()), last(GenericNames.end()); - for (; it != last; ++it) - { - string gnName = "gn_" + ebn.cleanupName(*it); - QList search_results = table_editor->findItems(tr((*it).c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - const int currentRow = table_editor->rowCount(); - table_editor->setRowCount(currentRow + 1); - QTableWidgetItem *bot_name_row = new QTableWidgetItem(); - bot_name_row->setText(tr((*it).c_str())); - bot_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 0, bot_name_row); - QTableWidgetItem *translation_name_row = new QTableWidgetItem(); - translation_name_row->setBackgroundColor(QColor("#F75D59")); - translation_name_row->setText(tr(gnName.c_str())); - table_editor ->setItem(currentRow , 1, translation_name_row); - QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); - sheet_name_row->setText(" "); - sheet_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 2, sheet_name_row); - if(!modified) modified = true; - CTableWidgetItemStore bot_name_row_s(bot_name_row, currentRow, 0); - new_items.push_back(bot_name_row_s); - CTableWidgetItemStore translation_name_row_s(translation_name_row, currentRow, 1); - new_items.push_back(translation_name_row_s); - CTableWidgetItemStore sheet_name_row_s(sheet_name_row, currentRow, 2); - new_items.push_back(sheet_name_row_s); - } - } - ebn.cleanGenericNames(); - } - - current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); - if(modified) - { - setWindowModified(true); - } - -} - -void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordListBuilder& wordListBuilder) -{ - uint i; - - // **** Load the excel sheet - // load - TWorksheet workSheet; - if(!loadExcelSheet(filename.toStdString(), workSheet, true)) - { - nlwarning("Error reading '%s'. Aborted", filename.toStdString().c_str()); - return; - } - // get the key column index - uint keyColIndex = 0; - if(!workSheet.findCol(columnId.toStdString(), keyColIndex)) - { - nlwarning("Error: Don't find the column '%s'. '%s' Aborted", columnId.toStdString().c_str(), filename.toStdString().c_str()); - return; - } - // get the name column index - uint nameColIndex; - if(!workSheet.findCol(ucstring("name"), nameColIndex)) - { - nlwarning("Error: Don't find the column 'name'. '%s' Aborted", filename.toStdString().c_str()); - return; - } - - // **** List all words with the builder given - std::vector allWords; - if(!wordListBuilder.buildWordList(allWords, filename.toStdString())) - { - return; - } - bool modified = false; - QList new_items; - for(i = 0; i < allWords.size(); i++) - { - string keyName = allWords[i]; - QList search_results = table_editor->findItems(tr(keyName.c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - - int knPos = 0, nPos = 0; - if(workSheet.getData(0, 0) == ucstring("*HASH_VALUE")) - { - knPos = keyColIndex - 1; - nPos = nameColIndex - 1; - } else { - knPos = keyColIndex; - nPos = nameColIndex; - } - const int currentRow = table_editor->rowCount(); - table_editor->setRowCount(currentRow + 1); - // keyName row - QTableWidgetItem *key_name_row = new QTableWidgetItem(); - key_name_row->setText(tr(keyName.c_str())); - key_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, knPos, key_name_row); - // nameColumn key - QTableWidgetItem *name_row = new QTableWidgetItem(); - name_row->setText(QString("") + tr(keyName.c_str())); - name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, nPos, name_row); - if(!modified) modified = true; - CTableWidgetItemStore key_name_row_s(key_name_row, currentRow, knPos); - new_items.push_back(key_name_row_s); - CTableWidgetItemStore name_row_s(name_row, currentRow, nPos); - new_items.push_back(name_row_s); - - } - } - current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); - if(modified) - { - setWindowModified(true); - table_editor->scrollToBottom(); - } -} - -bool CEditorWorksheet::compareWorksheetFile(QString filename) -{ - STRING_MANAGER::TWorksheet wk_file; - int colIndex = 0; - if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) - { - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - colIndex = 1; - } - if(wk_file.ColCount - colIndex != table_editor->columnCount()) - { - return false; - } - for(int i = 0; i < table_editor->columnCount(); i++) - { - QString item = table_editor->horizontalHeaderItem(i)->text(); - ucstring itemC = wk_file.getData(0, i+ colIndex); - if(item.toStdString() != itemC.toString()) - { - nlwarning(item.toStdString().c_str()); - nlwarning(itemC.toString().c_str()); - return false; - } - } - } else { - return false; - } - - return true; -} - -void CEditorWorksheet::mergeWorksheetFile(QString filename) -{ - STRING_MANAGER::TWorksheet wk_file; - if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) - { - bool hasHashValue = false; - int colIndex = 0; - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - hasHashValue = true; - colIndex = 1; - } - // read rows - for(unsigned int i = 1; i < wk_file.size(); i++) - { - // search with the first column - ucstring rowId = wk_file.getData(i,colIndex); - QList search_results = table_editor->findItems(tr(rowId.toString().c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - const int lastRow = table_editor->rowCount(); - table_editor->setRowCount(lastRow + 1); - for(unsigned int j = 0; j < table_editor->columnCount(); j++) - { - ucstring rowValue = wk_file.getData(i, j + colIndex); // get the value - QTableWidgetItem *row = new QTableWidgetItem(); - row->setText(QString(rowValue.toString().c_str())); // set the value in table item - table_editor->setItem(lastRow, j, row); - } - } - } - } else { - QErrorMessage error; - error.showMessage("This file is not a worksheet file."); - error.exec(); - } -} - -void CEditorWorksheet::closeEvent(QCloseEvent *event) -{ - if(isWindowModified()) - { - QMessageBox msgBox; - msgBox.setText("The document has been modified."); - msgBox.setInformativeText("Do you want to save your changes?"); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - int ret = msgBox.exec(); - switch (ret) - { - case QMessageBox::Save: - save(); - event->accept(); - close(); - break; - case QMessageBox::Discard: - event->accept(); - close(); - break; - case QMessageBox::Cancel: - event->ignore(); - break; - default: - break; - } - } else { - event->accept(); - close(); - } -} - - -bool CEditorWorksheet::isBotNamesTable() -{ - bool status = true; - if(table_editor->horizontalHeaderItem(0)->text() != "bot name" - || table_editor->horizontalHeaderItem(1)->text() != "translated name" - || table_editor->horizontalHeaderItem(2)->text() != "sheet_name") - { - status = false; - } - - return status; -} - -bool CEditorWorksheet::isSheetTable(QString type) -{ - QString column_name; - if(type.toAscii() == Constants::WK_ITEM) - { - column_name = "item ID"; - } else if(type.toAscii() == Constants::WK_CREATURE) { - column_name = "creature ID"; - } else if(type.toAscii() == Constants::WK_SBRICK) { - column_name = "sbrick ID"; - } else if(type.toAscii() == Constants::WK_SPHRASE) { - column_name = "sphrase ID"; - } else if(type.toAscii() == Constants::WK_PLACE) { - column_name = "placeId"; - } - bool status = true; - if(table_editor->horizontalHeaderItem(0)->text() != column_name - || table_editor->horizontalHeaderItem(1)->text() != "name") - { - status = false; - } - - return status; -} - -} - - +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Qt includes +#include +#include +#include +#include +#include +#include +#include + +// Project includes +#include "editor_worksheet.h" +#include "extract_bot_names.h" +#include "translation_manager_constants.h" +#include + +using namespace std; + +namespace Plugin { + + + +void CEditorWorksheet::open(QString filename) +{ + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + bool hasHashValue = false; + table_editor = new QTableWidget(); + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + table_editor->setColumnCount(wk_file.ColCount - 1); + hasHashValue = true; + } else { + table_editor->setColumnCount(wk_file.ColCount); + } + table_editor->setRowCount(wk_file.size() - 1); + + // read columns name + for(unsigned int i = 0; i < wk_file.ColCount; i++) + { + if(hasHashValue && i == 0) + { + // we don't show the column with hash value + } else { + QTableWidgetItem *col = new QTableWidgetItem(); + ucstring col_name = wk_file.getData(0, i); + col->setText(tr(col_name.toString().c_str())); + if(hasHashValue) + { + table_editor->setHorizontalHeaderItem(i - 1, col); + } else { + table_editor->setHorizontalHeaderItem(i, col); + } + } + } + + // read rows + for(unsigned int i = 1; i < wk_file.size(); i++) + { + for(unsigned int j = 0; j < wk_file.ColCount; j++) + { + if(hasHashValue && j == 0) + { + // we don't show the column with hash value + } else { + QTableWidgetItem *row = new QTableWidgetItem(); + ucstring row_value = wk_file.getData(i, j); + row->setText(tr(row_value.toString().c_str())); + if(hasHashValue) + { + table_editor->setItem(i - 1, j - 1, row); + } else { + table_editor->setItem(i - 1, j, row); + } + } + } + } + setCurrentFile(filename); + setAttribute(Qt::WA_DeleteOnClose); + setWidget(table_editor); + editor_type = Constants::ED_SHEET; + table_editor->resizeColumnsToContents(); + table_editor->resizeRowsToContents(); + // set editor signals + connect(table_editor, SIGNAL(itemChanged(QTableWidgetItem*) ), this, SLOT(worksheetEditorChanged(QTableWidgetItem*))); + connect(table_editor, SIGNAL(itemDoubleClicked(QTableWidgetItem*) ), this, SLOT(worksheetEditorCellEntered(QTableWidgetItem*))); + connect (table_editor,SIGNAL(customContextMenuRequested(const QPoint &)), this,SLOT(contextMenuEvent(QContextMenuEvent*))); + } else { + QErrorMessage error; + error.showMessage("This file is not a worksheet file."); + error.exec(); + } + +} + + +void CEditorWorksheet::contextMenuEvent(QContextMenuEvent *e) +{ + QAction *insertRowAct = new QAction("Insert new row", this); + connect(insertRowAct, SIGNAL(triggered()), this, SLOT(insertRow())); + QAction *deleteRowAct = new QAction("Delete row", this); + connect(deleteRowAct, SIGNAL(triggered()), this, SLOT(deleteRow())); + + QMenu *contextMenu = new QMenu(this); + contextMenu->addAction(insertRowAct); + contextMenu->addAction(deleteRowAct); + contextMenu->exec( e->globalPos() ); + delete contextMenu; + contextMenu = NULL; +} + + +void CEditorWorksheet::activateWindow() +{ + showMaximized(); +} + +void CEditorWorksheet::save() +{ + STRING_MANAGER::TWorksheet wk_file; + loadExcelSheet(current_file.toStdString(), wk_file, true); + uint rowIdx; + uint colIdx = 0; + bool hasHashValue = false; + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + hasHashValue = true; + colIdx = 1; + } + for(int i = 0; i < table_editor->rowCount(); i++) + { + // maybe extra rows ? + if((unsigned)table_editor->rowCount() > (wk_file.size() - 1)) + { + rowIdx = wk_file.size(); + wk_file.resize(rowIdx + table_editor->rowCount() - wk_file.size() + 1); + } + for(int j = 0; j < table_editor->columnCount(); j++) + { + ucstring tvalue; + ucstring colname; + uint rowIdf; + QString tvalueQt = table_editor->item(i, j)->text(); + tvalue = ucstring(tvalueQt.toStdString()); + colname = wk_file.getData(0, j + colIdx); + + rowIdf = uint(i + 1); + if(wk_file.findRow(j + colIdx, colname, rowIdf)) // search for the row + { + if(wk_file.getData(i + 1, j + colIdx) != tvalue) // verify the current value + { + wk_file.setData(i + 1, j + colIdx, tvalue); // change the value + } + } else { + wk_file.setData(i + 1, j + colIdx, tvalue); // insert the value + } + } + } + if(hasHashValue) + { + // rewrite the hash codes + makeHashCode(wk_file, true); + } + // write to file + ucstring s = prepareExcelSheet(wk_file); + NLMISC::CI18N::writeTextFile(current_file.toStdString(), s, false); + setCurrentFile(current_file); +} + +void CEditorWorksheet::saveAs(QString filename) +{ + STRING_MANAGER::TWorksheet new_file, wk_file; + loadExcelSheet(current_file.toStdString(), wk_file, true); + // set columns + new_file.resize(new_file.size() + 1); + for(unsigned int i = 0; i < wk_file.ColCount; i++) + { + ucstring col_name = wk_file.getData(0, i); + new_file.insertColumn(new_file.ColCount); + new_file.setData(0, new_file.ColCount - 1, col_name); + } + // read all the rows from table + uint rowIdx; + uint colIdx = 0; + bool hasHashValue = false; + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + hasHashValue = true; + colIdx = 1; + } + for(int i = 0; i < table_editor->rowCount(); i++) + { + rowIdx = new_file.size(); + new_file.resize(new_file.size() + 1); + for(int j = 0; j < table_editor->columnCount(); j++) + { + QTableWidgetItem* item = table_editor->item(i, j); + new_file.setData(rowIdx, j + colIdx, ucstring(item->text().toStdString())); + } + } + if(hasHashValue) + { + // rewrite the hash codes + makeHashCode(wk_file, true); + } + ucstring s = prepareExcelSheet(new_file); + NLMISC::CI18N::writeTextFile(filename.toStdString(), s, false); + current_file = filename; + setCurrentFile(filename); +} + +void CEditorWorksheet::insertRow() +{ + int last_row = table_editor->rowCount(); + current_stack->push(new CUndoWorksheetNewCommand(table_editor, last_row)); +} + +void CEditorWorksheet::deleteRow() +{ + int selected_row = table_editor->currentRow(); + QMessageBox msgBox; + msgBox.setText("The row will be deleted."); + msgBox.setInformativeText("Do you want to delete the selected row ?"); + msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); + msgBox.setDefaultButton(QMessageBox::No); + int ret = msgBox.exec(); + if(ret == QMessageBox::Yes) + { + current_stack->push(new CUndoWorksheetDeleteCommand(table_editor, selected_row)); + } + + table_editor->clearFocus(); + table_editor->clearSelection(); + return; +} + +void CEditorWorksheet::worksheetEditorCellEntered(QTableWidgetItem * item) +{ + temp_content = item->text(); + current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); +} + +void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem * item) +{ + if(temp_content != item->text()) + { + //current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); + } + + if(!isWindowModified()) + setWindowModified(true); +} + + +void CEditorWorksheet::extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig) +{ + bool modified = false; + QList new_items; + + ExtractBotNames ebn; + ebn.setRequiredSettings(filters, level_design_path); + ebn.extractBotNamesFromPrimitives(ligoConfig); + // get SimpleNames + { + map SimpleNames = ebn.getSimpleNames(); + map::iterator it(SimpleNames.begin()), last(SimpleNames.end()); + + for (; it != last; ++it) + { + QList search_results = table_editor->findItems(tr(it->first.c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + QTableWidgetItem *bot_name_row = new QTableWidgetItem(); + bot_name_row->setText(tr(it->first.c_str())); + bot_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 0, bot_name_row); + QTableWidgetItem *translation_name_row = new QTableWidgetItem(); + translation_name_row->setBackgroundColor(QColor("#F75D59")); + translation_name_row->setText(tr(it->first.c_str())); + table_editor ->setItem(currentRow , 1, translation_name_row); + QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); + sheet_name_row->setText(tr(it->second.SheetName.c_str())); + sheet_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 2, sheet_name_row); + if(!modified) modified = true; + CTableWidgetItemStore bot_name_row_s(bot_name_row, currentRow, 0); + new_items.push_back(bot_name_row_s); + CTableWidgetItemStore translation_name_row_s(translation_name_row, currentRow, 1); + new_items.push_back(translation_name_row_s); + CTableWidgetItemStore sheet_name_row_s(sheet_name_row, currentRow, 2); + new_items.push_back(sheet_name_row_s); + } + } + ebn.cleanSimpleNames(); + } + // get GenericNames + { + set GenericNames = ebn.getGenericNames(); + set::iterator it(GenericNames.begin()), last(GenericNames.end()); + for (; it != last; ++it) + { + string gnName = "gn_" + ebn.cleanupName(*it); + QList search_results = table_editor->findItems(tr((*it).c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + QTableWidgetItem *bot_name_row = new QTableWidgetItem(); + bot_name_row->setText(tr((*it).c_str())); + bot_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 0, bot_name_row); + QTableWidgetItem *translation_name_row = new QTableWidgetItem(); + translation_name_row->setBackgroundColor(QColor("#F75D59")); + translation_name_row->setText(tr(gnName.c_str())); + table_editor ->setItem(currentRow , 1, translation_name_row); + QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); + sheet_name_row->setText(" "); + sheet_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, 2, sheet_name_row); + if(!modified) modified = true; + CTableWidgetItemStore bot_name_row_s(bot_name_row, currentRow, 0); + new_items.push_back(bot_name_row_s); + CTableWidgetItemStore translation_name_row_s(translation_name_row, currentRow, 1); + new_items.push_back(translation_name_row_s); + CTableWidgetItemStore sheet_name_row_s(sheet_name_row, currentRow, 2); + new_items.push_back(sheet_name_row_s); + } + } + ebn.cleanGenericNames(); + } + + current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); + if(modified) + { + setWindowModified(true); + table_editor->scrollToBottom(); + } + +} + +void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordListBuilder& wordListBuilder) +{ + uint i; + + // **** Load the excel sheet + // load + TWorksheet workSheet; + if(!loadExcelSheet(filename.toStdString(), workSheet, true)) + { + nlwarning("Error reading '%s'. Aborted", filename.toStdString().c_str()); + return; + } + // get the key column index + uint keyColIndex = 0; + if(!workSheet.findCol(columnId.toStdString(), keyColIndex)) + { + nlwarning("Error: Don't find the column '%s'. '%s' Aborted", columnId.toStdString().c_str(), filename.toStdString().c_str()); + return; + } + // get the name column index + uint nameColIndex; + if(!workSheet.findCol(ucstring("name"), nameColIndex)) + { + nlwarning("Error: Don't find the column 'name'. '%s' Aborted", filename.toStdString().c_str()); + return; + } + + // **** List all words with the builder given + std::vector allWords; + if(!wordListBuilder.buildWordList(allWords, filename.toStdString())) + { + return; + } + bool modified = false; + QList new_items; + for(i = 0; i < allWords.size(); i++) + { + string keyName = allWords[i]; + QList search_results = table_editor->findItems(tr(keyName.c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + + int knPos = 0, nPos = 0; + if(workSheet.getData(0, 0) == ucstring("*HASH_VALUE")) + { + knPos = keyColIndex - 1; + nPos = nameColIndex - 1; + } else { + knPos = keyColIndex; + nPos = nameColIndex; + } + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + // keyName row + QTableWidgetItem *key_name_row = new QTableWidgetItem(); + key_name_row->setText(tr(keyName.c_str())); + key_name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, knPos, key_name_row); + // nameColumn key + QTableWidgetItem *name_row = new QTableWidgetItem(); + name_row->setText(QString("") + tr(keyName.c_str())); + name_row->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, nPos, name_row); + if(!modified) modified = true; + CTableWidgetItemStore key_name_row_s(key_name_row, currentRow, knPos); + new_items.push_back(key_name_row_s); + CTableWidgetItemStore name_row_s(name_row, currentRow, nPos); + new_items.push_back(name_row_s); + + } + } + current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); + if(modified) + { + setWindowModified(true); + table_editor->scrollToBottom(); + } +} + +bool CEditorWorksheet::compareWorksheetFile(QString filename) +{ + STRING_MANAGER::TWorksheet wk_file; + int colIndex = 0; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + colIndex = 1; + } + if(wk_file.ColCount - colIndex != table_editor->columnCount()) + { + return false; + } + for(int i = 0; i < table_editor->columnCount(); i++) + { + QString item = table_editor->horizontalHeaderItem(i)->text(); + ucstring itemC = wk_file.getData(0, i+ colIndex); + if(item.toStdString() != itemC.toString()) + { + nlwarning(item.toStdString().c_str()); + nlwarning(itemC.toString().c_str()); + return false; + } + } + } else { + return false; + } + + return true; +} + +void CEditorWorksheet::mergeWorksheetFile(QString filename) +{ + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + bool hasHashValue = false; + int colIndex = 0; + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + hasHashValue = true; + colIndex = 1; + } + // read rows + for(unsigned int i = 1; i < wk_file.size(); i++) + { + // search with the first column + ucstring rowId = wk_file.getData(i,colIndex); + QList search_results = table_editor->findItems(tr(rowId.toString().c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + const int lastRow = table_editor->rowCount(); + table_editor->setRowCount(lastRow + 1); + for(unsigned int j = 0; j < table_editor->columnCount(); j++) + { + ucstring rowValue = wk_file.getData(i, j + colIndex); // get the value + QTableWidgetItem *row = new QTableWidgetItem(); + row->setText(QString(rowValue.toString().c_str())); // set the value in table item + table_editor->setItem(lastRow, j, row); + } + } + } + } else { + QErrorMessage error; + error.showMessage("This file is not a worksheet file."); + error.exec(); + } +} + +void CEditorWorksheet::closeEvent(QCloseEvent *event) +{ + if(isWindowModified()) + { + QMessageBox msgBox; + msgBox.setText("The document has been modified."); + msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Save: + save(); + event->accept(); + close(); + break; + case QMessageBox::Discard: + event->accept(); + close(); + break; + case QMessageBox::Cancel: + event->ignore(); + break; + default: + break; + } + } else { + event->accept(); + close(); + } +} + + +bool CEditorWorksheet::isBotNamesTable() +{ + bool status = true; + if(table_editor->horizontalHeaderItem(0)->text() != "bot name" + || table_editor->horizontalHeaderItem(1)->text() != "translated name" + || table_editor->horizontalHeaderItem(2)->text() != "sheet_name") + { + status = false; + } + + return status; +} + +bool CEditorWorksheet::isSheetTable(QString type) +{ + QString column_name; + if(type.toAscii() == Constants::WK_ITEM) + { + column_name = "item ID"; + } else if(type.toAscii() == Constants::WK_CREATURE) { + column_name = "creature ID"; + } else if(type.toAscii() == Constants::WK_SBRICK) { + column_name = "sbrick ID"; + } else if(type.toAscii() == Constants::WK_SPHRASE) { + column_name = "sphrase ID"; + } else if(type.toAscii() == Constants::WK_PLACE) { + column_name = "placeId"; + } + bool status = true; + if(table_editor->horizontalHeaderItem(0)->text() != column_name + || table_editor->horizontalHeaderItem(1)->text() != "name") + { + status = false; + } + + return status; +} + +} + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index a124188bc..0cb0602e2 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -65,6 +65,7 @@ private Q_SLOTS: void worksheetEditorChanged(QTableWidgetItem * item); void insertRow(); void deleteRow(); + void contextMenuEvent(QContextMenuEvent *e); }; @@ -123,6 +124,7 @@ public: { QTableWidgetItem* item = new QTableWidgetItem(); m_table->setItem(m_rowID, j, item); + m_table->scrollToBottom(); } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index f12c6a830..47f6f95ea 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -1,3 +1,4 @@ + // Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited // Copyright (C) 2011 Emanuel Costea @@ -123,16 +124,14 @@ void CMainWindow::createToolbar() mergeSingleFileAct->setStatusTip(tr("Merge worksheet file from local or remote directory")); connect(mergeSingleFileAct, SIGNAL(triggered()), this, SLOT(mergeSingleFile())); // Windows menu - windowMenu = new QMenu(tr("&Windows..."), _ui.toolBar); - windowMenu->setIcon(QIcon(Core::Constants::ICON_PILL)); + Core::ICore *core = Core::ICore::instance(); + Core::IMenuManager *menuManager = core->menuManager(); + windowMenu = menuManager->menuBar()->addMenu("Window"); updateWindowsList(); - _ui.toolBar->addAction(windowMenu->menuAction()); connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowsList())); // Undo, Redo actions // ----------------------------- - Core::ICore *core = Core::ICore::instance(); - Core::IMenuManager *menuManager = core->menuManager(); QAction* undoAction = menuManager->action(Core::Constants::UNDO); if (undoAction != 0) _ui.toolBar->addAction(undoAction); @@ -147,10 +146,15 @@ void CMainWindow::updateToolbar(QMdiSubWindow *window) if(_ui.mdiArea->subWindowList().size() > 0) if(QString(window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor { - QAction *insertRowAct = windowMenu->addAction("Insert new row"); - connect(insertRowAct, SIGNAL(triggered()), window, SLOT(insertRow())); - QAction *deleteRowAct = windowMenu->addAction("Delete row"); + //setContextMenuPolicy(Qt::ActionsContextMenu); + QAction *insertRowAct = new QAction("Insert new row", this); + connect(insertRowAct, SIGNAL(triggered()), window, SLOT(insertRow())); + //addAction(insertRowAct); + windowMenu->addAction(insertRowAct); + QAction *deleteRowAct = new QAction("Delete row", this); connect(deleteRowAct, SIGNAL(triggered()), window, SLOT(deleteRow())); + //addAction(deleteRowAct); + windowMenu->addAction(deleteRowAct); } } @@ -167,7 +171,7 @@ void CMainWindow::setActiveSubWindow(QWidget* window) void CMainWindow::activeSubWindowChanged() { - //TODO: nothing to be done here atm + } void CMainWindow::updateWindowsList() @@ -187,10 +191,11 @@ void CMainWindow::updateWindowsList() } else { action_text = tr("%1 %2").arg(i + 1).arg(window_file); } - QAction *action = windowMenu->addAction(action_text); + QAction *action = new QAction(action_text, this); action->setCheckable(true); action->setChecked(subWindows.at(i) == current_window); - connect(action, SIGNAL(triggered()), windowMapper, SLOT(map())); + connect(action, SIGNAL(triggered()), windowMapper, SLOT(map())); + windowMenu->addAction(action); windowMapper->setMapping(action, subWindows.at(i)); } } @@ -589,7 +594,35 @@ bool CMainWindow::isPhraseEditor(QString filename) bool CCoreListener::closeMainWindow() const { - return true; + Q_FOREACH(QMdiSubWindow *subWindow, m_MainWindow->_ui.mdiArea->subWindowList()) + { + CEditor *currentEditor = qobject_cast(subWindow); + if(subWindow->isWindowModified()) + { + QMessageBox msgBox; + msgBox.setText(tr("The document has been modified ( %1 ).").arg(currentEditor->windowFilePath())); + msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Save: + currentEditor->save(); + return true; + break; + case QMessageBox::Discard: + return true; + break; + case QMessageBox::Cancel: + return false; + break; + default: + break; + } + } + + } } } /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index c58ce9826..dfc67d1f2 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -62,8 +62,9 @@ public: CMainWindow(QWidget *parent = 0); virtual ~CMainWindow() {} QUndoStack *m_undoStack; -private: - Ui::CMainWindow _ui; +public: + Ui::CMainWindow _ui; +private: // actions QAction *openAct; QAction *saveAct; @@ -111,13 +112,20 @@ class CCoreListener : public Core::ICoreListener { Q_OBJECT public: - CCoreListener(QObject *parent = 0): ICoreListener(parent) {} - virtual ~CCoreListener() {} + CCoreListener(CMainWindow* mainWindow, QObject *parent = 0): ICoreListener(parent) + { + m_MainWindow = mainWindow; + } + virtual ~CCoreListener() {} virtual bool closeMainWindow() const; + +public: + CMainWindow *m_MainWindow; }; + } // namespace Plugin diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index caf677a0d..c98eed5be 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -53,10 +53,12 @@ bool TranslationManagerPlugin::initialize(ExtensionSystem::IPluginManager *plugi { Q_UNUSED(errorString); _plugMan = pluginManager; + // create the mainwindow + CMainWindow *mainWindow = new CMainWindow(); addAutoReleasedObject(new CTranslationManagerSettingsPage(this)); - addAutoReleasedObject(new CTranslationManagerContext(this)); - addAutoReleasedObject(new CCoreListener(this)); + addAutoReleasedObject(new CTranslationManagerContext(mainWindow, this)); + addAutoReleasedObject(new CCoreListener(mainWindow, this)); return true; } @@ -75,6 +77,8 @@ void TranslationManagerPlugin::extensionsInitialized() helpMenu->insertAction(aboutQtAction, aboutTManPlugin); } + + void TranslationManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) { #ifdef NL_OS_WINDOWS diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h index ed42b4882..8c91d64cd 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -82,9 +82,9 @@ class CTranslationManagerContext: public Core::IContext { Q_OBJECT public: - CTranslationManagerContext(QObject *parent = 0): IContext(parent) + CTranslationManagerContext(CMainWindow* mainWindow, QObject *parent = 0): IContext(parent) { - m_MainWindow = new CMainWindow(); + m_MainWindow = mainWindow; } virtual ~CTranslationManagerContext() {} From 4462383a26556e0ba80e864cf0ec3f252d781e7e Mon Sep 17 00:00:00 2001 From: sfb Date: Thu, 4 Aug 2011 09:21:38 -0500 Subject: [PATCH 065/215] reverting log plugin to rev 1660 --- .../src/plugins/log/log_plugin.cpp | 10 +++--- .../src/plugins/log/log_settings_page.cpp | 31 +++++++------------ 2 files changed, 16 insertions(+), 25 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp index 41ce39632..f31a63924 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp @@ -103,7 +103,7 @@ namespace Plugin QString CLogPlugin::name() const { - return "LogPlugin"; + return "NeL Log"; } QString CLogPlugin::version() const @@ -139,10 +139,10 @@ namespace Plugin QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup(Core::Constants::LOG_SECTION); - bool error = settings->value(Core::Constants::LOG_ERROR, true).toBool(); - bool warning = settings->value(Core::Constants::LOG_WARNING, true).toBool(); - bool debug = settings->value(Core::Constants::LOG_DEBUG, true).toBool(); - bool assert = settings->value(Core::Constants::LOG_ASSERT, true).toBool(); + bool error = settings->value(Core::Constants::LOG_ERROR, true).toBool(); + bool warning = settings->value(Core::Constants::LOG_WARNING, true).toBool(); + bool debug = settings->value(Core::Constants::LOG_DEBUG, true).toBool(); + bool assert = settings->value(Core::Constants::LOG_ASSERT, true).toBool(); bool info = settings->value(Core::Constants::LOG_INFO, true).toBool(); settings->endGroup(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_settings_page.cpp index d1e3d65fc..3aba359a5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_settings_page.cpp @@ -28,8 +28,8 @@ #include #include -namespace ExtensionSystem -{ +namespace ExtensionSystem +{ class IPluginManager; } @@ -42,10 +42,10 @@ namespace Plugin : IOptionsPage(parent), m_logPlugin(logPlugin), m_currentPage(NULL), - m_error(true), - m_warning(true), - m_debug(true), - m_assert(true), + m_error(true), + m_warning(true), + m_debug(true), + m_assert(true), m_info(true) { } @@ -99,16 +99,7 @@ namespace Plugin m_info = m_ui.infoCheck->isChecked(); writeSettings(); - ExtensionSystem::IPluginManager *p = Core::ICore::instance()->pluginManager(); - ExtensionSystem::IPluginSpec *spec = p->pluginByName("LogPlugin"); - - if(spec) - { - ExtensionSystem::IPlugin *plugin = spec->plugin(); - CLogPlugin* lp = dynamic_cast(plugin); - if (lp) - lp->setDisplayers(); - } + m_logPlugin->setDisplayers(); } void CLogSettingsPage::readSettings() @@ -116,10 +107,10 @@ namespace Plugin QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup(Core::Constants::LOG_SECTION); - m_error = settings->value(Core::Constants::LOG_ERROR, true).toBool(); - m_warning = settings->value(Core::Constants::LOG_WARNING, true).toBool(); - m_debug = settings->value(Core::Constants::LOG_DEBUG, true).toBool(); - m_assert = settings->value(Core::Constants::LOG_ASSERT, true).toBool(); + m_error = settings->value(Core::Constants::LOG_ERROR, true).toBool(); + m_warning = settings->value(Core::Constants::LOG_WARNING, true).toBool(); + m_debug = settings->value(Core::Constants::LOG_DEBUG, true).toBool(); + m_assert = settings->value(Core::Constants::LOG_ASSERT, true).toBool(); m_info = settings->value(Core::Constants::LOG_INFO, true).toBool(); settings->endGroup(); } From 5414a325256f9975b4febbf1b817b4cefc6f3316 Mon Sep 17 00:00:00 2001 From: sfb Date: Thu, 4 Aug 2011 09:41:37 -0500 Subject: [PATCH 066/215] Update: Changed log plugin plugin name to LogPlugin. --- .../tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp index f31a63924..4eb02bd31 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp @@ -103,7 +103,7 @@ namespace Plugin QString CLogPlugin::name() const { - return "NeL Log"; + return "LogPlugin"; } QString CLogPlugin::version() const From ff5da0aa2269b65abc24057b99cacb11a081be12 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Thu, 4 Aug 2011 17:04:49 +0200 Subject: [PATCH 067/215] Changed: #1304: Debug for the guild missions. Now it works ! --HG-- branch : gsoc2011-guildmissions --- .../world_editor_classes.xml | 1 + .../guild_manager/guild.cpp | 2 +- .../mission_manager/mission_action.cpp | 20 +++++++++++--- .../mission_manager/mission_event.h | 5 ++-- .../mission_manager/mission_guild.h | 4 +-- .../mission_manager/mission_step_misc.cpp | 12 ++++++++- .../player_manager/character.cpp | 26 +++++++++++++++++-- .../mission_compiler_lib/step_content.cpp | 23 ++++++++++++---- 8 files changed, 76 insertions(+), 17 deletions(-) diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml index 7faaf1fcf..5426361fa 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/world_editor_classes.xml @@ -707,6 +707,7 @@ + diff --git a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp index 2460eb530..a93bdaeb1 100644 --- a/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp +++ b/code/ryzom/server/src/entities_game_service/guild_manager/guild.cpp @@ -795,7 +795,7 @@ bool CGuild::processGuildMissionStepEvent(std::list< CMissionEvent*> & eventList return false; } // I don't know if i should pass _EId to this function - CMissionEvent::TResult result = mission->processEvent( TheDataset.getDataSetRow( _EId) ,eventList,stepIndex ); + CMissionEvent::TResult result = mission->processEvent(TheDataset.getDataSetRow(getHighestGradeOnlineUser()) /*TheDataset.getDataSetRow( _EId)*/ ,eventList,stepIndex ); if ( result == CMissionEvent::Nothing ) return false; else if ( result == CMissionEvent::MissionFailed ) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp index bfb7c8d5a..31cf1ff0f 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_action.cpp @@ -4053,15 +4053,16 @@ protected: TAIAlias Mission; TAIAlias NPCOwner; // NPC giver the mission have to be attached at spawn time + bool Guild; protected: bool buildAction ( uint32 line, const std::vector< std::string > & script, CMissionGlobalParsingData & globalData, CMissionSpecificParsingData & missionData) { _SourceLine = line; - if ( script.size() != 3 ) + if ( script.size() != 3 && script.size() != 4) { - MISLOGSYNTAXERROR(" : "); + MISLOGSYNTAXERROR(" : [: guild]"); return false; } string name = CMissionParser::getNoBlankString( script[1] ); @@ -4094,6 +4095,17 @@ protected: if (vRet.size() > 0) NPCOwner = vRet[0]; + // We check for the guild option + Guild = false; + for (std::vector< std::string >::const_iterator it = script.begin(); it != script.end(); ++it) + { + if (CMissionParser::getNoBlankString(*it) == "guild") + { + Guild = true; + break; + } + } + return true; } @@ -4104,13 +4116,13 @@ protected: { CAIAliasTranslator::getInstance()->getNPCNameFromAlias(instance->getGiver(), sDebugBotName); nlassert(instance); - CMissionEventAddMission * event = new CMissionEventAddMission( instance->getGiver(), Mission, mainMission ); + CMissionEventAddMission * event = new CMissionEventAddMission( instance->getGiver(), Mission, mainMission, Guild ); eventList.push_back( event ); } else { CAIAliasTranslator::getInstance()->getNPCNameFromAlias(NPCOwner, sDebugBotName); - CMissionEventAddMission * event = new CMissionEventAddMission( NPCOwner, Mission, mainMission ); + CMissionEventAddMission * event = new CMissionEventAddMission( NPCOwner, Mission, mainMission, Guild ); eventList.push_back( event ); } LOGMISSIONACTION("spawn_mission bot:" + sDebugBotName + " newmiss:" + CPrimitivesParser::aliasToString(Mission) diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h b/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h index f4aa2ffea..b32599a8a 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_event.h @@ -416,11 +416,12 @@ protected: class CMissionEventAddMission: public CMissionEvent { public: - CMissionEventAddMission( TAIAlias giver, TAIAlias mission, TAIAlias mainMission ) - :CMissionEvent(AddMission, TDataSetRow()) ,Giver(giver),Mission(mission),MainMission(mainMission) {} + CMissionEventAddMission( TAIAlias giver, TAIAlias mission, TAIAlias mainMission, bool guild ) + :CMissionEvent(AddMission, TDataSetRow()) ,Giver(giver),Mission(mission),MainMission(mainMission), Guild(guild) {} TAIAlias Mission; TAIAlias Giver; TAIAlias MainMission; + bool Guild; protected: friend class CMissionEvent; bool buildFromScript( const std::vector< std::string > & script ,NLMISC::CLog& log){return false;} diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.h b/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.h index 76cd0cb6a..b8635d1d4 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.h +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_guild.h @@ -32,7 +32,7 @@ public: CMissionGuild() : _Chained(false) { } - inline void setGuild( uint16 guildId ); + inline void setGuild( uint32 guildId ); /// override void updateUsersJournalEntry(); /// override @@ -59,7 +59,7 @@ private: bool _Chained; }; -void CMissionGuild::setGuild( uint16 guildId ) +void CMissionGuild::setGuild( uint32 guildId ) { _GuildId = guildId; } diff --git a/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp b/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp index dc0be33d5..03ad33b28 100644 --- a/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp +++ b/code/ryzom/server/src/entities_game_service/mission_manager/mission_step_misc.cpp @@ -1165,7 +1165,17 @@ class CMissionStepDoMissions : public IMissionStepTemplate for ( uint i = 0; i < subs.size(); i++ ) { std::vector< std::string > params; - NLMISC::splitString( subs[i]," \t", params ); + //NLMISC::splitString( subs[i]," \t", params ); + subs[i] = CMissionParser::getNoBlankString(subs[i]); + std::size_t pos = subs[i].find_first_of(" \t"); + std::string str = subs[i].substr(0, pos); + params.push_back(str); + if (pos != std::string::npos) + str = subs[i].substr(pos + 1); + else + str = ""; + params.push_back(str); + //std::size_t pos = _Missions[i].find_first_of(" \t"); _Missions[i].Mission = CMissionParser::getNoBlankString( params[0] ); if (params.size() > 1) NLMISC::fromString(params[1], _Missions[i].NbNeedCompletion); diff --git a/code/ryzom/server/src/entities_game_service/player_manager/character.cpp b/code/ryzom/server/src/entities_game_service/player_manager/character.cpp index b70eb3b88..2c0e38f80 100644 --- a/code/ryzom/server/src/entities_game_service/player_manager/character.cpp +++ b/code/ryzom/server/src/entities_game_service/player_manager/character.cpp @@ -11641,11 +11641,33 @@ bool CCharacter::processMissionEventList( std::list< CMissionEvent* > & eventLis TAIAlias mission = eventSpe.Mission; TAIAlias giver = eventSpe.Giver; TAIAlias mainMission = eventSpe.MainMission; + bool missionForGuild = eventSpe.Guild; // add mission event are always allocated on heap delete ( CMissionEvent *) ( eventList.front() ); eventList.pop_front(); - CMissionManager::getInstance()->instanciateMission(this, mission, giver ,eventList, mainMission); + + // If the mission is not for guild members we just instanciate it + if (!missionForGuild) + CMissionManager::getInstance()->instanciateMission(this, mission, giver ,eventList, mainMission); + else + { + // We find the guild and each guild members and we instanciate the mission for them + if (guild) + { + for ( std::map::iterator it = guild->getMembersBegin(); + it != guild->getMembersEnd();++it ) + { + CCharacter * guildUser = PlayerManager.getChar( it->first ); + if ( !guildUser ) + { + nlwarning( "cant find user %s", it->first.toString().c_str() ); + continue; + } + CMissionManager::getInstance()->instanciateMission(guildUser, mission, giver ,eventList, mainMission); + } + } + } } // event may have been processed during instanciateMission if ( eventList.empty() ) @@ -11667,7 +11689,7 @@ bool CCharacter::processMissionEventList( std::list< CMissionEvent* > & eventLis } // THIRD - Check with guild missions (if event not already processed and char belongs to a guild) - if (!eventProcessed && (event.Restriction != CMissionEvent::NoGroup)) + if (!eventProcessed)// && (event.Restriction != CMissionEvent::NoGroup)) { if (guild != NULL) eventProcessed = guild->processGuildMissionEvent(eventList, alias); diff --git a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp index dcd8bb9b3..4b8bce784 100644 --- a/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp +++ b/code/ryzom/tools/leveldesign/mission_compiler_lib/step_content.cpp @@ -254,6 +254,7 @@ class CActionSpawnMission : public IStepContent protected: string _MissionName; string _GiverName; + bool _Guild; private: void getPredefParam(uint32 &numEntry, CPhrase::TPredefParams &predef) { @@ -268,17 +269,29 @@ public: if (_GiverName.empty()) { throw EParseException(prim, "giver_name is empty !"); - } + } + + _Guild = md.getProperty(prim, "guild", false, true) == "true"; + // Check: if _Guild is true then check if we are in a guild mission + if (_Guild && !md.isGuildMission()) + { + string err = toString("primitive(%s): 'guild' option true 1 for non guild mission.", prim->getName().c_str()); + throw EParseException(prim, err.c_str()); + } } string genCode(CMissionData &md) { + string ret = ""; if (!_MissionName.empty()) - return "spawn_mission : " + _MissionName + " : " + _GiverName + NL; - else - return string(); + { + ret = "spawn_mission : " + _MissionName + " : " + _GiverName; + if (_Guild) + ret += " : guild"; + ret += NL; + } + return ret; } - }; REGISTER_STEP_CONTENT(CActionSpawnMission, "spawn_mission"); From 2cd15276560bf62cfb97e92840768cf55e7a943a Mon Sep 17 00:00:00 2001 From: cemycc Date: Tue, 9 Aug 2011 01:55:46 +0300 Subject: [PATCH 068/215] Changed: #1307 Added undo/redo for editor and a little syntaxhighliter --HG-- branch : gsoc2011-translationovqt --- .../src/plugins/CMakeLists.txt | 5 +- .../src/plugins/core/core_constants.h | 3 +- .../translation_manager/editor_phrase.cpp | 38 +++- .../translation_manager/editor_phrase.h | 178 +++++++++++++++++- .../translation_manager_main_window.cpp | 53 ++++-- .../translation_manager_main_window.h | 1 + 6 files changed, 248 insertions(+), 30 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt index 99df2d874..6db50dac3 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt @@ -7,11 +7,8 @@ ADD_SUBDIRECTORY(disp_sheet_id) ADD_SUBDIRECTORY(object_viewer) ADD_SUBDIRECTORY(zone_painter) ADD_SUBDIRECTORY(georges_editor) -<<<<<<< local ADD_SUBDIRECTORY(translation_manager) -======= - # Ryzom Specific Plugins IF(WITH_RYZOM AND WITH_RYZOM_TOOLS) ADD_SUBDIRECTORY(mission_compiler) -ENDIF(WITH_RYZOM AND WITH_RYZOM_TOOLS)>>>>>>> other +ENDIF(WITH_RYZOM AND WITH_RYZOM_TOOLS) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h index 389b9a9fa..cfe6b7e80 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h @@ -91,9 +91,8 @@ const char * const DATA_PATH_SECTION = "DataPath"; const char * const SEARCH_PATHS = "SearchPaths"; const char * const RECURSIVE_SEARCH_PATHS = "RecursiveSearchPathes"; const char * const LEVELDESIGN_PATH = "LevelDesignPath"; -const char * const PRIMITIVES_PATH = "D:/Ryzom/ryzom/code/ryzom/common/data_leveldesign/primitives"; -const char * const ASSETS_PATH = "AssetsPath"; const char * const PRIMITIVES_PATH = "PrimitivesPath"; +const char * const ASSETS_PATH = "AssetsPath"; const char * const LIGOCONFIG_FILE = "LigoConfigFile"; const char * const REMAP_EXTENSIONS = "RemapExtensions"; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp index b202b1cbb..2e5a576ab 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp @@ -24,6 +24,10 @@ #include #include #include +#include +#include +#include + // Project includes #include "editor_phrase.h" @@ -38,19 +42,24 @@ void CEditorPhrase::open(QString filename) vector phrases; if(readPhraseFile(filename.toStdString(), phrases, false)) { - text_edit = new QTextEdit(); + text_edit = new QTextEdit(this); + SyntaxHighlighter *highlighter = new SyntaxHighlighter(text_edit); + text_edit->setUndoRedoEnabled(true); + text_edit->document()->setUndoRedoEnabled(true); + setWidget(text_edit); // read the file content QFile file(filename); - QTextStream in(&file); - // set the file content to the text edit - QString content = in.readAll(); - text_edit->setText(content); + file.open(QIODevice::ReadOnly | QIODevice::Text); + // set the file content to the text edit + QByteArray data = file.readAll(); + text_edit->append(data); // window settings setCurrentFile(filename); setAttribute(Qt::WA_DeleteOnClose); - setWidget(text_edit); editor_type = Constants::ED_PHRASE; current_file = filename; + connect(text_edit->document(), SIGNAL(contentsChange(int, int, int)), this, SLOT(contentsChangeNow(int position, int charsRemoved, int charsAdded))); + connect(text_edit->document(), SIGNAL(contentsChanged()), this, SLOT(docContentsChanged())); } else { QErrorMessage error; error.showMessage("This file is not a phrase file."); @@ -58,6 +67,19 @@ void CEditorPhrase::open(QString filename) } } +void CEditorPhrase::contentsChangeNow(int position, int charsRemoved, int charsAdded) +{ + if(charsRemoved > 0) + current_stack->push(new CUndoPhraseRemoveCommand(position-charsRemoved, charsRemoved, text_edit)); + else if(charsAdded > 0) + current_stack->push(new CUndoPhraseInsertCommand(position, text_edit->toPlainText().right(charsAdded), text_edit)); +} + +void CEditorPhrase::docContentsChanged() +{ + setWindowModified(true); +} + void CEditorPhrase::activateWindow() { showMaximized(); @@ -66,6 +88,7 @@ void CEditorPhrase::activateWindow() void CEditorPhrase::save() { QFile file(current_file); + file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out<toPlainText(); setCurrentFile(current_file); @@ -74,12 +97,15 @@ void CEditorPhrase::save() void CEditorPhrase::saveAs(QString filename) { QFile file(filename); + file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out<toPlainText(); current_file = filename; setCurrentFile(current_file); } + + void CEditorPhrase::closeEvent(QCloseEvent *event) { if(isWindowModified()) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h index 21152ad16..c792d0b21 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h @@ -28,6 +28,7 @@ #include #include #include +#include // Project includes #include "translation_manager_editor.h" @@ -37,7 +38,7 @@ namespace Plugin { class CEditorPhrase : public CEditor { Q_OBJECT -private: +public: QTextEdit *text_edit; public: CEditorPhrase(QMdiArea* parent) : CEditor(parent) {} @@ -47,6 +48,181 @@ public: void saveAs(QString filename); void activateWindow(); void closeEvent(QCloseEvent *event); +public Q_SLOTS: + void contentsChangeNow(int, int, int); + void docContentsChanged(); + +}; + +class CUndoPhraseInsertCommand : public QUndoCommand +{ +public: + CUndoPhraseInsertCommand(int index, const QString &chars, QTextEdit *document, QUndoCommand *parent = 0) : QUndoCommand("Insert characters", parent), + m_index(index), + m_chars(chars), + m_document(document) + { } + + virtual void redo() + { + QString text = m_document->toPlainText(); + text.insert(m_index, m_chars); + m_document->clear(); + m_document->setPlainText(text); + } + + virtual void undo() + { + QString text = m_document->toPlainText(); + text.remove(m_index, m_chars.length()); + m_document->clear(); + m_document->setPlainText(text); + m_document->undo(); + } + +private: + int m_index; + QString m_chars; + QTextEdit* m_document; + +}; + +class CUndoPhraseRemoveCommand : public QUndoCommand +{ +public: + CUndoPhraseRemoveCommand(int index, int count, QTextEdit *document, QUndoCommand *parent = 0) : QUndoCommand("Remove characters", parent), + m_index(index), + m_count(count), + m_document(document) + { } + + virtual void redo() + { + QString text = m_document->toPlainText(); + m_removedChars = text.mid(m_index, m_count); + text.remove(m_index, m_count); + m_document->clear(); + m_document->setPlainText(text); + } + + virtual void undo() + { + QString text = m_document->toPlainText(); + text.insert(m_index, m_removedChars); + m_document->clear(); + m_document->setPlainText(text); + } +private: + int m_index; + int m_count; + QString m_removedChars; + QTextEdit* m_document; + +}; + +class SyntaxHighlighter : public QSyntaxHighlighter +{ +public: + SyntaxHighlighter(QTextEdit *parent) : QSyntaxHighlighter(parent) + { + HighlightingRule rule; + + keywordFormat.setForeground(Qt::darkBlue); + keywordFormat.setFontWeight(QFont::Bold); + QStringList keywordPatterns; + keywordPatterns << "\\bchar\\b" << "\\bclass\\b" << "\\bconst\\b" + << "\\bdouble\\b" << "\\benum\\b" << "\\bexplicit\\b" + << "\\bfriend\\b" << "\\binline\\b" << "\\bint\\b" + << "\\blong\\b" << "\\bnamespace\\b" << "\\boperator\\b" + << "\\bprivate\\b" << "\\bprotected\\b" << "\\bpublic\\b" + << "\\bshort\\b" << "\\bsignals\\b" << "\\bsigned\\b" + << "\\bslots\\b" << "\\bstatic\\b" << "\\bstruct\\b" + << "\\btemplate\\b" << "\\btypedef\\b" << "\\btypename\\b" + << "\\bunion\\b" << "\\bunsigned\\b" << "\\bvirtual\\b" + << "\\bvoid\\b" << "\\bvolatile\\b"; + Q_FOREACH(const QString &pattern, keywordPatterns) { + rule.pattern = QRegExp(pattern); + rule.format = keywordFormat; + highlightingRules.append(rule); + } + + classFormat.setFontWeight(QFont::Bold); + classFormat.setForeground(Qt::darkMagenta); + rule.pattern = QRegExp("\\bQ[A-Za-z]+\\b"); + rule.format = classFormat; + highlightingRules.append(rule); + + singleLineCommentFormat.setForeground(Qt::red); + rule.pattern = QRegExp("//[^\n]*"); + rule.format = singleLineCommentFormat; + highlightingRules.append(rule); + + multiLineCommentFormat.setForeground(Qt::red); + + quotationFormat.setForeground(Qt::darkGreen); + rule.pattern = QRegExp("\".*\""); + rule.format = quotationFormat; + highlightingRules.append(rule); + + functionFormat.setFontItalic(true); + functionFormat.setForeground(Qt::blue); + rule.pattern = QRegExp("\\b[A-Za-z0-9_]+(?=\\()"); + rule.format = functionFormat; + highlightingRules.append(rule); + + commentStartExpression = QRegExp("/\\*"); + commentEndExpression = QRegExp("\\*/"); + } + + void highlightBlock(const QString &text) + { + Q_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); + + int startIndex = 0; + if (previousBlockState() != 1) + startIndex = commentStartExpression.indexIn(text); + + while (startIndex >= 0) { + int endIndex = commentEndExpression.indexIn(text, startIndex); + int commentLength; + if (endIndex == -1) { + setCurrentBlockState(1); + commentLength = text.length() - startIndex; + } else { + commentLength = endIndex - startIndex + + commentEndExpression.matchedLength(); + } + setFormat(startIndex, commentLength, multiLineCommentFormat); + startIndex = commentStartExpression.indexIn(text, startIndex + commentLength); + } + } + + private: + struct HighlightingRule + { + QRegExp pattern; + QTextCharFormat format; + }; + QVector highlightingRules; + + QRegExp commentStartExpression; + QRegExp commentEndExpression; + + QTextCharFormat keywordFormat; + QTextCharFormat classFormat; + QTextCharFormat singleLineCommentFormat; + QTextCharFormat multiLineCommentFormat; + QTextCharFormat quotationFormat; + QTextCharFormat functionFormat; }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 47f6f95ea..efa7743cd 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -221,7 +221,7 @@ void CMainWindow::open() new_window->setUndoStack(m_undoStack); new_window->open(file_name); new_window->activateWindow(); - } + } // phrase editor if(isPhraseEditor(file_name)) { @@ -592,8 +592,33 @@ bool CMainWindow::isPhraseEditor(QString filename) } } +/* void CMainWindow::keyPressEvent(QKeyEvent *event) +{ + CEditorPhrase* editor = qobject_cast(_ui.mdiArea->currentSubWindow()); + + QString chars = event->text(); + int index = editor->text_edit->textCursor().position(); + + switch (event->key()) + { + case Qt::Key_Backspace: + if (index > 0) + m_undoStack->push(new CUndoPhraseRemoveCommand(index--, 1, editor->text_edit)); + break; + case Qt::Key_Delete: + if (index < editor->text_edit->toPlainText().length()) + m_undoStack->push(new CUndoPhraseRemoveCommand(index, 1, editor->text_edit)); + break; + default: + if (!chars.isEmpty()) + m_undoStack->push(new CUndoPhraseInsertCommand(index, chars, editor->text_edit)); + break; + } +} */ + bool CCoreListener::closeMainWindow() const { + bool okToClose = true; Q_FOREACH(QMdiSubWindow *subWindow, m_MainWindow->_ui.mdiArea->subWindowList()) { CEditor *currentEditor = qobject_cast(subWindow); @@ -605,24 +630,18 @@ bool CCoreListener::closeMainWindow() const msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Save); int ret = msgBox.exec(); - switch (ret) - { - case QMessageBox::Save: - currentEditor->save(); - return true; - break; - case QMessageBox::Discard: - return true; - break; - case QMessageBox::Cancel: - return false; - break; - default: - break; - } + if(ret == QMessageBox::Save) + { + currentEditor->save(); + } + else if(ret == QMessageBox::Cancel) + { + okToClose = false; + break; + } } - } + return okToClose; } } /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index dfc67d1f2..02146a9dd 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -104,6 +104,7 @@ private: CEditorWorksheet* getEditorByWorksheetType(const QString &type); bool isWorksheetEditor(QString filename); bool isPhraseEditor(QString filename); + }; From bbc33e2bf91daf21921b9cb927464be912ad3826 Mon Sep 17 00:00:00 2001 From: kervala Date: Thu, 11 Aug 2011 10:09:30 +0200 Subject: [PATCH 069/215] Changed: Remove libwww warnings under Windows --- code/CMakeModules/FindLibwww.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/CMakeModules/FindLibwww.cmake b/code/CMakeModules/FindLibwww.cmake index 8f1c1c02a..b594b3140 100644 --- a/code/CMakeModules/FindLibwww.cmake +++ b/code/CMakeModules/FindLibwww.cmake @@ -65,9 +65,9 @@ MACRO(FIND_WWW_LIBRARY MYLIBRARY OPTION) SET(LIBWWW_LIBRARIES ${LIBWWW_LIBRARIES} ${${MYLIBRARY}}) ENDIF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC) ELSE(${MYLIBRARY}) - IF(NOT LIBWWW_FIND_QUIETLY) + IF(NOT LIBWWW_FIND_QUIETLY AND NOT WIN32) MESSAGE(STATUS "Warning: Libwww: Library not found: ${MYLIBRARY}") - ENDIF(NOT LIBWWW_FIND_QUIETLY) + ENDIF(NOT LIBWWW_FIND_QUIETLY AND NOT WIN32) ENDIF(${MYLIBRARY}) MARK_AS_ADVANCED(${MYLIBRARY}) From a983374b2ab609a42d991512ae80a9d4889c58ce Mon Sep 17 00:00:00 2001 From: cemycc Date: Thu, 11 Aug 2011 19:16:46 +0300 Subject: [PATCH 070/215] Changed: #1307 Added tab view on QMdiArea, new syntax highliter and added base functions for Undo/Redo framework --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_phrase.cpp | 33 ++++++++---- .../translation_manager/editor_phrase.h | 52 +++++++++---------- .../translation_manager/editor_worksheet.cpp | 2 +- .../translation_manager/editor_worksheet.h | 2 +- .../translation_manager/extract_bot_names.cpp | 2 +- .../translation_manager/extract_bot_names.h | 2 +- .../extract_new_sheet_names.cpp | 3 +- .../extract_new_sheet_names.h | 3 +- .../translation_manager/ftp_selection.cpp | 2 +- .../translation_manager/ftp_selection.h | 2 +- .../translation_manager/source_selection.cpp | 2 +- .../translation_manager/source_selection.h | 2 +- .../translation_manager_constants.h | 2 +- .../translation_manager_editor.h | 2 +- .../translation_manager_main_window.cpp | 26 +--------- .../translation_manager_main_window.h | 2 +- .../translation_manager_main_window.ui | 9 +++- .../translation_manager_plugin.cpp | 5 +- .../translation_manager_plugin.h | 2 +- .../translation_manager_settings_page.cpp | 2 +- .../translation_manager_settings_page.h | 2 +- 21 files changed, 79 insertions(+), 80 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp index 2e5a576ab..081dc455b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp @@ -35,14 +35,15 @@ using namespace std; -namespace Plugin { +namespace TranslationManager { void CEditorPhrase::open(QString filename) { vector phrases; if(readPhraseFile(filename.toStdString(), phrases, false)) { - text_edit = new QTextEdit(this); + text_edit = new CTextEdit(this); + text_edit->setUndoStack(current_stack); SyntaxHighlighter *highlighter = new SyntaxHighlighter(text_edit); text_edit->setUndoRedoEnabled(true); text_edit->document()->setUndoRedoEnabled(true); @@ -58,7 +59,6 @@ void CEditorPhrase::open(QString filename) setAttribute(Qt::WA_DeleteOnClose); editor_type = Constants::ED_PHRASE; current_file = filename; - connect(text_edit->document(), SIGNAL(contentsChange(int, int, int)), this, SLOT(contentsChangeNow(int position, int charsRemoved, int charsAdded))); connect(text_edit->document(), SIGNAL(contentsChanged()), this, SLOT(docContentsChanged())); } else { QErrorMessage error; @@ -67,13 +67,28 @@ void CEditorPhrase::open(QString filename) } } -void CEditorPhrase::contentsChangeNow(int position, int charsRemoved, int charsAdded) +/* void CTextEdit::keyPressEvent(QKeyEvent *event) { - if(charsRemoved > 0) - current_stack->push(new CUndoPhraseRemoveCommand(position-charsRemoved, charsRemoved, text_edit)); - else if(charsAdded > 0) - current_stack->push(new CUndoPhraseInsertCommand(position, text_edit->toPlainText().right(charsAdded), text_edit)); -} + QString chars = event->text(); + int index = textCursor().position(); + + switch(event->key()) + { + case Qt::Key_Backspace: + if (index > 0) + m_undoStack->push(new CUndoPhraseRemoveCommand(index--, 1, this)); + break; + case Qt::Key_Delete: + if (index < toPlainText().length()) + m_undoStack->push(new CUndoPhraseRemoveCommand(index, 1, this)); + break; + default: + if (!chars.isEmpty()) + m_undoStack->push(new CUndoPhraseInsertCommand(index, chars, this)); + break; + } + +} */ void CEditorPhrase::docContentsChanged() { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h index c792d0b21..d542b6e34 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h @@ -29,17 +29,33 @@ #include #include #include +#include +#include // Project includes #include "translation_manager_editor.h" -namespace Plugin { +namespace TranslationManager { + +class CTextEdit : public QTextEdit +{ + Q_OBJECT +private: + QUndoStack* m_undoStack; +public: + CTextEdit(QWidget* parent = 0) : QTextEdit(parent) { } + //void keyPressEvent(QKeyEvent *event); + void setUndoStack(QUndoStack* undoStack) + { + m_undoStack = undoStack; + } +}; class CEditorPhrase : public CEditor { Q_OBJECT public: - QTextEdit *text_edit; + CTextEdit *text_edit; public: CEditorPhrase(QMdiArea* parent) : CEditor(parent) {} CEditorPhrase() : CEditor() {} @@ -49,7 +65,6 @@ public: void activateWindow(); void closeEvent(QCloseEvent *event); public Q_SLOTS: - void contentsChangeNow(int, int, int); void docContentsChanged(); }; @@ -127,30 +142,12 @@ public: { HighlightingRule rule; - keywordFormat.setForeground(Qt::darkBlue); - keywordFormat.setFontWeight(QFont::Bold); - QStringList keywordPatterns; - keywordPatterns << "\\bchar\\b" << "\\bclass\\b" << "\\bconst\\b" - << "\\bdouble\\b" << "\\benum\\b" << "\\bexplicit\\b" - << "\\bfriend\\b" << "\\binline\\b" << "\\bint\\b" - << "\\blong\\b" << "\\bnamespace\\b" << "\\boperator\\b" - << "\\bprivate\\b" << "\\bprotected\\b" << "\\bpublic\\b" - << "\\bshort\\b" << "\\bsignals\\b" << "\\bsigned\\b" - << "\\bslots\\b" << "\\bstatic\\b" << "\\bstruct\\b" - << "\\btemplate\\b" << "\\btypedef\\b" << "\\btypename\\b" - << "\\bunion\\b" << "\\bunsigned\\b" << "\\bvirtual\\b" - << "\\bvoid\\b" << "\\bvolatile\\b"; - Q_FOREACH(const QString &pattern, keywordPatterns) { - rule.pattern = QRegExp(pattern); - rule.format = keywordFormat; - highlightingRules.append(rule); - } + translateStringFormat.setFontWeight(QFont::Bold); + translateStringFormat.setForeground(Qt::darkMagenta); + rule.pattern = QRegExp("\\[.+\\]"); + rule.format = translateStringFormat; + highlightingRules.append(rule); - classFormat.setFontWeight(QFont::Bold); - classFormat.setForeground(Qt::darkMagenta); - rule.pattern = QRegExp("\\bQ[A-Za-z]+\\b"); - rule.format = classFormat; - highlightingRules.append(rule); singleLineCommentFormat.setForeground(Qt::red); rule.pattern = QRegExp("//[^\n]*"); @@ -166,7 +163,7 @@ public: functionFormat.setFontItalic(true); functionFormat.setForeground(Qt::blue); - rule.pattern = QRegExp("\\b[A-Za-z0-9_]+(?=\\()"); + rule.pattern = QRegExp("\\(.+\\)"); rule.format = functionFormat; highlightingRules.append(rule); @@ -223,6 +220,7 @@ public: QTextCharFormat multiLineCommentFormat; QTextCharFormat quotationFormat; QTextCharFormat functionFormat; + QTextCharFormat translateStringFormat; }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index a6ef28d36..ce02837c8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -32,7 +32,7 @@ using namespace std; -namespace Plugin { +namespace TranslationManager { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 0cb0602e2..839601047 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -38,7 +38,7 @@ #include "translation_manager_editor.h" #include "extract_new_sheet_names.h" -namespace Plugin { +namespace TranslationManager { class CEditorWorksheet : public CEditor { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp index 4de3d889d..e8ed68bbf 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp @@ -23,7 +23,7 @@ static bool RemoveOlds = false; -namespace Plugin +namespace TranslationManager { TCreatureInfo *ExtractBotNames::getCreature(const std::string &sheetName) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h index 9c4ea51d2..df1cb39ca 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h @@ -35,7 +35,7 @@ using namespace NLMISC; using namespace NLLIGO; using namespace STRING_MANAGER; -namespace Plugin +namespace TranslationManager { struct TCreatureInfo diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp index b9411c74e..5d0b9b455 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp @@ -21,7 +21,8 @@ using namespace NLMISC; using namespace NLLIGO; using namespace STRING_MANAGER; -namespace Plugin { +namespace TranslationManager +{ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h index 8c9151e2e..ca7295f91 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h @@ -35,7 +35,8 @@ using namespace NLMISC; using namespace NLLIGO; using namespace STRING_MANAGER; -namespace Plugin { +namespace TranslationManager +{ // *************************************************************************** diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp index 68abd456f..b3f637b80 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp @@ -3,7 +3,7 @@ #include #include -namespace Plugin +namespace TranslationManager { CFtpSelection::CFtpSelection(QWidget *parent): QDialog(parent) { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h index 64ea136c4..7a90271a9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h @@ -20,7 +20,7 @@ using namespace std; -namespace Plugin { +namespace TranslationManager { class CFtpSelection : public QDialog { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp index cf38ed589..18c49f3e3 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp @@ -3,7 +3,7 @@ #include "source_selection.h" -namespace Plugin +namespace TranslationManager { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h index ab0100fc3..99636d410 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h @@ -12,7 +12,7 @@ using namespace std; -namespace Plugin +namespace TranslationManager { class CSourceDialog : public QDialog diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h index 3cb66181b..dcb91676b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h @@ -8,7 +8,7 @@ #ifndef TRANSLATION_MANAGER_CONSTANTS_H #define TRANSLATION_MANAGER_CONSTANTS_H -namespace Plugin +namespace TranslationManager { namespace Constants { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h index 39ef74f2a..bc46769c1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h @@ -25,7 +25,7 @@ #include #include -namespace Plugin { +namespace TranslationManager { class CEditor : public QMdiSubWindow { Q_OBJECT diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index efa7743cd..5856f9c7d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -44,7 +44,7 @@ #include "ftp_selection.h" -namespace Plugin +namespace TranslationManager { CMainWindow::CMainWindow(QWidget *parent) @@ -592,30 +592,6 @@ bool CMainWindow::isPhraseEditor(QString filename) } } -/* void CMainWindow::keyPressEvent(QKeyEvent *event) -{ - CEditorPhrase* editor = qobject_cast(_ui.mdiArea->currentSubWindow()); - - QString chars = event->text(); - int index = editor->text_edit->textCursor().position(); - - switch (event->key()) - { - case Qt::Key_Backspace: - if (index > 0) - m_undoStack->push(new CUndoPhraseRemoveCommand(index--, 1, editor->text_edit)); - break; - case Qt::Key_Delete: - if (index < editor->text_edit->toPlainText().length()) - m_undoStack->push(new CUndoPhraseRemoveCommand(index, 1, editor->text_edit)); - break; - default: - if (!chars.isEmpty()) - m_undoStack->push(new CUndoPhraseInsertCommand(index, chars, editor->text_edit)); - break; - } -} */ - bool CCoreListener::closeMainWindow() const { bool okToClose = true; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 02146a9dd..6b9822031 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -52,7 +52,7 @@ class QWidget; using namespace std; -namespace Plugin +namespace TranslationManager { class CMainWindow : public QMainWindow diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui index 71c139e0a..6f2cd2815 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.ui @@ -18,7 +18,14 @@ - + + + QMdiArea::TabbedView + + + true + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index c98eed5be..3efbdd0e8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -37,7 +37,7 @@ #include #include -namespace Plugin +namespace TranslationManager { TranslationManagerPlugin::~TranslationManagerPlugin() { @@ -137,8 +137,9 @@ ExtensionSystem::IPluginSpec *TranslationManagerPlugin::pluginByName(const QStri if (spec->name() == name) return spec; return 0; + } } -Q_EXPORT_PLUGIN(Plugin::TranslationManagerPlugin) +Q_EXPORT_PLUGIN(TranslationManager::TranslationManagerPlugin) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h index 8c91d64cd..94c75d628 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -42,7 +42,7 @@ namespace ExtensionSystem class IPluginSpec; } -namespace Plugin +namespace TranslationManager { class CTranslationManagerContext; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp index fbd713ac6..09588da7d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp @@ -28,7 +28,7 @@ // Project includes #include "../core/icore.h" -namespace Plugin +namespace TranslationManager { QString lastDir = "."; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h index 1cf19787a..34caea5e1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h @@ -26,7 +26,7 @@ class QWidget; -namespace Plugin +namespace TranslationManager { /** @class CTranslationManagerSettingsPage From ba7dfeb9072c4acf296f2bb3bc7da5732199ecd4 Mon Sep 17 00:00:00 2001 From: cemycc Date: Fri, 12 Aug 2011 15:00:22 +0300 Subject: [PATCH 071/215] Changed: #1307 Added undo/redo for Phrase Editor. Works great --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_phrase.cpp | 6 ++ .../translation_manager/editor_phrase.h | 71 +++++-------------- 2 files changed, 22 insertions(+), 55 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp index 081dc455b..6033ecb38 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp @@ -60,6 +60,7 @@ void CEditorPhrase::open(QString filename) editor_type = Constants::ED_PHRASE; current_file = filename; connect(text_edit->document(), SIGNAL(contentsChanged()), this, SLOT(docContentsChanged())); + connect(text_edit->document(), SIGNAL(undoCommandAdded()), this, SLOT(newUndoCommandAdded())); } else { QErrorMessage error; error.showMessage("This file is not a phrase file."); @@ -67,6 +68,11 @@ void CEditorPhrase::open(QString filename) } } +void CEditorPhrase::newUndoCommandAdded() +{ + current_stack->push(new CUndoPhraseNewCommand(text_edit)); +} + /* void CTextEdit::keyPressEvent(QKeyEvent *event) { QString chars = event->text(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h index d542b6e34..f26dec73e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h @@ -43,7 +43,10 @@ class CTextEdit : public QTextEdit private: QUndoStack* m_undoStack; public: - CTextEdit(QWidget* parent = 0) : QTextEdit(parent) { } + CTextEdit(QWidget* parent = 0) : QTextEdit(parent) + { + setUndoRedoEnabled(true); + } //void keyPressEvent(QKeyEvent *event); void setUndoStack(QUndoStack* undoStack) { @@ -66,73 +69,31 @@ public: void closeEvent(QCloseEvent *event); public Q_SLOTS: void docContentsChanged(); + void newUndoCommandAdded(); }; -class CUndoPhraseInsertCommand : public QUndoCommand +class CUndoPhraseNewCommand : public QUndoCommand { public: - CUndoPhraseInsertCommand(int index, const QString &chars, QTextEdit *document, QUndoCommand *parent = 0) : QUndoCommand("Insert characters", parent), - m_index(index), - m_chars(chars), - m_document(document) - { } - - virtual void redo() - { - QString text = m_document->toPlainText(); - text.insert(m_index, m_chars); - m_document->clear(); - m_document->setPlainText(text); - } - - virtual void undo() - { - QString text = m_document->toPlainText(); - text.remove(m_index, m_chars.length()); - m_document->clear(); - m_document->setPlainText(text); - m_document->undo(); - } - -private: - int m_index; - QString m_chars; - QTextEdit* m_document; - -}; - -class CUndoPhraseRemoveCommand : public QUndoCommand -{ -public: - CUndoPhraseRemoveCommand(int index, int count, QTextEdit *document, QUndoCommand *parent = 0) : QUndoCommand("Remove characters", parent), - m_index(index), - m_count(count), - m_document(document) + CUndoPhraseNewCommand(CTextEdit *textEdit, QUndoCommand *parent = 0) + : QUndoCommand("Inserting/Removing characters", parent), + m_textEdit(textEdit) { } - virtual void redo() + ~CUndoPhraseNewCommand() {} + + void undo() { - QString text = m_document->toPlainText(); - m_removedChars = text.mid(m_index, m_count); - text.remove(m_index, m_count); - m_document->clear(); - m_document->setPlainText(text); + m_textEdit->undo(); } - virtual void undo() + void redo() { - QString text = m_document->toPlainText(); - text.insert(m_index, m_removedChars); - m_document->clear(); - m_document->setPlainText(text); + m_textEdit->redo(); } private: - int m_index; - int m_count; - QString m_removedChars; - QTextEdit* m_document; - + CTextEdit* m_textEdit; }; class SyntaxHighlighter : public QSyntaxHighlighter From 9ea534f4f26709448b294144729b07777fd693d6 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 12 Aug 2011 16:41:58 +0200 Subject: [PATCH 072/215] Fixed: #1329 Program received signal SIGSEGV, Segmentation fault. --- code/CMakeModules/FindLibwww.cmake | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/code/CMakeModules/FindLibwww.cmake b/code/CMakeModules/FindLibwww.cmake index b594b3140..77140492e 100644 --- a/code/CMakeModules/FindLibwww.cmake +++ b/code/CMakeModules/FindLibwww.cmake @@ -6,6 +6,8 @@ # LIBWWW_LIBRARY, where to find the LibWWW library. # LIBWWW_FOUND, If false, do not try to use LibWWW. +OPTION(WITH_LIBWWW_STATIC "Use only static libraries for libwww" OFF) + SET(LIBWWW_FIND_QUIETLY ${Libwww_FIND_QUIETLY}) # also defined, but not for general use are @@ -46,6 +48,11 @@ ENDIF(LIBWWW_ADDITIONAL_INCLUDE_DIR) # helper to find all the libwww sub libraries MACRO(FIND_WWW_LIBRARY MYLIBRARY OPTION) + IF(WITH_LIBWWW_STATIC AND UNIX AND NOT APPLE AND NOT WITH_STATIC_EXTERNAL) + SET(CMAKE_FIND_LIBRARY_SUFFIXES_OLD ${CMAKE_FIND_LIBRARY_SUFFIXES}) + SET(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + ENDIF(WITH_LIBWWW_STATIC AND UNIX AND NOT APPLE AND NOT WITH_STATIC_EXTERNAL) + FIND_LIBRARY(${MYLIBRARY} NAMES ${ARGN} PATHS @@ -60,10 +67,14 @@ MACRO(FIND_WWW_LIBRARY MYLIBRARY OPTION) /usr/freeware/lib64 ) + IF(CMAKE_FIND_LIBRARY_SUFFIXES_OLD) + SET(CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_OLD}) + ENDIF(CMAKE_FIND_LIBRARY_SUFFIXES_OLD) + IF(${MYLIBRARY}) - IF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC) + IF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC OR WITH_LIBWWW_STATIC) SET(LIBWWW_LIBRARIES ${LIBWWW_LIBRARIES} ${${MYLIBRARY}}) - ENDIF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC) + ENDIF(${OPTION} STREQUAL REQUIRED OR WITH_STATIC OR WITH_LIBWWW_STATIC) ELSE(${MYLIBRARY}) IF(NOT LIBWWW_FIND_QUIETLY AND NOT WIN32) MESSAGE(STATUS "Warning: Libwww: Library not found: ${MYLIBRARY}") @@ -74,7 +85,9 @@ MACRO(FIND_WWW_LIBRARY MYLIBRARY OPTION) ENDMACRO(FIND_WWW_LIBRARY) MACRO(LINK_WWW_LIBRARY MYLIBRARY OTHERLIBRARY SYMBOL) - LINK_DEPENDS(LIBWWW_LIBRARIES ${MYLIBRARY} ${OTHERLIBRARY} ${SYMBOL}) + IF(NOT WITH_LIBWWW_STATIC AND NOT WITH_STATIC_EXTERNAL) + LINK_DEPENDS(LIBWWW_LIBRARIES ${MYLIBRARY} ${OTHERLIBRARY} ${SYMBOL}) + ENDIF(NOT WITH_LIBWWW_STATIC AND NOT WITH_STATIC_EXTERNAL) ENDMACRO(LINK_WWW_LIBRARY) # Find and link required libs for static or dynamic From 067c045855488455fd9f2aa72ec77769a7ffbd72 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 12 Aug 2011 17:28:30 +0200 Subject: [PATCH 073/215] Fixed: Moc'ed files not being generated by CMake for qtpropertybrowser --- .../src/3rdparty/qtpropertybrowser/CMakeLists.txt | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt index 8fb0881af..38131aeb3 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt @@ -36,6 +36,15 @@ LIST(REMOVE_ITEM QT_PROPERTY_EDITOR_MOC_SRC ${CMAKE_CURRENT_BINARY_DIR}/moc_qtbu ${CMAKE_CURRENT_BINARY_DIR}/moc_qttreepropertybrowser.cxx ${CMAKE_CURRENT_BINARY_DIR}/moc_qtvariantproperty.cxx) +# We need to add new depencencies on removed files because we need them to be still generated +SET_PROPERTY(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/qtbuttonpropertybrowser.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_qtbuttonpropertybrowser.cxx) +SET_PROPERTY(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/qteditorfactory.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_qteditorfactory.cxx) +SET_PROPERTY(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/qtgroupboxpropertybrowser.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_qtgroupboxpropertybrowser.cxx) +SET_PROPERTY(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/qtpropertybrowser.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_qtpropertybrowser.cxx) +SET_PROPERTY(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/qtpropertymanager.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_qtpropertymanager.cxx) +SET_PROPERTY(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/qttreepropertybrowser.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_qttreepropertybrowser.cxx) +SET_PROPERTY(SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/qtvariantproperty.cpp APPEND PROPERTY OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_qtvariantproperty.cxx) + #set( # qtpropertyeditor_HEADERS_ONLY_MOC # ${CMAKE_CURRENT_BINARY_DIR}/moc_qtpropertybrowser.cpp From 1ef3c1fada01b0bab6323cb0e7880a079c679332 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 13 Aug 2011 17:39:55 +0200 Subject: [PATCH 074/215] Removed: Backup files --- .../src/images/georges_logo.icns.orig | Bin 16519 -> 0 bytes .../src/images/georges_logo.png.orig | Bin 797513 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 code/ryzom/tools/leveldesign/georges_editor_qt/src/images/georges_logo.icns.orig delete mode 100644 code/ryzom/tools/leveldesign/georges_editor_qt/src/images/georges_logo.png.orig diff --git a/code/ryzom/tools/leveldesign/georges_editor_qt/src/images/georges_logo.icns.orig b/code/ryzom/tools/leveldesign/georges_editor_qt/src/images/georges_logo.icns.orig deleted file mode 100644 index 1417688dbbf3552a74c4af2b99a07ef4fa0d91cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16519 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

Yz$Q6(eYd}YJw&I}_ySM?}<2&k=BN(R&7 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%0DNhgjR-bRp!H&_b9Dj z@moLprawIT&^@Ql^@Fg9O*~yZbnD02e&KV);RkQsa^K@$c#XIAjVZX7#t;W7W_nU^ zmf$_EZD`t-;5{kxB#Ex28LnqLTmdsPefr&!rfFy-QYt!1<*8E2&Q~sh4>D6u48$af5u&wEiTq+p zw5_9cY5K!seaJZ?(wx}2|GXQ&>yl^lE5G_{=laro+3=2c{w1$}*>wmhS60G_m1aEL zCX|>?u*o?^A_v8MzT$>57Z%HT_g}Uww8n2tpEBl2mCL8@JGDy4X;2K#Q|4O4%Wl9OGC_!SVEDK zf;1JB$Apq#Ox7&2Ym8xe_wL5o|2dz z8o4ZyD4Mz$%CDE!tP0a&DN+2UKdJ=}@BS>&fgp|!3iV$g<7Ns?1r$bql zC~eS1g^-GrR9`6Z@?@JAMQ86y2X1?Rl;!(w`N&^CAw~2r_~!3B*OzJ&n|MZe?9N;G zwV(Va+aA69_E-22KuJV8yJZAH{?45pcqtMo*z9O!W45;Tf!RV)DwrAcm|a-F$Sq9P zm(fL+Zm*9Jj^#5;jK(czHd@w)W3Ib&4==rJ3oGNu*1l%v_AR{N+4G3eQD!f^fStQv z!l}n@=Zp6pVrH(-zKbuQIdhb@vDBqzb!DA?DTpyqhlq+6nH)k$v=TTZF2w0JCTl#- z=6OURF~&Ud!H<0E_`m)){~A92*=JnCFRAf>d|sbj*!qRZXybc?NG1{qF>g0)f}~Uy z)eum4jFAKh7(;hTYpQLD(gmjMQ}uSUar#M)96H6?+Jt@k1>1M;!Zibi$8N{FhG0i1 z1r#WMk4IjiJ-+f%nrDG*p$& zsgp605L5nJ#3zAFl2R*T5CjoWQenp%S1liU{QOe~zPQNl#BcI#zUXK zc)Wb#8-s0rz`ORjF@ymr5<|rMg!7)(Hn_IUu2a*zjSvF$q``ZK51x85#@l?b3NhlN zqD=+PCYnh@@D34u7TA&SK@g{WQH8`turcd7q?Blr=opQeT6w0F&@%6ULrerLg_Ibe z34tI|-jaw&<71{{32;8rSdWkj;?%Bm>R>gEqo67jK1g&~J{i1!U}68RbA5Tf?3i6x zKFzxwd? zZ~eb+)=FF|NM{!i#1!!{V4}oGg_4TO7&=9fH5Dlj=~NWRuEeK&P)Sn?M=TYsQ?x$d zvvC+=Dqamf2R5b@@ge7gq_Z@|6j4G#Y7rtKQ*J<2DJYeuqg|$Pg{if)g2EIe0jaS_ zACOW}xEv%#bQqj z;X_VFOn@U#8$$y^M}*2N;I)l0N$kM2>jb;O^3pm%&C;JUkQ|ex`zZ&DcrQ43V2QF? zXZM~t5Q)_dPuZ+8Po%Cbqp?RMhcS}Xl`*~kX%dO1ZaK43qqJiC{4DFMW9s3Us0)(m zkjjceOJvM^^WXzQdW809si)IbD7DJ&AHmNkqrd6T?mBhr+oyJ%o7lwHBF?mlUIB-0 z{ctBVYtL^UJ@V}`w66=!ULruH;Bl=b5{Mzt)Gf`Vp{ZNi)=ueG4Q<=fwk_k)gvq2K zrrdy}Kn^Hso4no?dD|INnudl(Qk?2Mq|A1^OetAHkx&Q(m6k{W)_O!Sbp$~{DiG6f zGg4&i^Px&gO_*vm1OlxLF*bydh(e*H%oF+)X=9`bej03-8x$WrjSVbrnK`lVyz~5l z2ktr7m+H%ovOh~Tm|H3i+<#OR>KY{iDJDeH7|zBlMU)V93r(RV)_Ee1sx+8hmlQH3 zt@V-+BSm45Au^)_DMU&o5fNJFurXi?&3su=78(gy=NZ)#M6gs!VJeL@Rcktx3&k_` zH~E^yZ~yN9;@hvgu#t&_g_am8D3qj9P~Z_E(l!wPB}3?rY!n)*5N{=u@3894)aZ!N1pW`ft0dUX3CD$7;PwvNWTgc z`foKS+7y(XYAS9QkQ7E5DwA^wlmKNkK13!{?W3`dvMe!Wha@~PNt8}VArT@O0T&VN zPYI!Z(74r41))E1_ox2)6X@XsbBuYw@ z&Vic}GR;&-L`+2INhyb{fCyjJ6EgVBb;pofSxc6gk@OqWZadjUyxX(kgo{W(UfXINcp zuqdKXxL}D$gw!Mv@gjoJ7zN4achuQ;1u#9UtMwTCjWb(1oH$c|Nj3bE8;>1(_fd7b zOZ9N$;KthWLJUlWoPwN>E720PmXNY&W(PbF3~MV_Zk}UUCtI zvUG}sZ&$&t)9LkDUvJqMIrQrHl^eYm&>F3Fq<#ge33@e2UX_@SOP& zlOV-}6d{u?Q_6H7nGgh|kT@SGbrvtn+Mx?ap%kUmlt!bKB#J~7f~X8WWV)3`5=A75 zKtvOt+JfXmlah`?L{tGH%eZiDe$KOvZnbv#)z`#R$B&=uOZH`lE=smu_`Leytsfp| z9*#mtjSz~WsHn;UDIrCLmV!d%>79>|geD{jGEr!WDFsdIa4}osYGqK<09NN5QELc_ zLXkhPLOo^4=##>taKYgnlv?@HbnsE0>k@rMFzEM*DYc`u29*K^PbHyS7?e`P7*H~z zfg*wOndd7Kl!Zc?l9(KmNdqaMJSUaV@`7G%7F=o97;6t{O z76P=CQ<@zR0=|`uVt&4alt}3?##2~}OoGyAx`m<9iZ(h*XVIplEVDU5$2nBT6O+M` z2q_`G!)Q-X3SDTV%C0_ADkO0+5x!sH-=LDcKZf=%ypSLN{7s)eAd~kmdg*J=_2t{d zCjP(TkN?M;`JG?=+1}FeBd<&`^Lv$)C;=(Nsgo4hU?qmB)hxxu? zZp%FV*)6!y5`MBl-FArUaN^h(5hBv<6$o9jvbu_oaNy)RHYB1nTzl2U?A^07vk)4^ z%9%0l*fI!7)zS3&9ZY`*p>?dCImXI*%fv=f6x6L}GPX2r-ke4u5F!ym!u$NsgqY@e zL}E(xW(E&e{lV!uso~6*rpx=18;>nJck}ume$&eD{lZUvA|{&w{(%!c8)KPId$M5&)(A~1=zM{2cC+hV~2=sjUW+1 zz}W_ZMhle_lx099fl%2!T?o)Rmr5#$(t)P62*st7)5or9R!;u?_k!HS*9DFoJ;FFIYP=*tF}$}m{;RWEl5Qa z6vj+0e(v+r&7bu3OPn5waw-OF(OmGeohEH0gaG>9A^0S2p1A5 zrR2ijQlDZHn4+N9F^FWcmLf({h$ty2iz3_Ori5@di&JC7PxVM3NP*nQg@6k&Z>K~) zEHp0b7aJF_K2y4~NHl9b1o_!XDr*?En!3itKnmHXUrC8i5Pgel^K)95l28^DMnbSe z+n}X_s*C7$X`?0K@To$yIbFT1DnePJj3K2GB{foM&vn?oS9D;xCuYU85t)oB=I2=4cOg}$q(3{4YgSo0_$Wg6Svzx@M-Lz6 z!2@U5zh^t6NkbDQ*IxA;UjL0RVDFAuI=w!fGP3lqDY1N} zrk*&AQn=)EfG<-wNs;wpDdq2l6pm-DPyADO@EkWDSHJ2z z`NRM4Q@$$AeXcBA^43To6DSfQID8vOB2g%X5)w&5B!y0at~MZG_4F9$662;}=e9mG z^Ye_C4>DR?rELSFVZ-B3+|SgEH%!T^!foi9ur)4Wf?cw&P+_#-QKzBVJTvLiUjpL8M;E(?L*3bRfAO079lxqdD-@edRa-G3W1mtQAVZvNU3*F-gn%8cbj!jFRETs6&@!i!cT0g*x!z`)?@=m=q$GuDmWnX6ZklB410m!T`G`Zx z-1I4>C_Ka{&?@JAxRg*vg3*LB;W~oadWJS(9q2NdN;=`q*JtZ736dD2+ZczS@ydrp z2ss_Nsq6eLoNk_?PbjIWgP3lkrpAvcWnI;jwy1<4MMn&g;5^>vTn--tt@BgyxX2vs zX(R5ZW)Nq!O))|UV6@B)I0~dhdJsxreGVklN@cSZnJ1yO48*8N$x^z4UQywbp!ETx z2WSjMbWHaeC>tSqf-_9o8l??I(aEXmS|e47(gLLwQW_d+zMI|? z6Q95F^EW=K{n)+e<==d+FX<-!f#Xm9@W1k-|LUjtj%zM>Q9WsPpVe(8k?<)ZlOUu_ z1MYMbx-4=|osXGWFg0E(s{)}V>l-zb;Sk?8v|HzpQc_it#l;yeyzC0vdIU;x>Winb zLGVpiU(A`+b)GnM0$DF(mmVa|?q^}&wa73bxQ6qd^Bmmh45qu4(j9+zU|gsYb*IMXA)dQxy09_-n3)rnGbAqUfpYz)61odNSXb~yIvB}B&Fz-nx?UYAq@JO zg~fR~GmA7M7*CdQNmI`6CU)VWFFwlr{3-f_9)p=Ke)SNmE9*RYbd{Bjn!~3joLoJ~ zsP%NJlJj>huyfBY6oHMC%SbmtMvo#=7>SdLC?z%qIx$cfMb+uxT*A44b%78vjU;-X zl`AnJiI9>|viGWA{*{0Iwlk01b>>_@7@PQqg~wNF%zPzQKl7flT{?8}$?G3@eG2h4 zLZoX1%mzPgFs4n1_YP|wJ1fnFRn5}iE^eu$QO^FbskXOS2iE}x5Sqh1ik#1>F(>1clDieGH7pbqj>^TTk zoa;;aWruebq^ufGFEtJq0^OpZBQLl<$lGWk^M;}F`QW-qBg<0469VgG1siBY0tF>OEv!#W2xh6GJa(0JK|ni%7k{+Ik3gf@m0Lka?% zOL!9FHnKd<+YKqtQV*x!N0}YoV$3ECrO{aDXj%&b%EC|;hQ?Y1WcRQTXno9^4)3us z5|ae-@9n1)2`NO%$D_RWfTw_0-POI?s-+^X%HbK#Tz?VX&|j-7%yTnAnyl9yyGy zN6c^AL#I@XCL>zsIDY5}Tz~_|R~b!eF55SYkr6*$qt`1L4;!j*l)dNep>=_x+{s|u z#cV7+!IKA0(vdlj3*l&j!-%|bj@kA1DM7pxAQR-6@BCet(j45nd(T58_?dt4rZ2gc zUvlFC(BHC+E!!6F`{Mog-`_@Yk)KY;iXe!Eq)6H7lIaB0hC~}YUU*s&C>l_s$i=gL zcJE!Jsx-k4ncHB6nJ{i6HYBu$ zS6+81=kMCetQt{-6_hRrA%Ku*orBB-;E=3O-1H_9NF}L?3Mm7HPDIgQMBd7fBGKom zRZLUyO+C48x(R|Q|Z_f9Y!W$*v+zPcS!d~i6Qoi1F6gphC{ z;LkeXNT#;gIlL^TJlaXqNvqOkB$Qm1DK149PH1DsQmeM8s*T`mkLdbFitSSBi&OBs zQV^nzo){$ELeW=&p3Gk8&IcMFrrQ*QQWBZ`)N?!$qNk?>iGY|=un>Zf1W`#e5R3q! z2yIU5uC!D&neO#TXVc_!=7S*9p`y>*r644swP>Z#MpKnl z&Yn>DlM!OVJ5OCtvRGZZOm*{FPZL$bhcPKm8@-e%T`>t{ZVIJ$EoWWgzuwR*3sQ_? zrdyGMqSPukcrtJK3X?bg2tT!&7lG>-8|s zWj%??O^DV8rF7PSNQID6DJ7q!O}Zq;_6L&?54zO6AE7>T_idkkNS}IWJbuBOc;WW) zTwm%<`~$&_*T0`juDI%g;o8~@l@_Q(-WU_Jv)unJy8|SdcI@-~ixkKiI$GfB30t-; zGSipz2Yt$_#1tBYq?v4GxUs_efyY^09uj?IV{CcyOwGa|QywqB;&Ptz!k02S{wRkJ zo*|}e;<0UUhGw*m>2G0i@qCgr)MLxq^4jzpnTLtNT2kBQwDPGF9tn!6=cxh!3#~;u$);P(b~wy zB+x4iGsB3rEg?lh@RY_(W8;}7xn{dv=Nu`8xj{Aex;KC3@NIA2bhp~XR|xk#^e_}% z8K1nlAD170o}a9|K}-D#MZ7|$R7#NpE``j3?qD4;c)ayE+u+(vjk5KG;Ij>CN*;p8 zwRscLG%ce^&2TiDx?F*u^2n{r(`p|Qt&h|$WXB6ZrhcR}&4Q3FrZ_IE>UgKuz2CR( zt)|yMw77HkiG^*8N9VR~J2n{&)BpUFKL_BKf8pQq|N8C!#*h7z@1HyN*aMf3*4AD- ztm_|k&h1x9Q&~Z$6;yR#rWDL~JIr(>3QG(Qgq-?~XT$DRN(u=w`Fx1;iI5^%WS+Yx zco9 zka>bF0b*|6qR_}vqlyNTJif6Q;fNw)v}92BNFvjATAOeII=amk%`Pw@VI_L7W8a*c z3|Dykm%r{r5>aS{&`L7s6!fc-Qs;Uabv7I`>2OLX~xsk@d&snRnR$) zm_CCOCz&*Yi4EAZ1A)XTWy-gks`}{e^DnyZ(Fg84bMKQYz!-ox{pcI%4rauW1CMSw zbMnObYs;s%M`!mIW#?LF+iOCwdlI5g#H18ON(m`*>b!IjB@{}6b`d2V8{?YUZb{E5 zx`o6w20Jb&jHcTwsfvo<aFiFhR0m3Oa*6vMkYBQ&tsaS)z*qDK$k=B8?^qh0z9M zoJcXuB6zV7yh!Qth#PU(3#SAB zZ*P3H@}=>Q*af34ugTj(7~1aRQ7z zW*itB8{;&IvB&m1<1pCR#()6_qKz>o2Ma`&014%+R!iNj&i&*Qs_Ks4ANP^wo$s4( zUfZ;2!K=NN7ObUST7B+Qb*lE>*LD3IJ2tOn@6nnEjx5vf6r6wdPQLGXSFmBzM%MKk zj_liq>2JU`HRIt5M-DGDw-F+eqPq@XY~aW+(X=^*a6AgMRv^$sA}&GeBR*z7CMjZF zW_jd&v~X+J&wb@xZ+uf2oT9TlC5;Ebo^@SBN+0bM`d5g4O{oQ?k&`vDKnW;xj<|G> zXunfJ$wX-cMk!*`vV8myT6=5~tgasB_lfO?8-Gs1eqqwW3Ip>8X<8DumGD zogis}w+%`r3JppaQW97@p*qHd_27KM`a}#-G)Im<_kX#A2{_KD#L95Q>TpQi+PvA2ing^l7axU@Au<$6#Dq~9eijfZ>2g_-?(J0N zJ!{r(ynWQzFOBN<=vkLtKDhkJPa7S4XkYr%H@@kA`}uz7_uc@&oB#T6mw)(0-}~jY zyDs?J{(J8H)cEA_->aMU>DDEj5!4z+jbpJcP%;rx$czaDg(>mLlX8|JI!2~YMeDs3QsO+cF2~wx@}@#7IpJo62|+$n%l#yNQ)b{f zMk=(*l8&aeq!cjUS$yS^;Tr;b;bFOAlnmjvgD;MZ%Bgix7cFUe+FK?YDd z7YHUx%Q{s_w<>Z9R<}p5-_7ybO5>f!hlnmDLIzT3(J~@)5FSb-gtR0qF=X?3Fb3fj zQc1MdD5Zo*c1KFQRE6}S6zt(?Q+|2f&F}np(w*CH_{z2WWen~~PkSO2>xnwG^_D;Q z4PO7c*UYY-Jo^072w7@{m~7|LB&}77gwYbMG>Lq_x)|}vA$;~t&8%BXVIsk|U`kfU ziGBCp%k)%1+j>GwH0dx8ooIOY_$pl^IDgkVe)t7fadOqtt4g*_1)5{`G3qQqXz9;x zq*F>l)kUmd%fY>SSsplqoTn%X4xeZ!B}k(fdz-`T1=K;(+6lcp3S!I>%^0DLc{`F> z;Bv{@@R^x@!C+`l$$6f##$)r2T~yuf@$qo@!0OV%l|p8Rf0+_eCxp&wF`)#Nk`!9f z(Tcv3OqCj;CNRNPG25SE{@8H_%R}auhFIr0woudSPjl6kyV$XzWyNVyAiv5 zl-u?m=l(-WT)bnJEo(Zgj>k+*cj%aitur0B)9bQ%N5#_O0N2#SHqbGGZl^*En4Ru1 z)2*nKqUvZ6=~0lN(HX`hh(xkR)_a3@k*M^!!y^w|Jr?rgr{SJ>qP~sl-V-N47b4v9 zsSZk?B}ON|LlK{zBv-27HcKUh5aNH*xnsyC(~vzJDP}mBmXd(LrwKJVd>x?yQ`$kJ%cu(s5#C-{_K5}l`Uev;552+>axE|OPTFZ8FUZcZ_Mrt0JVIQ)yO5j^$yX_L{0NXeDW!W87Gf0;392N{m~}$OaOU z#`^x zb>%!|`UIe;Aq9O>F$?7V^3<%Z=W5}=&OioOb z5s@M>MzqdSE2RreQGl30E(?eeTeedbM6rQMWgSzjg>q#+QRcg*tVIrGU3au59lm`Ve`=+FnlVFq*7XpYD z>l<8vez#!Vx@knLnVFuZ>{K8WWoMc+uDRv*d->9B_p;hVLIf?dA^Zi;*us?;@8QJJ zL)>)hy&UiNDGPy&0oTrR(Pd|_X8lHn!-m1yE=??%n%l|3iKDD6%`@F8Xq|-s!9|jD zB$?mML(Fkd-g`od*>f4QZ!*P5*`2y;cHP|Zey`%t9eYp7d7iSyWBv9W{Mm;-G5_6r zwtiAbb!Dcsl7AS3n&A=)%MD5-4lhM6I(H*azkG_-=6<3`Y~Q?w`9Vv+@TBNby{*KUpkBqO zAyYFw8Y3Ck%hZj{np77_6S#^j45m{OV0vc(44D&jy*VW4l&-|(P zUiYbuXTL7|?63dMX}LL`sBhi6<7?Ldq91Sn_}Rqx6qV8oR7jVS*pRa~1>zCGU&`_f z5wqtg*p}#8Qt-q8);XMOvk!@Eq*gM+$r2$`jzot3)lEy?w$x3_xUOm1K=%7)lWA8K zI3KYtAWd~5r1bUv%$hH0V?NQJU32h)tDd!T?7+VCj(5H9l$^uw|G^tLQ;1BQ)*6%`U}zE4-cbP^sR$v<*HW_h z;Ill7$W*B*bVc%(*+StblY18 zO3~PyVkMMHWw-y%=l#$tzHs~XU*M|;{_z|7d;iIwy&ZsezW<{OZ+PcBuAN@L;j{nv zme;>y_1Lj3KE&@Ft}Hz-rSzo6xh;@{(UK?=QFxlrQrp0k_oPW&Xtz@#j06!UFJwBy z43(J3mi6R+WsdX|St6MuuO-q%QV@uw5CPy3I9s0DiXe$PmO}VTrIeS55MC~%Jm8|c zMhLFG>$;ELxu~`uE>azza`n}x`?7eVz6I+|zxO&`^=rSSe)2izKQlPDUa9O8)+T$G z3n7pqr$Q-G4rtK%dF`Ah#0XMQRGR3<2ya-sc8Zy~wM?(uf`~0X8N9LFcl7J5-LQ%0 zop%PdwS4uqz3hMZ1bgo*m^*QT>+d?ugU3ev(7!sLjc4p;e*aw{Bgc;}ky4~PvyPp6 zc4KWq6kXbSig6n<1lR={muM_lFS5~ml3W#1PM&2Fv7X2S5g)-s@tMzk?bgK~_`dIg z$6Mp^fe(L@y6m!f&u)e%_tWosI^B7!3b-|Aux4wIbN7w8_PSfS?$*7`R1uj1n>TOa+&w!e zsviAm$#`5-JI{khR?w-XD_UZNs#_9LVtLdcgv8m%pl_97pQi_Dk z!doGUvBpPBAP`b|{(FD%U;nAM?V;0idpuF!O7*}44?v2tJp7eTBRXgJ#e>gH!9GJP zab@yudx}8`qnP{_0F!vlG=cqv%%EwSdIA*7DN`Zj$Xx3KO63kiNR=c1M4)K{i+Hf{M}km~2^)^2I6qu&v93q?!<>vR8PRQ{pNlO<5fBPZMmm$P59 zEJRNH9&Qr-8z6+7wO+T))QMw<#c+J86s(Vrdi77eg6rP)PR@JQ`I~T!FiJy^nV}># zMofO!kO*T?N+6TXFLp^FQr-{=Es@4xgH@ZP(gR1b&Woq~G_!S8l4n2~LB>@NYAvI#!Q0%;7|NQBIR z4{1Vh#(TWXbmt@!Itc=iR5wVaNXkG;6jgyxc?+VX#27`Tnuth}LKit=JSDWz2qeJ^e5@HZmXXUTkisZ@OpJzOIwsOJ9$$DQ zo?$Dw>Gq>2DfpomJd3%$qF!B~scViOKhD(bI_^Jsf}8I@$vHbWaPHQMI6g+V-(~yx z7cg8n#*rgS^apMBO7+&!sUof((J!D83P~b3P0$KqG~*;lB4DII`bhF2HzOph&+iz@ z6wA8Px$TN`&PX@kf8dmz=_zYG0M>8b4uYG9OY`@|q%PMo8_J~=KtiOfbG5-g>ivj~ zM`({J0-a95uy!0iyoi*N{&bi1n>Vv*>t^ioK5(n_x?Q3WG|uszPdk_U9z4WX?>8Z+g9{ zzuT~V_d_QSJ=DDAFW>u3Iji$7yns%B>V!${-K&cWTa;8xcMaXLAOT97++=x=P=dlJ zN-ZE}6MRa7C?qa;Y{2Ce+unY5TJh93!ncbwMBh(;rNk1ubcLVoAK!7 zjSm<4WaY?O`aV%d$=Cipl+k|-qJhbbxDQ>D^Z3qSZb2@d1KSGo6^YlJj+ z-*&^7_HGV`M|Y|7c+QhAIo+4d6ZJT)+dh09Pydl;T`KMH!b0W1q;99AFb3~Dy8>NK#>4p!E{fcu0AxE2KiGf;LK4hn5r*Cr=JAM$-7ikweEhbJG-S#nV|?QV`fco&QFi@*X^x8T^(8YL8^ z0-qWP4TbcGf(#Q3c>xeaLMu%$n$~)Z&hk&8^nF`)?z)YXc-%G~TXyf^jcy~|oh}{jMyzj$B7%a@C!?!-!4Hlp2 zrM!X^F0gI0ZrHZC;1EKhq^2l~4At{4uR#Sg6Ubi-4&OQ?0;v_IFqopE=yZ`v6Wbc+ zBc|$NjKhaW>m0Um`9+6B#F1!GI_LDK1U{!GU}Tsu1FA(+Rexi8({H=#M}GE>2fli3 z{IkFO+i&XGops*DT>S&DTz&6vyz=%L<(_Y}CZ&W?f~qhSg(fAFU)o|s%j|1m!VU=` z01H9%zS6o#Yu#jXnWs2DDXjMl8;kV`CnM2^e%YJW=Uw@<_Vags`@8^;R#yr4-&hQ6 zI@?*nRHdHe|kyjtZ$%HhhMOYy*MR zhSqwNDUOet#g#X`?Vs+Y`bO(LAN~x0`}P0)x`V&_+rRY(-~a5Ve5@WUydpUJ6Sj4m zR(ykZiQp9YhPG~)?pCawnI=WD42ujX)RTB+;qtUqD23K0^RiTd^<>g{l9QAxO~T_{ z3sDihKq!qa3{rOxLi0bVC3%|`gA)qb7ebyRLUWE3;$Vg60fxht^-~qc z4xOOeH4Gcap(95)Z`Wq(y5+#pMb^$vvwO!{1eTeNo0y%Q{Z{~CFeluInzTnfo9lcZ_Iti%TNQ!~71Z^}*<(S4K1U?FC z7ZFXsVJS?cQm}4LQ^ zf}y-^xet8x%XfcsZ;vPH@ls#^>X*6V3*X6Y zPy72>ao;D;EK)pIYyDh&eRV=??dW1E5k-iT>~Ad~CdT72&PHN{y0yfZ85B|q>Sn}X zd4OviDS1q3(8dr#rlKMuLQcS6&J&R&k%2_EZSdX^MWXe2t0zTHvRWN9EUXMz9gOfH zKc)*)-qGz(|HcKEKj~9P4n9=>(og@)X*tKKwd?o~&%VU%x^(v;5d%g^v=o_)93#;M zh=PtWG|A#!!jnns*+-V>RhnKQk&gnB!UX0d>1u_Gg4#x+&{I3k*tNw-acTstkCSq% z$4SkKPVI1;kdnqnqDT}rN2IzG&;rD`K}Jb47&01k2vVX7o&W4oLseB&MF*o4M&uVj zS(F%4(UBE8Bt$bt7ZAK6ktjlB*d!Vov#iK_M&oSgZ=;}ff?(8{qlIJJV+`L`jmL0h ziDo>WiYac&&AvcMO}{MAMiNsZ#WZ2xB$U>aQf3(D%P9oy$2p0uX8ITSs#6%7H2#Gy_$jMiQ z(MXkJd38~sOM@~pN1IABG5<=8R$42)J1M!_6vc~@A1p-VeR65<7w>=m`>yLAz3#wm zH+|)ylu~=?lb(9IFQ+H!G}fQ|`m3jx)|Zk7j%Zhxgvc19#p{f2v|`!+O?i+{k$TBxMRTKB9=I z6wnC_Okktny~BrqN?@2o>ShQJB-KpKOn>IQuHfGK)Sl}pZ9D+hY}!l=_G?-fNAYgA z68XU&ABhNzkbKO#PazP&qkK!56kVY)GQVJSr{}18(;Li-1t^On+kUvmdCDadNd*tDXR{u-v{4Pk84(k zp0*hwVgQj?ts53rYitk{WkuZvMs{mM3GYVA_zmN6hgwFb@=FsI0TXuAx42z0`CH=ZHrbKA0Qzzk3dKe8m%&o zyHp^QoKAMMyIpnPR*lDMlhBE&)%nGKHQ79S(NM>RXdhvE1m@OGV`TPch^mVcV5*Yn z15zl2G`Q$#ZB6naKdih*l@-mnp&m651G+SfTprRW3DH}WkO--MM1YJ>31uXk@y%VWbyymWVjHCKHw&BT9VAs*Fxo? z7n!Nk`ZS@zgOrM*Ed8Zttv{`w_x;!2$g6(p$L*Wn_Kr{gz;muR7)FENu6_FwTSpn9 z!I((n9K*qg2c|ld zWyzB-+)3QK$Sn^pGKq3zeNPc=L!mW=N|a(mcl~-~+{)^zXJu)DV-Gycq5a2MIKISi zoV_RY*fJjFh<%brrielzq(=l#2-(mqW5S4p3kjk1(b(Fr&90w=gNvv1Y)^UPF}-Og za~rpu*mv8FU#RPLw~m6ckVu_9**O9u zm2z>!&T}uKvt|vZ$kCbmkKe_yg&|2u&fBw-=RNfz78e)!`%iuq;ag^kmS!|21j*8J z!*pMh6M{yB@dC|wg!74U>u7zXae+!(N>Na$lBs@$F_A!GI2@A_I3FiD>j{w@O2SlE zLDEKO^?m2O;Fa&&chi>^9_t(9i8?)X(~V!ot4j9AN4Kng{sU)9U0kF@dX|dzQeZ<$ zvhTc?K6rx9(U-wHf^SJNl0qcJNi61M?U`aA21j(6wNYx5k(0rI z!#OwEf3!rI2kgs(_RUGj>^vKddoAQj|K=;HUaj{eJ$<6N$DN6J0<>Lq{u8ip1n8 zQ;YQuXCoa6+LYKRFh=0LV9|jL0*u0kw#R63#;=^@HLrdZZ}`(UpY9|3hU=Gq>D4UU z^hso=-|Hkh)vFBMZb7HiIZ8G~3Xu>RtjuuB7$RCh(2_DKB%0PcVp0U3LnKm)nG+y0 zM?)&IG27ej```b*Yz#S#_2#$bL4}C^zG`ab)u|Z?-!?DtDA7d0poth2kVLGsq|_pH zh6oA*s&@F0Vs>x%Gh*GdPu$(8i#&ArWxC&D%L4 z5LjdqC?SzdI=AQ%(oiUc(kjP3N7t43ix6BS2|p}Tv)t**2iKPE^(R(d^5utje&WHw zO<&q~p4m6J*`CW4PrB-KUvf{>V^p_%`V+kLwLiV){u{say_E=skkHYfwZQq5Q>=`} z7){lws0xFafIeFvNdgirGczU1NSLHcT5Fk~_Y_(JOT?52NpjZq^(@zk6RRy3oH50& zO;fz!nHR7)u-y01QC3%nY}+uyV5KIe#M07;{de8Q+D+>zyVIoB|F0@iKa#*w2- z-1+qbSli;AWp!o9V3gs(N~E0fiX^3k&uA}POcSODfS_MiU)Xcrg(v2Z9C`dV9vgR^ z#hst|z_>TN?(@29UM5LJuToS-p{1ZK6`f9nlG$8tq`)Xir>mIiD7sb7h9564QqT5r zwk4tHZ`j51$rIdq-Iv(7VT!5Q3gZ=9HgDpR^Brw7<^@;nWKE|*6rO&kb@Zf_4Zz+``#jFQkX#y>YZb#umi$_wJoZ*~e z%qw96&PN0p#)2C>MHDB`IeFhL*MBo^jwkA|t-HSVIn3G5#@_YOGOZr#nEu*bYV`0` z$<`P7Brb}sIS=pFA~TpR#zYDU?-N4g9~)O&AV3n>+EI_k)Z-Q(0?s+=@rWG6BnYlW zDv6Xj!?{Alr!higop%UfA{`Pr;icQb7@4QYApk08|0jn+AW+04(M8EzcM9FJEG#YK z5l9&cg+!Otz18f7*V*BTy$^eM!y8U-(*gO%d9@i82S#e$@xuew^h%~WikYsVR01ua zDl*Jyc{D~SjnW#SBtC#dVUk4PVJ&w{d9Il11|Lw@78ev|+D zUw-p+fBaKY6#)_2OXsG0Q^bhYdCMkBfe!(%4MG&m4_X$6fo`R8qvjm59nD-?K*l>(})mAKB5YVC=I%+kRp3ZnwYYDwvDvblZ2Z@8%r9O!LT48fwf)~{qCh# z{lO0nk8g`8<2YL3ufO;JKly#nIB&SHTsR#FN|S`7s}v#yf^GA_K;(1qA@eze z$mw*}2SO5PDYBkYs(5Rw9%0zS)A&TsYi7i5A=x1GfmR4$FlAr_LwHT48 z$|94ZL*5dN2MJRclqpFu<3ft6LKj_>&hH<(EC5MSmPn=0MpITLrYKNRArT~*c_NWy zgpljCR_kSSPw|MKi-d!H+PfZZrth5-OE=zk{YP(-obh{mjqCiZ7CKgW~!<}5_ysdr8Vul?Xra1c0K4y$&6b0jP%X6+gle2fNL#cud zJNM9=UdR0Yds({wC^IvQ%x%~}Z|w}_+#LJveUQp13RN)-0feM9hBiU#!i3M2p`JeS zXanLx-ry8QTi^Q6e(WDUHvYaJdpUgN=2LpMr@Zm_>%V>%|LQqU0=Q-2;6wYOx97D) z@M^Mt_b3FWEOK;b%29$o3fdshNzzdUXA|`}FuisIz4hlXHMfBSw|$no4jw>jL8mK; zw&m*c=U6voc-Kd6;K-rF=nYfYal=$8IKFDR=g^pY4?n<5Hq5eq<5rTB`0+fp9br_7 zPD9*iOt06W=uQ!>hZN9-V5%?acOp~00%V@NNF{0NI%{yHBob*_OR$#GIE*%Hv`#PD z_1E`(g&oCdokBfP|FXK{>tDd5RX?q4Ww3u|y7{lpETw)LMqVb}(srfPOe$n)Ld2j^ zO6Lv61mG7^1OnbUBmu2GF=TqUh&A{TBG$wmc7|GzvC0v z-~3De_J`q1_nhwI2Vk^xf=|5nFFMox?v~}Ezy`tcDAGhhRcK<&4l82z#bhv>&oRIV zd;%jA9W8*YXDxKr$_JfLwT#D}`DM$KFWAmm8>d-bSf$@BDQH-`ZY?v@ zS0GHm{Gt7vICg^0$`U?G7Edg)cB+GQSxS|hrPa_Diq>U?QH*)W;X}3!xR7umPC`VA zeVcY{zh(Q5?Y!rm?>^;cd&(ORfT{Io(3zUO=g>n3ZVxt{=Yq%SL|J60PzYm`YEUXq ze2i9@a!jw^Fg0cAcRQ0fLFlbnPgN<#C+}srvV=AzX~Ht-Oszw7G;3EIB7$pfI>a+B z+=v1OwWYD1`wlJ9sWc~!&(p0m)3fUd%@V^wLu+f0irI~usOBW2;gFTZRYt3;$l(y9 zBULqx3z|}u`HQkG!Ph8N5$U{k7kJOmB`S z>Km(DZo3VHkY;IbRg4!ZU(TFwPwaiN!kr(A&iRCJrc8cal1@sMZxI!|&9njUEzY;35V37diUAiaDFR00UBo(1-FQM0#CD93VnPPa`Sp1e z=+RnYh-eY$_lrD{&SCahCvH7bS462uNzytGQe^0nOz1X23=C32n&OVxO*=pSs+YYe zz2&_hKHXo(qqTf^Kld)r_ofYJZlCKD0qX-%2-WBL?f9NAol#U! z8l4-DLL;QWdrc4$sk1hjq)9$vM8v3wRFYxqsjbeyl$2Nlzb zT9w2ourc66LdeX6VUom@CREuRNuHBqo8j{I`0!p6Tzk5Y?3C2#V;In40bYfrIzo$C3T}DGS5qU1zfMoXc35 zKgz=4`#E%Yh2x9k{7zwghQOwX_dd%yg+LdI5TP)N5CYEU=A&^QAHbC6w)3vI>fZ6{ zGTd<6DL>!Gr15y~-~ByjD*U#K&ic&oWc}ilQW0Xpx-^+iDvAl9OdzthJ$N)w$b?Lu zvZ`2KRy=&*7|mdw>6so<8P31*$%JNwW_*mP%AsXqroe|B?-^1!e|g`1&t5*V|2?0&_S1a#^SVTj=}p*c_5<;}z}u{~3ZChOMq?8Thb=`mfm%t~8NfLsfZCl#5#RrR=B;5!BM(53n zmMLGGreLbqMRgRNs-mnEN+gIP`$hz~2tsROkXY*|C75I>i-gvmvM4Z}?yYZr_do3W zhfjQp$5Z36|ABky^rkxF)#c6kE)z@@nj{le=7!H@zyTzcLpWldHaQ^(NNkL>K2uvo z3VBkM3{vFns}>+*=AbmgcGrRX_svD`9{Qa>_yhj)um0-kK7wzk#!DvOl!h~TFg(W2zxtQUdJ`z&MJ`NmW(!8aVP+k&Z?$&hhXc9^W zkYU5hp?$jr;&Ih@%4r8bt zXp@H`!Pc~`!}-X_IYzCcwgDS5)jUH$EmDZdCMXAWjrOZU4C{Q~88H7YBNt$f$#3YuSlq#7x8^qaC zku;;R5NRc84is|n_6=_FhLqAn`)|4S;g#5Z7-%oK;t87idpLRQ7*a_(-9D*ULmQR}Nzeqrz{0SNG+tz|VTjpln*4;Rl<*-BhpbP; zBy7Jwd+p!6`Op2zV|2!kN#g;qY3D4O@cB-mP9$fyNdg_I=o*RCk>Ddr2nwx8F;WNx zt|i$5RR$^|It12k+(igWpi0g-e-Ea&jt~+j58TOU_%J>|bS>v>?Q!PzwH!Vvfo#=`pui=BL zFTsgd-LA#z!7AFO8%^h2;l`Kg>)(4WLZ9tr+AYwVlDCyikd(>^6(Y+TZ0l&-1}S5H z(8N4_j1yUt_Y;VpQaR$%HnfdnJg#ZRW7>9vZQF_e$1)m@i6LS`ptcql5`$4g-B_Z? zy8CSIN(7hD+*(RXqqDRRK~*RUBaotIWo1ZZ3Z{D{Q@sk20x1T1Q$3{8G<8Ev`ChZm zQ%|H_zOmS*MhG2e*Ka)ZvZtNF4}9-;pYE^gf2q%ZWgkEAqu+PV{)0!RN+mH03Z3XG zO~)ug@N{%e19Ay!>qs)Ej^rtGq)-AW6jA5+PAMSEu5xO}&}P6L5v1@lYp3SU`{b?n z-L&hHQ>$nFX4h+f>E{{W@dYTV^)mU5NP&-$#z~Sa5lQAHhKQsT@NGoYP?nmiP=ufu zj`9YrQ)*K1I9H=ofl?j1-4bIwA$iax6OTUQ)^TiUgqwtg7!z|!p31G_9p|f!1eV zr4Io=*?dZ=kwT)hLK>Z=SAbTEvMi`7L%-Lh+v`x31}UIpG=3`i#bd2J>MLFAA+Wnh2 zxz`1`_n{j;cBdfToZ5Qcb=I{@a~HIU6tCL-1PAPN)eRqcH_!gpKR$KK=RWlUk?0py z4bdfaJm>9LgCbFg zz?s|D^4zN~#*Ifb>w4V157thV)YdbZK3bY9XVG&{VdCj4F&Ru=rjvF4UGk&7} z*Gf0MjeF$T)1_bC7MqoghlSk6iTf@hg^M&{w~p=llu|DVQ4kzVrrjwo)F*n)ync^B zQUX;-LU5!Ia`t}6(_N7=ZMbd2Bt^Rjm5e|>pX*To2Pau(X@`% zdaU&{O`E}BLJ%jC0SSl*LFONdLhC%;(+X4OC`uztPSTJ$f7>XD(vrcT#>G6jmMLPr z!}|RBTAx@MM@Fs0#T4drcXf66`1AE12ZXoJG{#guX6}Sff;MIOjt~;{Xr@0zrLdD! zj)Wj3Nud;#$+59UYf7y_Mv#V(B&~JS^*ASBMWLif&-@LE|jK zCX5sW=QEMJEMT%F38|!>sTf;N>pW5_LWnr;sjbM=av>0TIt(G=B6{h>@!dymKfr5$ z`Iq>^*Z)qc$Q(t(?{RlAry<50qc|*xx;_^lDYN4+vX_ zXdS^M-N#H%VTwEm5K2&#MQ&U}L<&t&Od*ZN$B0WcO|(QLQfZV)L>CAl5Mus4^FIIo zBpgh<9s-b)En7Z2gVBmrtx!_Y>vidLE4tl^UZChDdtqn#?v`|Ff659sX3Vfre zwa$`PW%4G(lqe}FirkU564wl~oFyLN$jLD`-u@6L7Y6Lvwwbjv1?$&z`RZNA znCn%XyK5UhWf;+5+;Hah^=#X|74P#q#oUey@Xa#4=^o<(mQEa^C=GM#Iy8-Aetwnt zVM7p!HkQn#4&6z%y)qi53{oVLt$~CRl1{Hft0f^AYzXUPa4-6u&m6e%cOYexL1?sZb0ad zyyd^0x2A7IZOKMYA#ov)QpyQoE&ve$_>d($VWP=aBBuf=AuvYbeWZ0QrIBc(hyje0 zL{p%p%v-2><)Y<<<5Szty=aMRzk0fl;a^;Gw8+U1{BETXPmfX}L_!Nm)vNN0E7?B~ zqNfcKqYI?T;6u4KK>m0$yv!3$fFW)dcdf>$AKD%$EmJaXdNk8^O>yO-h^OK#+ z&lxEMoe+^C3(lI>Pr$ReDQj$CNn$}ZW zch!g9@_Tcm;o#`$K7;>zEv$|}pjMYxRScem;g};QS165|Y;hAx3JRm?b}PD7H%DQ6 zhm9WVBZF~^3%O|>w+*dzBuHB8RiX5eqSOl^rsTbw^4?DS82d`9E=qMnN-_X#fC30o zVuV06nIjW?i}Nkc<;}LOYYMF>jK&y+l6lMBHWnW|O3O!+h@=omB`M1iQ&t#LoTN3d}QmU6LNiA{5N_%4e^T!{05LG<% z(APe4Pw@VZ)C?9}HEkEy>}sBV-X;D=$M843C*CbiRi%`H55Mw*&-{KP(uT5AVYH?w zDo8QQ8=1%hRc=O$B72N{aOg+2UuD7U+zdqt{Z7fcb#qM5&eH96kuov2ZUd{UL&7Zw z2nkeonzh@{#B>ae8*%K|alU%*Q5Kg6T(J&lW4A2Tl~=Jz1$11>-a6MI=q2q}YvYp=cLvx67^;EUmg zTOX5ie#{z=zx%)kdFGQY2Dstq{)Zm)A)FOc&d)~*lFBz}n^NWz08L0Vslj5fK~hx} zqmu&;J$#HkXU-v{!8^xjIN}qZyM_6M0aKM`v4%YtT)jsMC$_m zevdWNJ+3%+3%hr2qCYdu^x7ThYMN$gKjWnXNMBQRI;>gWLyr@yON*S0f#ZuqR);l% zLB-O*VN}-K7sh0{f`~}zP&#+0QaGGzFtLE8_%e=tPX2NbxYl}G);pK9w~LyWwjV1_lU&)UVrA{?)1zpgQfYe6JyiqO@&T> z+6$3{Ye%Xu#YN3v>E{+!mcFkX$)Xu+T)&GiO6d2YTcLHrDFqbSgAzT`^hr@cawwBu zgp3qaMvx>*$9itMbGpCj|FsV8y$cd&FSqO&4sABknw&UOHx1T$lz>R0_1S9^LiUf9 zRk`e9C?u|HqC(3&< zU^pFGdxn%HLP#7UM~DX?B(&CaA89OfjyaVAv=j&tvCdM6h$*^xO9VJCsDs3MGW&p> z2xc8P7ig?UNKm>&>bzz1B(zqfm?L5%5@aH(NFxH{2qwvkkL`cp?3CiSY~%6sul_j> z-f#^?cj}Ck!c~|o{R!G|qRB700)ev*sRT*{TI=vBe_z%I1TmPRSZSJiO(coJ2)d=g zdsrHcK|rU|!RUZ(EzVnf2s8^Pcd3K>*B*N4fyY(jv9!DbP_?x!6$y=?r6pQPBk~3= z#7Lp#gtYGHnyTCR^tz36Z&0|KRXOEik}zCWF$6``>2#)M zFK$NTCymF0?NX{;F~+l_Z`Lc>>Rwea-K${22T?IM!zv|O?-4TPfhQSUC{oJLgOqYm zhtF+emUl_4kBD&#DZhwnRZK|iIkcfD3rvx>BT7rO)R~~JvS&o;+~DNFio#2QRI)>2 zmqP3kQands1JTyfhk;M=L}6F=Z<)XQu7|&T+a0xwC;rQqK5*Qc*~2H@^h!^)>FTFG z{d9lz-?H`k*ZmUF`|T_93*X~h5K;z0iUgQ66#}KmK+7m{(16f*2cz0DZX9Fl(S;%U z1{(#Kh|vk75=wYVqlhBXq!E=d44c5w)x^c+6uJm3kKz9L#0~eJWU6D>x?z^-HFGR3 zjv3r^2SuS-zrIgQ5>uUI?aoVBzvDudPafsuu~q8EVI!S&f0*omhoLRr=hO5?Z;&0yn*u``|W{n5H=G}WRMRn)=(Zjb$5zkWc5zdjw zMp-2vjl;`pES&*ALQ<6ly-t~@k-kLgPNqEm& zT!#RYKm?_9_IWs;|6Fj4$0LTLF~f1o;_`^axntPYN_n@AMWbnYop=l z%bot3BkQ(ozvt5*_~_B&9Qr?=)vI3i!q2YVwC$dw58S2gX!ZNO*Q8jIM2U~&7ZDpl zQ=S~-CJEffPP(UV+(IV@+ zW5g&$Wejaf8Ehvd{pqQ{6NGK;Xz;xvfdW(t7!~uxJp`h62vty;lB5JlD+rR{{kr92 zhn{r*b@zSZ7k}<&`Qtx(%jrIfe^Du|5F$R)`F6dCkt7s_Dv)S|j1V1dYY}LA9fg(& zsS;8ul7}cfQsqrw@D`;7N<;*XN*jz!G}cm&9ZDypiUixX(`=9x2V~4Y< zYtTy4sY;{(+qSq6ax&9I5~z*IAl)LNoJLojub18ar`_?R|6WG4kO?6)h0%1%E~OD! zE-0W;0vj|T1*FhZXKdSk)dxR!%j3pi{-!tl9{=t&zs?Um{em@PJrIRfbPL67SJNpq zA>=_&h`FhVWC@?qeSU89wx2n9aNmKeU;N|$|D<{Qf9w@6 z|H#GP@qK*p-EUpLaOB~02gBjkPOo>B9oeVNuMD>r#;omVsXLXS-z(|&dd&9wOi%Zy z%0Qt7RaH<{1C?Ov8+zF8(Y!Z=8=t`gqO<5KQG)-ess-`Rq#%M$eNSVQ$ zLTZ%G=5--8kEABXEVx4{J&|!sB@%rhQeP{zS)_1@i0*|+$!JK+#t)8m!r;&>>2QeZ z@B`Ps|AC0QPq!-vY!Hp_Z)|OEdrOM(@{2D%-IvDWwm$fQzu_-l^P`-3#nYbSyg$!I zh|W_8gNTvV29ozobxKN;??aI!#5{NoDdLhPM8VwbEQNA(Iu-q1qT4AjX^eCuVu%FS zq6qBSxslr*IL6oS+{b9#ux6&m@%a^wo?Id(IP;9{Jn5-VW2$Hg&gBSmp*eP9g{BQu zWsP;WQ%xP<$ipX?A2y5~*f!C)IC*yw4DBR_5mu4<6w2*Wbb! zTQ{)f`A?;{b{(rH_A^{O&eU{4e|j1%z*)pz7*iCJ*hwXrp6OAKEvhK!_DW{^ z6-p$WcR<$S8d;E%BSc3MimIweQR2L(v2CZU`2HXHm4ElahrjZ%rN{EDAMbU;Ew>Sb zl51I7qy2bmw8J&*{fF7P2lPgt#5n|i7Njl7yA3)n_G64?2(hvWDey#}Dco_AJDnj@ znH3O8q>S5q6w2qlCxt)^VZwY6BqrOA+>&I1xSzFLtrqjSzI2lurgpUs&Sc0-bB)EMOA&iGd2D9iuB;wmtQ^py+3?& z^ZC2(;yD*y_)j{&O*{ASwU55%=!PA8-Wt4rUgKkLX;{8H$8W=f(c zw4&Q7D9eJng*qjoOpKGDZ2|&8X#_@sQ5vm5itK5EJgpBYA_1c%J*}ATcNn(`=Nu_G zI;Ft6%u#SI&_;!uyq<*+NlK$tZt{(e^a?12z*%^Twj;-V)o< zOTDdkmyX!ybtcZ_2!<^)#ez(OO}Si=dKLLS8yWqOP%FkouZ=B ziqa^i`X#+?fzldXWSLYZKIb!*QV>GO4Q8)HuiwiC^pxR_#uS)h@`^Pk`#g-KEDPi~ zx937+>70}jV-#gkBK0H5S=m1-1P~KS$^74!GH-{Jl!7#;m7GKA3qpdFd=%ccrIIy? zK_SKA!Y)17^DFm>*zVo;g}+{mva=FQ|HL@;j?b83edd$C?{r@-kBhqdvmfU9KmK#G z2XDXe`wQW_WjBWfltuo2LSgVB5rxEyJg`YZ(zJoqQNz-3#A@B*LZTV1LZR@srJ&;E z!F{CQ(OR%}-4rni=I2MO?IfOZ(HVU0u7lis&whH{l07>%aqg}y+_Co{S6p}oYr7UD z19RK9Gre{Lt{zdh5m`*La`Fi6aGBL(OB_CUj7kMeWvHVks6@yq2YG9!AjUwDfx;xT z%)i+#CVUJCWLx#8KJmGCzQ-MSjL-TpZ9D*W?%9kIe6cEvL$0mQ)Ka5mHinWicqK+q z3Y5w;^AHn_b5!0Vm1NKa78Yx|Jx%hCm=an^O7}23rV7NSOR1(euzcbn?z;VUuDSL$ z4xdni$fWL|ep(94e(qW>IJJ zMS3LmZIU7tLN*gR=N|P&#E{c>vb;**CnWH8oK3^l`>bW0=o_t1IO{;>hj~>R%0f{n zg)u4v>4L{wPjD?h_&nKADod({?c}i|@IFuv$E*&9tTru!)-tGDhIK;>$#r^DUoxir zi<$Ksueys%T| zgs0hFNofj_5V#1K9>b()Mh+hYg52PjWr0JHQiD`EBG@Ihi;g?lUA1jL|tUFw-W9F+4i`Ptwr@P(ERlLZr0N zpPv4#xBs#Y?R(z){uXF~#qDs{GhY1NZ##Zq-;+i3KY_2mOUu-$%0y*A%S79bsK+aS z&YTdbP)d_xoJ8yCWL8Nhr)LS7-*u`&)2S*tWkIPW zowCTDkxqwhr=l=<@B$#cL|C8MJCB5GD2bK|Au`iM3Mr)&ouE}m3OR=eJ8|QuitIfI z2{9D3%c^OQ_0#+@H;%{dy7nDMQRU$v^nD_<2YoXd#aMdTopH?Wu*1tPbJYRIm1h*E z`?7jW>%h%lWXDBUo*P{AtfDZOi62J_@^_`x1p3KD8mKT@p@OG&S;niCBoG-kElC(k zqgWl~v{Wr}YJuuJMM&{;b_}*t+h{v#gwnItFC%=3TXP>c=E6<&w6sssH zF=-X)3N)TlK{?fDcHNaA5{LKR!F~50!3Tk}fx)<8Shox(Ub>JnSUAS`=x?^^qq^`HB6XzDYGiIf6@kY!Fn5LGs~ zXr)Lh~GlsPrHzIY1mDM3-sTd4eHg4aE^A(@T;+7w>$ z!tZ~qkNh#I8*jdWgiuweXN4QDP3?Ft#dazs*FSpv!CAR@c)ONzmzHs}B&}D(S?yQ* z5xOa+l7tQk5ksbI`;eIz)`z@*n5^BiaX3z1?k5Q*K6@*|WP=fcC;EuU-3lp2Lhv+g zOY1V#+qRzJxM4hQus+h*JRxseNAw;gQvT5|GFh}+6&RhT;#n1yw5ZGX|ve_{FF z>*DD?kAH5d;XDX&wozP>w8vEgkPS&9dp^3>8MSzfStp!?Cp(rXvRpx}N zrfxDrLL^Wg*G9%|#3#kL$r2);A`vh~WvNw6#E{>#D=CSSfs&8{q$CQBmjRa|F?m!7 zn=zdyDbzKm`waiN^=rTU3+Q+L3I6%deQu@1*6EoFU1+qU=DKfS#wX?L| z)A&e0E_SM^H(vM2k1Sts`C*FQ?6oU{V?UIFpDIkmx=5$ckOCnkv?=J61%XJCc^K0M zan{|p-g3DR;}5EAprT#bJ=>EAGB-dM6+&y82|ZbeL|MtqLkc-fP0B>w zB*tw^V?EBhZ0N4H$p_3pu2jtX3F_GbFV=zULe>Y;9#Q17j+cH>P#af?ZRhydS!3rVfYhP=HpKzQ= zP)vT`q@2jQ3eD_Pk6x!kr(4nK_UKd{bWu=LRfZrU(1ppScC9f+=W7jt6p$u<5v6mQ zmr@eQJQ+wzN;xA{oB?)1mLE%zqIUxuW2%)DZHjSOVCS)m$Hd9SgKIu>%R@4`qjz2N zwh^Ykl5}Uu%FaScazTg`FMry1p6<)-8?Fz1>?3$-)Sd79366U*M8m11>*%177jYq*=(FD=PMQ%gy7=@Nmpu8{@q_!|TfOnv zaOMSk?UNs9%5MMjLFyk%G4{L=1O%E4TM;G`N28QDB(3%MK?}O1=ty)D3H7hI+Y|gj_sS*ux6&quvumIx`OEv=Bl+^I~;S*{w1!wbjb9~ zEbBKajvr|`ete0xb!^){g)IAwmya_XHCQ*K>Q(gH#M07`K^@T2BBZ9*DJYVdB$P}d zStIz7n@kB)mMg?Skpg8He%I0K|NbH&+C!?QZxOYy#tWR;BwQj9*VNNMEjieVtFwu_lcQ=VTC1(0@ zoRDdylvxMqJTYatllPtwCflPhanOi7CCw74KyV)C+~lTlgy0F2<^mGV2ZnnB%D5}oiZQpawZ=5`QaQWpw^x{+Y-F2!CRd>pF zOJki=6SHYWr&3f}QfWgVKlFrjsH6amL?M8FDd|-OD&=k6sA*8RwTp)zTz|T+%SY-T zKlD{z_>({UC3I>yCYo!!L+5OvU&E_xfxwK1n-3DCSewz7(JDqn|izE;}5>ucwl0r+A zO!&z*R!KpUntI%9dg#78FMQwUZ~N@uyyLC>=#T&OX+4w0r2*T|zHr;ohmWjL5=@cd zd=8nn&#ond^5!&R?69hgb3Z;lbl9a|uO(7&E!s^O^)#X*Dru(c}JBwC{ z!W4+;NG{=nVl-$-NnolHQy7?dPmDHHWr-<@M#Y1b-|crXQg{91-x5PSfBwkf?<9DH0IMz92(*$&3X+sL zy-y^R5-4Rd2-R8|=Sf0f3eB3CF1@a%l$JO?i7993&h{wu6w5=)9ry3!$ij$o&pwl~ zEV*azejZ*N@su*l`MUvDA70?V5zA~xLDX0m@P5pZV`Bt~tvg0IYl*&Pygbk0V*`dw z!rDOFScapTVO>+V6FG96NExLh2B^Kqr|dZk8EG-PQ z#^W#F_IAGaxlbp-S0B3f&U;b}PwEsI90&=ma;M%YG^J5_>w!Q>O{X+;N`usjlp;rt z4jBz4yY_5l+xeHW;jDl*U2w~+9=VyO9y8tVa>1EfICyM@mDN?mRG+&KECGU*fn{ZB z84+rX5hUNTaB>wP1;Q;-R^W7`P#!Hjp>2>vl$1mOt%*YA^|p_MB$2WMNg}1d=#*Dt zlZS&85@Sk)(r1Rj{CB?QO&`DYum0qB_&2}spC6O6zWECu1!W|z*?}9c)gtyJNF_o~ zE2*>+72^--sLM?$iP;dsCP;G<)&jFaiYZd)8Mjgh&`AoDQqq7%3XuRpHm?GYNL_QX zL&{RA6ho$cPxPy|z^&_=7$UB9 zcpnKwn$|HI<(NeuBTd^-w=PRKLZWT#M2u8u~+@_V{&c&Q|hKW@8j|d&gPeY@Y&B-B38&isRM=vBfwz^GSTfR8Vh3^ zAO=(=F_roFOa9}V?*HO@{+Q=pxSJPTbspB#qvj^F=huX*h+^M*fs{b@Use@@x?hmk+|l5TzS;qMo%6DcWT zjC6Y-W%g>slpFCJ5t;c0-V1CLnc6HBF(np99_KvO#5Y8WwBAzNCjSNq=(?0WAqvVA z^KiiwgfihmWKfSuNJ2=H`!3UViBL$1MACRinhX%FZLgfW^7}R{K6LxHOuE&7{<&AM z{JD41yX3i7r8vA&n?P0Q%x(Z;Xc;$k4uJ4EKG)jJtk4>wP`6L7S^FkoSI3|D;@9}m z7e1f&edN>g-|>|5-`_Ulr-YDX6cet;Wa*?uqO1x+)QsB(DIzfh(W$EEz2)`4{m#9& z-MDnRAo8kVxTMvx@AGP(}4uO>Y!!*9gak1RQ57>pP!pC#K~`d#kh5h ztV0M@bUKxaMxD~X>u>(I*Ym1>_0(~9&HA@DzW#y1>R_9#6~1RF3q!BhMM_1}dZfvl zjg%6))Ojdo8KV)HYzWMwrf{S?*Fuf)F$jf)j*sAdpB( z((9J=Iu*TM2VIuv3DB@83kqX$@)i->T7qklWbkZ^iJ%R3G{Texolb=|nv^`_aRXz} zMj?dCGA;pHs!Wo9lmwrZCrXOR^HM0K)HFg&3n{k881vA7@&$^}cqyfqA~`{_NL5Rf zR%41w$qg3#LVJ=E`;KYX91)>ix$PhRs7)z2S#-uWVllm;HoZ3Ja@DKOf*{sUe&%<5 zQ?Av&c*Ae=%GbP(UwP3}zEi~ZOsO*WNt--4wG(-NGYMglQc{`%A+l6*X)t7Qb;z)B z%uILDGEo?TA|R7Rt3cW5k?e?*3(FimIp7Dr>&cvb#ui9|>*p$Nx$^-wZQsqz%rqaq z<}2KB{}F!d#ZTwLD=%Q}#_cHS7%m-Wv~q$F3`Y(xa^mFZUlFP&W%abbLTe3GXep+SvQj=`Os+syemqC(j+B`F5*HyYwH88_l$;P zn!2HBEp^l4+PtQ&MfkRsE{opTT+#uRiqk)DsVKJH()b^BMk{b~5*7f$K-o0=tj7|r%7Hk#?2AFjw| z%xu7pEDu|}&)fj5B{~G8$jNO!CPr<(PK%j5)k{)0Pi>;`P52|MElE z0MrJ6e(TX4?tvQz=3mk2c4_{P~JVq#%fiB*a??4kZmaQCH$xMJGv073p_1S{np8 zjVTg|NfM)&3|0c#2#gkZmqQ)e7^q!D%AE4%tw)k&kEB3Hi^--l|4#Vih(xs1R8>ic z(6~ellR$?UE^CkOJtqZ!;tzb{SM}p-D*C^z-bU7c=TBGW#63SQOIe~6Ww%6Wjcpx5 zCS*s`xXy~kx!jkF7Nu? z=Th}&jvi@-cUsqMBPCRT=n{5Nql6$vk5>82rIH9!P!=W5`^ivEFm5elm$%@f)?s~I zQl|6yq~nc+F`c3?+hTCry|WwYakD-Vrb3LJ7^4Cxiy{XPq=*z1Mk$;zG^wE-HI${M zC@Zu!G;MxvCYhVs)(5=Lc!CsT9yWCav zK#ExsU6vV;D1-njh$(wjAQ8hPvYsS?LTg<-lI+DKF)}6yv4J$qd-oI`31@LaDxYE) zix{kE-LM_Z+qP+zqAcDjQhe7p>w4Yy)oc0hKlj61jz4(sOPq7Ms){V_P9oFjCkhEU z0Z7IaNlD;bAP5hVN7)#SwX6;zipW$y`$t7+7%v~eHZ6DGe~2@;&2s)0gLLy0TGE$8 zwyc|`ke+?}_Hol)4{^!)yLtLkE@u6PHB8O)&??dGb`jgpB8I@y{5*@t4|D9{11zpW zW7FijPuK~-)%idS8B8oEDxNQ{Z>Vbd%y4UgPFMjPYKl5)& zkVXY$Y;o;~SmYO383Qxx zx1!@o9^Q8!(=%QA{fc%p#B`?V&dss9w7`}*$=Tc2aPR&Fio)=e3%3)JpnJw^$t(lEa?&dCx8h=J*D^=J&9kRU{YrpNj2PDqbeG+@3u|aR4U1I2d4TOEh8yK zRH<=3kyHm86p8$DVN*iX2`?>GClZ9BC=64xT{=b)+cD8AVl;%*!KcWet{JRG9Fp3$ z`6VV9YU^-bB9g%SAyO#1g@F_a38cy|F>ULKQqk#DNC{1ANztt(xF`SRHMe}}=U?%X z(|w%(q*4oa6H|O{Yz9{sT4AalQk8@RjdKjgj@mlLE@DGAmDetNJG3r8w0`Zze`vaW zIFujuuX_29^3y-@Bb@b|AH4S~Z+qQ`Ml${yO#F{2poK)lEd4ABO{XZ(Rc1su$@(=L zHa_#Z4_^Dg={%G6*k0cGhTl4?5b@%uVveDgAaza!N+IC9BPNJRWPll#nA!;&3ksdn zy!_;O;(X%&!`)j)OLmp%|IgMVrB~ZM?j28v5eE`TaE1WEXNKVJZUY3%z~I3Hga{n($?VMCx>S*{?TElytL?bJLIu@ZWQC2*U9eWBsVc@G@9+DDo^+A_ImT3 z$8J=JJ+o3ZDJ4V*hA=QN)}mgEP>VuHDL@HDsR{@Ngv~k%W07^bn4a#UlN896=?4@t z2u2atx@b0PXxC#j;;OhXM#I=@|BFJwqY6ZnoT5>W(5%N`oK?tqTX_H~bZ}*^w$4_P zx4sOS5egwH?+IlT)|hHa#|20~`=YP!1So?A5d>6dr6X-}SYwc3{TywPCZLJAHB3P=uXDubm`23i}LbUH6P=DnZ!-EmwbZ+_>8claLnrn}$~ z@#W;CgUu1EW|gQ)DT1Jckg{U@_u0+%aWWL}1cKHX3!_a%oUskt_8C zF(i-z^(e$ZJwmHS5eQx-F2ke99dv16bO~(~dRd0)Zh}sd!&v0?X6wSCk@hb~XU|{0 zaPI>*r=2PH=}&+Dzc=X46##&*{pXJW0H1mH8;iv|?|S`4XYW=?=U75LlB5TtO4J*5 zgn@u}24$Xt(kgq#_0xrwQUDUZf0FB$|MeC8%Uj<^zjWsD`?ra1eL#E9)V8npmY0&!4vI=utnHDU>fX4P296@^75ePe>b zgH;4MEx=HvKO3tMNDjfOW`=PV9v%T_;52|_3|d}bBKq}&!>}rq40AlrwAp7|$?gkS`Mv&tvY z4+N|_A32;gP-OurCF->Z-c@0=CYDID9IhSM!dee5IKn`pQHwCxtV6XM@B>ZM0s-$F%CbZ^&CyMB+wR6tZ4Y6`@`~WB9C}e)m71iz0sLx=UX|nUE|H{pMnIMuK(K6v!JmWnqbja}|a* zD=fM_g{fYNOj*<$5o)yvQ7AAn+^m}FLV_{@IACHr1K|@;B!reDn4FqMqY-1x){U6e zGFWzd7t2>qq7@6o$k3VChEO^{m!O2eyBv+-CE#HVCfSO5O(3qt$cphwdTuR@(eTb9 z;1moJPWTF$%c@ubK?w@wQTU2zWvoY73-1Rd_sv?o02>~8xYZZtG zB2j6Y8FL5P4G$ivRessBEgnbC+Objs=LsyRcR=Zlg)S91)@(aeW4m__f>v|h0bZ6MrGeK4DU$9E|Ni7-Yw+VsejVI< z)rBYcWRgh1!32Zpn^dg>hynl)ud5=F_7SO!vI_NRDZH`pf&iupU6h1E!HKCY8=v$1 zv%d75!(Xyaz3^qP-r?8(_tuI#m*Gnf-!rmpIyt#Foq=%)!U=lavckA(^@LW{7zb@! zMNEoik2``UY-2=UvbEzC78LZC!_AY~!BB(xa@ll?gcv z;TeTiIkYL@m4|m8nki%~13aqcfwN%T!GnMc2F@wGwaC*HMwbXBM<%Mnhc_C6i0Tf) zs>X`o3<0mg9fw2&58Dr}lqmYxSi=MuBYlLk2bTdFaa?&!iV{VZLX!g8Z~gVRr+4IaQ$nD!4&_2}1OlyQ3@JFg*5CmH z6JbS5R0`T?5M6cqt#&ALfN~D4EwuCCVSsw80U1V6g0Qq`}~!iVUF?h(g&1pH$NzZ5_I4fo)S=Y@6sH zQ#NCLy1}>(Wup+9l&6#%p@t`tU;zqSYr^#urIT!WMDD~ zpp-!`&5))&bf=~PM&bN3b1q%)J$~@olfa}rQtRRn;w@ll-wRO{NFwB5oIo%JqZERu z1}+3(G8i~$Phr7OtA`jGtb@X#9!m@i4r6H6dX9v5A51)h7* z-e?XC-q6&Jtzu;#^4|tBW*mDV9G6avoBhy#S7M3s+hVH|-Y z0)PjnsBmn~K(b07D1{s$5qpW<>EIhyo%^0_NAI!r1eONQI3&b56nR>O*2wBc<W zTZ?T$Twf6}HqN=23M8BDh}|p6%Xvadl&T_A6{P|wv5kR&Uxb$YVn*CtOPHJjrM)uk z&FM{!?dz@jC?oDb;yqw2Xu!c94^83>oC|Pf5J~}M9D-Wo-jO*A&NW)4-}uq*p7egj zQ4I$kd6fChS4?4y0ZIiIRM+W^fl;b@SoTw$OQoQS0+cb-8+E`}MY08h0|%D@taH3b zc6?T3+U;WH-M7}0$?f~0kDRPUl~$g3RQWd6fKq@^2CV^Q3`*Pg@VIK!N-YI<6_;Ka z4P!i*_kgzttiAKf{k(HKRy_SA*Tc*Hh@~gI>>!m+>>h*!GT^X~P})IfDe_XGFqNDy zQxz#ypAjp0}KVv%mif{QD_~A0lDr zmO_F^0mB$90UVR?&H&bdQ-Q)((-1-_n0DX{@Y;e44#pT9@o?Tia0cQubfHn^1mGN* zCVd$NgJc1eF5#iUnC~aa8vq7`ICy9{3rNmTd%SS*1T`U0Y7L!a(3C=3i&9q^I|Ou? zC!Oc{NA6s9*4y8ObH4qJXM219>N@M)?}v0{z&70VCL#H-Xhe`v2u29fUIJ?XTly-{ z0TxzQUN!4HnB*5wS^N1(hwOfphP2<6t#dYU46&3J6mIRsFsz$ zBA?vGnG70*2RdnqT4)dj6+)F*g@`lM>){g}eI5u{>mh;wQLPSREvC8&904d}2;;gN z7;O6|dkt@Y_lE!gb7sxSMhDtI+qUMRm#eIIG@OPnt0sYz9E?kNLSdCbk!P6hBp9FS zLRqY74Gw>lYRsnp?NPv0`^zX9)QxK#8 z(_IMHg>277RIg*#ox8aDwiVbmwHXKOu>eC2iM@AUf^Fl|n40Q>aD&|z4rAv%c0(Km zkTL|SfgrZ6c?jcc$IxgsAh?6J6jR;mGc2`2sY@8Ep{=gqJ+}H@IfqKQ(wC1ZQvrS` z^`J~_h@0&jpR?}(F1YlWBbh&Ag$Dr4-DzhqE^c4Z*?yb$YCa(Z6ctjBI1i^v5b6<0 zfv}&XDUp0pN zR&2()%>s{Z>|iocm^a$Q%+VGabqeNd=yq~soiQ|82Ca4jJMX^)s#I0DN;-kbu?b{_ z1`i~%LPIcJRr$O>_VfmnCOqTI0Hb8S}odan;H#lyVZr zkq_d!!(@kAmyoVNe7?^p3{p+)G=H1{s<)#|X*A z$~%_5zsU1XbbK}T_0B;RC8P*e%$>LRYrW}-`(N<7cVgc?_5c8U{qrAQvub?Zn#S4d!$*8vQ*K&$Gjjq=hRmx zXTA5MJMP0J1V)!fB;D>@<{k2W>Ti~n2&e~~f-)MM6NDiHB?iq%Ac|rL9`t1lmB+^u z0}lol2GE+K$Q2S>F6wMveaMIY0S@4RhzWvP{lVN+ieDXWLddBwU$*!%B zJ%pE^bZDHUz0;)tp^SiWgMgKAMnecddk+H-?@KUBVI2_3s2`41VJc4&n7lt~4t*)hv*{b|dIWzfY5aRX{w2;h{duSl4Bp=DbpM#= z`c2w75NaT0l{8Phg4YC6)Ignuw;71jBJ}Fi=l|RLe%F2Umg#4DZ+hJX@xaJ2X_7Bu zL{yVw!U0e?<4_hAmf5&Ua#qok9C+U(UuXy8EhIMxBnRgl-V=CJA;FXF)3dGIwAX+9 z;?g^Q`{VE6Ll2EmjxIZ8o`1kyW`&b zAH?O$?!(eOmSFEamVmkx#3ZnWAZ`w!-fDyS`G^MQgYf`c*R8_%hAq&#nh+Xmke4Nr ztUy|rejZw1rsu0B69qsl1xV%bupZ!?-adDiJ@2sA0QaqU+V}kN^^6uCzc}wg%mehI zeRujpk);~`ShIdRZoh9m#wJoQO0aTs2b?q1>oNA(btd*&G=%wcW?W zu`LrQixQp+#Bl;C96Hkz=uM3yQwCD9%9K@F6j=ftc<2nUB!J`$F&AJ=00dB~gmb=f z?syJHLl_0PFAGuD4ms?Q&wc2&yYD#r&;#&-*Z(D2^%uYW-T0M1 z{py<8a~58mbvxg5*1TA9hA0eWdvIvZN51-<{DmPp`;ITcK8q1v3Ep4g*976hE9>TzdM7>cVZ$T+6 z;q7(zTpvf=|3ez5Y-}yo7gH@xip7jssM4aya!^pDog7gd_oXP+?4Qy)JgY|Bc~O8p z9w-=kP+my|BhaDN;NSwMiUrz7s0WLYT^62C_ z@>2EXRh2rGu__EPlqKRYK*0Op2I?RLMG#6f;utg*NSOjp6hi7=cHxh|@XK`F@@Ld! zzTxhxvElBk_b^#{D0Nj=m^h0XyoPxP`m(|z(+BMK1~1XFnz&5lE((MNrH4@uI%kmBFI-+112 z#F%Gh4*d@<%OWt!maf~dYA@<5owf`m!jM(QQSTv$MnihU%!9)tU<5=NC{qQ})guaC zaU{SA2b@8!H9!M6M^Fm^W=o*V!MFsM5|imPKnWNlphWh~ss_e+5Jw=HKokIs0%d7n zEQO#16dpmyA-RBd4xSK5UumheaWKGsEFIsMbhoX4_uJltum9%{pY1LFi?!2=pFy?f zj?jq>Z)MaCSt6b=e|zO%at*C4oF!o1Ll6o>9SCDx5!Jsmao4@~-&x>q79EeTUtD+@ zPCe?NEULH9b!Gm1N!)Bf0jCWJWe@@&&cc)>CpX<8w+!$5#A0#6AX<0!3# z%6j0eDu$dXo;a-LNs2tr;i*JXT4c(Aa5=Q+5r@wF*>{fF@Y;WW|C7F!MkLYQzKIMC zHXDH*=^xlFdPx`FRZ0r)EWEQ|0HRQ$-K^`u;gN@)Ka05Aw*Tep)#3=OikYZ z^WR?bSFihq-+l{W7>;b;w)O2(+qazQwHh|YRFh6y;q8G6!Althg9gzVxbx7NK`%`! z7M~Er`zJjp`lr@)H(Z601-sJoKl#R2c%x?K+Myi|fDoX_3K;9)K_LV~k|s!!6mcBF z`%2neXoFswBg++h+0L$N<9|NY$5GV2qdjsJT+qlE_m%v>@H z*7_SflaFa#?oO$P0L9eHap5 zcqoR=y6AQ?-231fTyXuPP}+e|iurSAV!^Bd3^f9*-LM7sJ+dCem3aO!dtzv02*!Ez zdIsB8Y^`KkvwNu5Bj~(`EK{fjmBGw{!B{}$3K|@q1z;jX7)dbt_(aTu3sFg&0tt|c zm#vkmay}S^v-zp{{a5^w5VAvMEh1{*XV0(Og-5+&y$fnp@Q)`TT!01v9Puc14r;1H zJP3u;791WzN={Ar!m>ND*Vm6Y@RRL%yI#2L$_vvYPyFuyyfa?@G629Q-m&w>R&(yV zOqMXG%NK@$WOa#m{O+7j-WfT44DyPqpLmauc>su96a?P(X;pnnhp`5fkSYj{Q~{K=fCD_*b9&t)vz;~n zP~oxRq5DuD92pVb@6xE%P!bgM_89;&;1u=9SN@~^=wEP#W<3JutncUX2S}IxYd(AFCqMJf zGu0=)dG0f{J%4w-@7-tNHD`aDeC61EP9JOrGvFmSs{jz*dw9l>ml{3g(MdeQzHHHX z*T;4e2vR_X0+ca$=OJh%F_Aok_ZE~o2wI_=8Ko!+2Ph2?58^1w67Zx7{G_as9~$S; zF&QYUd||eqmJ45bLR7`-24Mt3uTaI~U^f zuRH$bR2RpC3NRr+C?LH9_ZHLxf&j{DxGE+XjKc7s_LKVXtc#*U8^HBXZs0vQ*n%9G zdE2I9`XZb4{@rQ<4+b~}<{3i95r+aya2RXgjRkXd$Q9|!uD|&HXSDFxc<+tawEX4+ zn0L<=f`f35dK`c;jyfl3OM#bR zSD-gJcEI+{TNZ+_hj-Mq7ZaPI7!Oe&WWDZURVsMzzy$;295RrI0GV%t z{Ou3wBc6NG?%Ov$_ReNKtQRVVtkrG_Yt46W+qCZfJ@-2V=p)M?UpHU=8#HDvp%W|b zIHEJY?IS4D;~QbXVo@P~i!84c7S8^6o|iI!_h_{nFh(QKN=OE98G#8=;At*AZv6ER z0QjXk&rb`28qqpKl9ULe0M1wBPE}|aTY@tW3WCBI6vm@;1WBrq5tf+G%Gf(7ji zEPVu++yW8R&}eWt-vke95VDCPn?%-~#^kn1H=Pb+Bo&O0V89)8Tt+D3RwH@Ibp2f`>7> zog9IrFp8p38cCWV4k^NjS0-*yRI(2WFbV5fmG9vzZZek=gc1lw7ZO*VN{;!!T|fQi zeEYh0eqzUbm{9}uU601(#KgERGDqPkClpc&1Yv+E4l3LoulyO5Fi0x;T_;k76cN8z z^(MdFTmR58QFG`kH(!7CDz9_|r!NasSKeBw@-|J>R+4aZY{scdM$Ln#>si*8mOuElNJ%p4TBH*yvBFPLaD3m2I zhCn+C4+jQ|D4+-;4(9+V1!}PbtcH*bjZi?!5DpY!Ea7Yqxps|-EgN5X%6q-pyEK6=*c@cDCo`n2!OlUZuZ3jFwt7wi%-e}V`_znfUW7_6#N>|2yU z2_RDj-diy5Kskfs0;Yd|5&#%nUm73)=YYP1%6f}wtHFeTaRkNyg7K=$?g0;mC}4oI z@Y+C|N(RZism$!AgmD4{RVEGtfr5o#m5)R+KyU%?2#8T&#KP<3sK92s5>D=Ww)gn| zU7z{aSK`yw>@~f4?Y|SPWFQ?vp#ksEOLG)O4~4cUEJbcTQeEL>sgT#nTJx(q?___t z;mOlr#o%nLz4Z!H8=7@)R+c9#r4}>l`xx}fTx_g`3qSAzMuC5jc8&^O4u=oD&PL7_;T5;3W*zftT zoZsEPxe*HrK`A(`OubAHFdo7aj;wT*2hduWEQfh~Vkbj{frK?>KRld3>#0Vsmml`- z@BQZYLvt4C7r*wbCv`1T>mP+R`pC&m8+V4Q3L8*@Tp1MFJf1X-Kva`9cpzewg+AZI z`GinXf8x}B{e?e-2;;{1V-LMcwSq;x%%dnx>OARLZAZZvrSQa;`I#&3xarT$nL91d zCpUeG!X8qK0zW!3y3DfRJ;bVezV^qPfIr^!#C6_r^=~n6;SxNy@{!q7kKX(`lc#Sn zN-cCA@PxuR1EotiSbzbH3z(v)lxN=>0!;LamYX-jzX;8Hih+DECw4PS`o18`~wh1 zF$ghm9+Hrtp6rvtTa6-VTfRsU%AHf-dkV7zm(K++c9*CgI5@|O<7)b>6VR#l`uU+P2 zuXzJtMF;CQkK?vSx55#IBM;aGyDyr7=~QFQhB1uB9-|{e7@ai>uIwN$IZ9>WtVYxx zL2Gyh;*eJOyh0((6}pLnQV!k$)K`u*rYYj#iq>MnQfcF&EfT&WB z+RYDMrZ%sfy!4JocgWsec+ns5!ozk$$gv=z_9cyg%DVK3ghf5%2snWt6iyisFvM|; zAe4yW0Aa`x)*`gpZ8YljFpzAi6y$i}b7Kfy`LF+SYWag-UI+j<_Xpp_vb$H}-(Gf7 zr_mhz^H94vt67hB3nMX~F?z5rvtztb`&(b3+`z4=%a;SdLl6Dq_(p6Du4Xjph+CpVH$}B;la%fitdO7PTq2kf+UvbYhvj#(d$>rDl z-D~;3)*-VSIP&yY1e;c`{CF}sv2QI35QPCaqp+&ln?hIqj$f zAca6!wGm-mMPTvp2nEByU<379h)8wk0k%}@LN;P@Ficw_h& z*4z4?fsh1)4S|RQ-deyZ097zjt{h5jVZBEX*MGM2eg}UK-rM^gS^W>a-Jm7$YUP^%h}_!(|w_p8DnOs~9g$LF;CqwBqI zdL^#<*~OT5TfBdpcH1CG2Xv3)W{w=~Etkd;-qVqqK{Ya zUvTl$x;H28Hh>k&Z=0jWH++HUY*ccfQHu};0T?0h&cPZ3YYgBlv@ys^gRC%BA>zE} zGVEBbrSpzd$a>nBj0y-Dz!6$e(sD(er)=L7UUKE_*IkkP@#;T6Tiaww%>jRw;NVz1E*90p>J26vD)!$`Y6 z*xk75%5u}A( zy@r8-7Dk2!(P}pQ#Q69XXa39S)93#BBHVV#PjJ`^-#|9pbL|^knY`CpBek)f1@Vug z_RNom%rA-qx~N@`PzzXtQ?omCdZNjbli?hA5dWb*JnKEI*t~q!P6y-sKm6&5>%8g8 z3t>qRO{{RQEY+P!5mwE3tWN z51rJYn`>XbTgsksedciH%?m&Uv3y*&eco5Ixi~!d_D8HXB zWl)RiO@y^N!de}*MicdBmF?lZ2PhENBGg+AL~)ETjuFKXWE6l2K`137*63rs&i6Lf zr>}YCY1{VvI z4ATwMq98xfNM_?>Prh%Z=rYj$~>(cET*7cU%{nQj!FFO7(q-p1%&5tgBKXJApMP;rx zrAD{cLsBSACpo%FiEgH$J)N2{f6?blrDvo`I>$RePyz-IXKckMcYQs**61WTdc7QF zVNetrDlZ$xVDiv2KYHB>d(Cmb{=?Hpy7=W!zmLHKpD#Aw`{$2$y4`~^rQunXd`nS{ z;wd3ul)!nwSO+1()j@msBU@HHuyJ(HgK*~^|G1iD-R3b2lw;%Jz~E*$bFB4fSO*we z{YguWyfgq|a2`C_<81iccNPAw9}us!vdNF9=rprbqz?O+*& zvL2KXaB4uR>~k>U;f#iJ2HHAM1`twpx0SZYN`hQ_l-9wJO7==Tfk#En@^AnMjAbzW zU@=EIN+XaIAi4FDA9d6SsGgf zxH%0t1wkC@wGdKLz-chpnZ#swUGZQ4_0YF}_9Oh_cYpY&umAEZF2haNTqaiBee=7u zDtER3l=bk0K{<-)(qJMfG0`iK=NePJ3|X#y5Y@LAS;17E@jwXF;t2Id3ynq-K^!Bf z)ezU>K^ZkBU-F^v{P|O#`jr3lr$7BSuj7Ube}G4R^>MW3?|B+E#e0HKhCB>Vc!ES* zbPEkl#H!lB(3iC%^Nn*OVHBZJuOX5GjYe%qP`b5w^jmKEmt*$Cb51z}w;i-*0WOS+1|M&JP2&PHSmjpS&Kf{K(^}T z@BIeO|HGef$89&`)nEA`-gVO6HLrSy*xvS!nmql0F7iE$QcRT^d67da1*J+9WdT)| z712#AQ18G=A8ca`9BB1DQI$Wj%ATT*NqH3)?D`uYczP^#Q!6%N-r`xW%eqsqv_?_N zX#b8zP$<)v%5hM}0qfw5hGY!Zd8B!XZdM>G4FXBgh^hu?tJOq3aDXcjMll8lLbTd# zv`F*Ii}I#W}an#kb{z=;R3C8#UFT>(xlB1ysfnJI$xv+;c8rLrE#v_t7U zz{D-HcG~UhVQZjRvFaadhU|#-tP~y(uUHMc^08ug!K^{2^W!N2<&=O^fU_!~%~${+ z5R8K{fKVWi98yRK5r8s*JlEh%V`yX$MV28=70Ob<8-?~j1MPt!G}^;xL=KbP66?24 zVg6_Xu_SP1hK=JH2xk~>hhQv35I4{o96&a`6Y4jz5VRWCmAInx(h zwPU)=CmeYw9Fv1tZ~BF0Q4XonR)@+=qs$bFqDt9xPQzIRxQZ1-8Lx_wzA=fy!#e|K zlz=XGqh6gFa2&K#@*o+qTetn|YfewU|MTDDHShWy29G}_&mXzF-kX{_sWa7ydeaHg zEC&ng7dK|ie~UMMMQiS2uyQIB@xWcpNbFUw*OtV!FpfgLG+Y1Z{jnNfGh?@d_H`=z zk~4a?Rwb~z#@e)4NSzTiKXU47|e#OvoULI#7NB8~{bK|qAnYPV$jV|T5( z>B99N_{10Rv-2+at9x|#JdW4C>ZN-p+qZs6YdwpJ0LE12wzO2plt!U6jCBZVwQEP` zE&gP$J9TKG)L>~Gly&g5n!X7^A%y?{5a+-v^kNgv{o&zTZ`ibQ>YtASz2v+f<9ToS zD8KZFUwU`a?VR9+fTI#wt}!`1jc!(;n=2%RK`&EK*85s}@P}u->yux6=&qYZk|ZZN z2U2Q_(ik{r`bPgsTBkh$=Q#VOBfBXgk1QU0|RQqSdS#3!V_}z1wqu>-&c$9$bC# zJ)54ky?HY0h&>m=tL%8vn>b5yAIX40h$@3dsY;STCQ{nDPWcya)sXqojJ~2!>D!G$Mgk9H1655T|ER5iHlM zZhi2Q2PdAkz1gMd(B66J{4I|>@aa6w7a0gR5kOgjBsb{hIg&yl%{6ju(JK`uvl2k!o9U%c*ppZNT}x87Wzoa!D?C`~C)WqY}58di&A1cHN80 z`h3jT>zLh4GWm6*i-pdVNYf0xZh~&NhrGzQS;F4;_Mcw->68N~xAubK?m`I`vVa(vLWmA8u4p zvqnQ}g`y~tCMnW1MV6${Wfe-SwT3ko&brF$>1=-rX5g%@WSykK*m^<@Wx<~a=q)$i z|JV-yUH#H~-;AY)E}ggb;RipJ<;i>yKq~{KEVQ;TN~0_ttgA59LP|JCkQD`bMTzM= zN4L-@jIGY4@L=$O&S1(Ex=`@WKnjW|UR`zQC}sWdx)4DWLkbB(D+Jo?`Ex;0pfiU4$AQNUmQHLv8So1zrGTr9-JDSn z0thL=g@g=ZI8R|+m9%SA39TvwC%mtus_@{PfHM{_N_M7%oGht3lrpm9&9D2H(b#qN zvn%gP-GItBS+Yq=R0PV7k*ok{3Pj@GD`f{ex>RyIAjh#Hs7IuU|RU>voUG z(zI3<1&B3>qX3a)1CqFTbN4uGnN#`n&o8+Af9=ne=beM0d5c@qn^t|+>-_jQ4#25L z-s_;ua-?~ILTP|7h%nmH8Xo!34}X8n<@@isBrr-H4S;#VP#S|G&ES-QvkHXx$~+Gb zMu=<<4vqcv#z(LHzdBt1*ShwmdvWErKZ1vEU$+0Y^=m)wlo_Z65r_w}G(%dJu%w?p z?<>Pzv(>(1$?ki9@a|<-Z*L5aOioQq9FZpJY(fA=e0BPy1e_5F>LG|jEI5LIK}HN* zI0)+DUD*sGnY{iBKfU^er<{oI|KP_v{QH0YIiE(bXlWp)*1xkM=?U#-j7B4X6o6G_ zbz?1xYNC@Vq@{sp;jOjNIUirQ{Lap=E_`Z}m{seyVBYA6qhaIWEK6r(h2CFV5aT>N z2!xQ3Ou&O83?tOz5bb6It(t7qLq0DXTzvg0#~w2M$7`RJ*b{v8oo_^K{;tvFs(U_Q z%j_^NI7A@9Ifd&7oxyof;$WRYX&iFxo{;~P*28#OIIzrm!V2SQY5Uo2r278^1sFUe zagYpJ2KA~U^?-oG1FAp-%|nLv!Xuw^F_MY$hWl1MD|>y;2hTt*2WJGTb9O}Z;h|)R z0}crb6gfv6 zdJpdT)349SyVGy*G+?4ugNP(3rLeXNDixfAa02T&lp)Z@qOcVqh=^dr(2Ut1zUSs; z8=I|xN2jN|`(#ehSzsq4S|$}4gE z&%X?%&5KRed1cHU8E!`yXojeT3~?Z;LY!7zWXdTxXTH^L{bb>;d!C&Z<=Q+;kI`BU zdhY>ZA%#aAax@zCD$9%0whV)Lvv=SBy3T!QUTkHPC9*QLDx6 zFqFHZK0Fcl{6mb`BjGIwW(AO9*2 zS~Bwtuhp5HG9JgZYARq!C;SinKK7q$P^dLIxbIMu_&%0GjOq zaL$nTdQhc>)(&~0K>&2d$1%2T9FtQ$tXj7X%U5qllH_QH)p=pdM2f=?*aZjgzb9tQ zU4r)TEVyhdRNg}Cq@ScJ*S)oiN2jd*I(4jpt0|X9~5@@B-nd%~nIT{1Q z=uD2oJ7Cp@No*SHV7iy%(TyE!p3I=M#lE}F$IkOw(8)HeTD=~Bx@isWSv`qBaLgHu z5J?J26{aUU7#mN)d1ZVnN)2xvB0&)gil$_UIYEF50~iI>HAYc@APgYG5QJ4Jt~?M3 zf`A24G}<`z_{(Izz5AQ1?|jPpdirUnVQ}|D^6n$I9Y;O(7K~Tu)xH+k`YJfCD64Kt zp64jbDyf{3Dh*mGRdp$>gVGvCS1jntxMT$(ei4N`g1YilnEXY$NDfOo)2%!&GUaiZ z7vay1F+<*}18M<3bobqMT|HyJ6W6KUxI5)7pT?`7yZ;!Xfk&ychf-ifm-%d+r>7K2 zvTJ8zii~X?N4Hy`&~949wJ%45qh~Yk^Ur?cC)i_7%k%ocLtOP#z{p`CV?h*Da<^uy zg(!*_QR?SLwfN6xzUb)m4WIr2e)8RK0st=k$=L|%gW>pNkG!u;x^E!_LMx44w})Pm zqfiE!GAK(8Yh4;O+n-u`+>3rYzd`-1c?(zM-KkxiD)$H&Lns6&XCQ(D@X(H+Fdj)^ zk!nl2Ss8EHw(-&zz2Jo2pRWAJJDrCOPT={kc&T{swi`Y?HNN#ka0&>9PL^S^*F#}! zg>B3=v~iGOw0UI4jCbCB*%05ReKD z?VN{@1cZ9$wZ-FlqOId}{mWoV9gN zJ)GCDDupUCs3M288s1q5N>Pi0PSk9DB)jvDTcgFh;h~jJ{h{>O`i&T%rt~tthk3g@ zBiNbrNgzq#(91QFT%)uE%2O1^SL85j=R2dqjtXx$<*5DCWy_vXco07S=}%zn)=L4c zjx=faLvXgvLCqVtg#>j(8i!FEx-v1Yf*TQ+}Mg#q#YcP2ci5?I5_hw zl#6RKWmKW;JfnS}wd+GSGz15{w{XsZfx##wF8sEZLJxz-Zi~; z)!KL6J%-8XEz9(rsO_MJbHiy#_9S)S;PZU{yZ^}!)gErtkUP#)s(J*L12iex$6 zbl;;d`qz)*f(tL&q5Jcq0~cf6$_M5p+t&YwG5H+EDWqTsW!2yy&VdsRM*GiiSLK;# zYXIUQ1@kjz&HVQBUi;SHOmE%bH?DXfnY(Dodg$y#L2a`hQ4G{M25SPXkf9k1#8H*2 zLA{HJDdoP;IrPdG9eq%F{uTeY59nn_?TOuwI(cwv<74mBX>Zp+#$Y^xHmDM{^Bje7 z$c#f_D>6{6(Y}4ol3m_U3ATUd=ND|c;mSWG#F=BHpkX5p(X7X4L?N2B0IhZtfehh| zTV#qt))($`%WGfx;^P0E#=q$JBjCeEtF!Lm&!{{(!&}32S)h~T*gn}sFE3LXHNVlG zx68Ma&8x~!fAu?f-jN6TU|{Aw%oN**vxj@DYYYy85fJz}(B<=qH@kSFsfS<3g6Lhx zEPdydXZ9Y$S#NnQKC;j5WSdimna;Ma>mondK|GAG_4Pbgo1E4Qi8I3lWPjrH3y4uK zwq<^jQRQw{m610Z75}&dQ~_#ANaDeq0Yx>PB9wq~4r?5AR>Bw!Z5_1rPxw`gu8LC9 zk54ehz&Ky6h z_U$Nb9V<8X@aKEhp>!0}X^G9_JxnDE2kyNDN9{9;fk;CNK#&5qm;#k;Na4{O7=a9H z2%-ikEHn;4?{O{U7pp)&mG{>PsD z`@Gb4^}1(gYX7Vi9;dzO9k}m`-&h_Guf5ug?eB%KZj4`6kHb~{9ob@1@2EqH9hB({xDW7Bwwu}*S*c*GLs@DRS&lr-P-Hob zRaFwEbFjvtEESBg)EF&_BIn(yDKa%VJ?VM#lkxC^&ysBVDjGHJjKXk1!0Ew4@Z-5o z>J0kPH^23)JRTnPLwg;&8R=$XY;mM>MjEXHRTd;k66B>qrp#6nHa{zyBmc#K{9|AF z9suCR+wZ|i2QG2#p}~8tF}_s#U}Lqk&cefj3PC`yn}e^5dh13%{z&?cQ|B^3sWq~)gti*WD%gH^6`bq#=EHH_ z_u`GGABT~73)XZeCJyN)>0)gG=lUtp#=u$%>Mf`@@Wz2tj(WR|AgsePhAc@2R9X7r zT@Jl^#g)G`zrS+FnS0Os!8g&~dH-PZeK&qInHoR3)Fpapi8R%inCxMylU0W3vNC;p z5Cl>5^Y+X+|5=+iNB!!O>vq_+Y?)55|4zfbdM$WhAoPa}#B|n3Q(&aQFn2h{+@TP& zS{y^I2<=u4Q5b>~N&-T5k3u%NymjbZ#~yu*zwz2DcGxxjy>-;CqnNSR!Lpp#@==)b z2pNWz# zpah_Os!$v-aOM$*4E2aZ3iPuVJe2Eqqjh!I&J8e?8%$*uS?NH7`em*8yT3h6())_C zdv9~jqAQ#(H(T%a^42Bq+ z+n+he)sMgWN%R)Kgp6$X(<=gEKN*HWO$Z6b8LZXF(;m7>ie9demb%i1I}gg_I#F-E zdveVqx8J$7*day7<7=0l7hv|DdvDmZcKy&qH$Qy3&}1sBAOhW@L{?}h?Vz=-_R;$o zoW*SM{#WR2j}#YOv*Vx$%kI1%&wJTx@$d~7z0D+(uMC5LHk)+_K>=@}OM@)QP!t)I za?pk#FG_UN485{y@`xb1Z^rDopIGZLA`^pA zAeIab!BLL_Fh;-_AUK;}6lTTeKXK0e-~HZKasDO$*d+9XMF8Vse`FIMs!JL+>JeJ) z2AYiqnBXXsMK4oGa)oYMziblKrNYd+uNs=5|l!`g; z0eBEpQ2~ME04!m|?c;6E#NeDeUVHLE<|~PD$V4!&12ZO zbpqql3A#y9vM~Nmd+y>dJEMD_{(6;ZbM=jP;?$E)c4BDH<-WIRi#BTivdG#+njlY7 zlBZcv7K$omj3MDy?ZRVze&+NFyYv2gV*8p$N47n9)9drj#FvP-2L^HN&(YwF_wapQ zbY3IyS2hEz3j(>n5PX0!CXCXuRGO+%SBG3}p-Y7#FJSs9*h=fFxKJ9(C}?9~Yz1ZU zs1WXqF`z#wMgP7;Kmi(5m{)CKoK0kN@P}5b4a;`$g!BG2TXE1%Ghe-B)27#}QZeGH z;>Q{SxpxqWW%WRfWP*O5s5(+8_Yq&?lDm@2MVO zLTr!ft*>lY@#u=9U;Q>LyXqMu=l{ie)(VfCue}Nf9(ov}=HOU9zU6RY_0F6ClKBd+ zNPSEJy9WBrs>;-F0d>lCzA(9S{`jl57$ zrqW%jLc=Hvtqim_EYGr`b#(rfr=M_m=c21`dCGfx+@VVWQQKAL`EeeeIcwpqMidA{ zQbKYL8AJ%92vI#oSdYPEg+0`|1Y;DOF%VzTCY;eQTKBsXv`X!^4oYd1MS(mmz+~;# z_N=)dMA@5eEIt70;j6X`?S9ntl3Ec3>-9g}pwf0b}oq(zI z%W@k!J9y@PEpBl-icE-umIJ zMZ14y+uD^=i}yJYmtA=^jyd8$eCxc+w;sB5&oWKYW0kSeS=&#o2SNsff*=YcLczej zm4T4u(Jwyp%9YD5Ru^6Moz>kH5-?=Pub?AGaDDmI7*rv}t% z1cHMR4loAofU;D`dL5V|g)Q=h0``un4J#k}(;Yje+x)X%{Sw7J7op6O<9*TjkYuDD z1R)sXD078wH${?b^a_jVtVFIXED;Z|#>fYXyxjQf%Wrz>7x6#Vij5O!r4!ra@SOEY zRveX=wxunBu>=GZoN|OxqEWA7V4#6!JwQETQgFJ@EHb_F9sCoIe(U5AmtFDHrIJr( z9kSmZ$h+MGCbz8rXr@fVQVv5HGUL!IDj-L0st_~fs?_pQJAksSakKfpg~z?}$LXe( z>Gk(-#3QTLVNp9UBC5wOPfq3pGUObQkRYt02Zl00C_^wR;5;gpy{#T(&J%dTP>Hmv zq;pCM!Z7^W4R<|w^TCH4jH__{ezyT}_ zpuIt&66ARSSj?iTxO2nx7q5KvJ3ox8Z+K=1S8qOM3BqLUv4L0Lgmbg4C$Nqn%@nq8 zo51$%)97X;l0qX_2F3wgh^e?<|G+NCzUX4&ELN=Du^q)-PI)UHx$J!Jg7&JZUUEP; zEf*xYK{rvDn9ecP%`x4}(aRJ%nMSwpND95MGo3s(x%R<(uD^fBk%C@&z$~n~=f=JB z$t|CCM$G^LL=aZ!*F44a^fa4TU3`s2yu*x}ZBcqX8Y#pL7^CVLrrg~ntj#Z)gxuB@)LhR#0t z_~-w4)}ndt8$bRH?tEYs_S9cu z7uSFJHP_+9gXef1kO!imy}T^S-OH?;qe=}`C=?YziZVuKk*PJcSdGm~Hm^C3x$@(# zNZv@iZ7>=9ipK4C)i>U=eCF%lh34cYz3HX@wtVDeC+)&14&oHxuwX=2kp}Rf0MMTl zfL4LJzB;18c@JwmoCC^2Am6b}Q6V{sF=2lHz zd(Vz5Jihg*vvBg!2hQz{ZTwsFMt=@{s$pqe7WND7H z*F}+}C<}!&cPL6z(ZZYs;9z|PZL-$&@6H}viW?U1vEMfaMrW4a|Icslu>Ji<*Rxi5 z0KmhKuEK^DtIE0aW;F9OJrVG%DmDPYX~iNo3XD)t5D01!3Q`5l@QQB5IRhgE^+p2& zqoZgH&P1&>fY#_7^g11^Ub7X~-0>K?g~yVGb8*6vOR>|85IUX4y$`O&t@p3PqZ_9% zI5dP~58Vq(7uImhf%7nbL?RXnswglyo`4DtAt>TlpwSwHRt8hu6xvq8Xhu*S;wXU( z0*EMrkP?hgFv{SZgCi9Iq)M4|&=x341!L4Gyji>E6W_n%t>61Het*f;PibF|Jz!5T z8P$r3ZKuQ9ND2jYZM4^5S$`lv~NBlpqxU;06`cdjBEYB2MD78 zVHiO6Q@e#=2qZ@sO3j7%L%=b4?CGz=1vfp4cfaPuZaj0RKS$grQt-n##bR&G@hU4$ z?@doVZ*u$gxf7FyVBE*$Li}wB|OQwo5-tyNIUzgpn948#MhiS}O zaEDhpBX9?B3K<3h&2}46t;Q(9esJYNSsQ%BmfbT|+WDyWcEovyJnf=0HHEZPC@i2s z;0P(>R{KXY7B2Y|q1b-TuP(u5S5}|J&G$Tvx4i5`hK z1KGMQzrEq9>G+?_`q5WDggp;BL~dMu%Lkm+$3;ealXeZs zN!a-O=Ldnpqg=IaKKi+v}bHyKSsAOo5tzD1VP3iYL@s!n@Ry@+RN*%?$ zix_O*AK_tLg?jYPgD{F9kO+BzfbvL2QLx~+m;XskY|+=<@$?az0N@z!@s)-`77gqo z%89eNSNmGmr+1YK(-TwJJ~4^uG{>)ilUh1EWi1SzbNkw8wNe!}xT9 z>9jyA6i6`QVaj5rS4H>4w|(ZulMdX)|ML7lKB=E!|E9*eyKXpm{UZ;3pw~;=$`O=W zVSM{ICMKsaIn~2-FR$?WaA>sK*UsB{&yOXYPUmai``LeA@BNQr@kmRJ&Ru-FH|3x+ zdS7oXb5_CW5?*TvP7&84#Bn5oKpr57i791cteAWsHO1S!)p3#Z(3_e-p676&MNn`3 zs5xWKXDQs|XTJ5bzxp#=@#nkn(T{w>?|k_4HkRAh?!t*Zj0+AXSd|V=JQ#5!f|KL9 z5T|kY=W~LEoO8wE_E&;|S)Yk(wJk@y@w51sPkjo-wHKoP+E>hU$@m*gnJ=+M_s?Ob zqE!_o%CbaR7AQ-FqRdg|8MG;3t%kGa32!JR1e|dYLZC_oU7G$Jh{A!QBC}PKE7ngl zCzNgWMtuiX=hrUZF~;Ygobv_z>j%Dw1LrkgZHx5vwJ6|mAgVJZr~P{z17IKn3f^b> zMv{P03auQJvT*Q-;sCQ|4xmvBP>TeHh8h@}F$x(*&}9y9t87*89Hz&|v3>IvY+An# zcRaWe6Ws#MzyaQ2#fE9jnbF3P2k(qovuB_+IEsOxQE;XqYc+62&=?#sc*40ATT)Avh|>JDGYxCs)H=<01^`+D-CvDFaz^u3}S5C1a7}?1(vVchK-XsMn^|*@=^O> zmpLsA#2gD}Mi?0#MYBdAnZ@|_4m#5r7$a!4>KL3c7e=MnwrvV|X%Phi%6J%UAvr-k z4$!KH2n9nZB^VL#w$eQc5dt6x!T=R}l7kQl5YDWl!;RyQzD#f5*!$y6Pfbqnf+P0? z4|p!PdsPsK!N)0EPFFVvLaV}!G6G$eaK?gj2_a(yVGL`l5u`B|T4?|t)+lJLpbG`9 zRNrr7;e3_qO$Z%y-p{upy7kN#9@cr?Ss%s`&wC}_`r4Oi61MO3D%sZEea8G z0p_cmlsF7g3nYlwF@+6|f9ps8^x9XyQvdwdzx%tN-RZ|2g!^y4YWML?Yrbl{nq8}h zaNcA4)HKF>IeLXdT2_Te6xTM*TeSH7=UsWzpVy6buyFRsU+&Mcn<3IMJ2+#;1HFmq zrD;+wG1@|D2WJ_8s-Q2mg$O06V34u~7SxcJ4v7X*gL%DPZ`G&Y_VMLE{PmA;-tYf; zY$X7^aPMI_kXNBd-bo0A3X)W1(!7tHGoCucDFpU*BW#-17H3i2U}~ z)?*ts;W_*4sMpeca5AgD=#h*K~a zNCt#){Ht(e?&q^Dk7bu$`9CzPuCGt|E)K%bKT#N4+o;mk@k3Der<`=)^ z{rLU)=l{j)zx1>h;Ln#`G%;u4!bi$dAL*zhKK$ix;+N;0|9@WprRRPP`y6?E@Zb%Xzc0^{6J->mR&T*tpxY}jooP%a6*aKX z8ZxS_9GyM)9S_}l{UbZ=wJ&b?AH~~)Yc}KXeRnCE&E^fEKqv*dg=bl|A$2Cb2=x99Bid6y@XANr5)|IMG{ zvOk=Uf!$A_E3Z7~Rb?{ufkNwAS}63=(&t4%N@IZjl!8!7ohQ{Hl*wy+ea45NlF5Ug zcP0Sf64-7q|3CIl%lG=DGD+bwJz>tNH?X=K`BqL*R1mm;fd z07gOKF>B5!2mz)iCz0e8B`OLk`9Y~IgbYw?)X{9W5k>)mpy~jLKtP58xCkHwhl~V- z6o{e#oC4Hogw&75kz6C!-+$-%_ilbldwSw?_5*kTSbZXOdI_fll2H)jV703}D3pOQ z2I2u{ES&YoiyR&f&YFtPuNACPuv($aa}-$*)6-KJAD_VX?UU#w8S<>GJRjN+Z_O@( zkbP@|vpQULrr-Fce`QSY(B+*0nEW*2{H{us=uAvtYHAW=<5QUK<#}PqEsltDyz`5! zHbcZ2S(E<20iI3lv3S-4uRHBH`@`S;bt20(x7>@@z3?bg9~iuAXrMJ#kHvmrz*<2R zAqZn|5l}eSbXp0kN+grx7~ir1)02~MwhE{79$gVNzCAc^(HFhW;{R22Jid-R zcpuz&!FiK&cinScsmi0ZHSLPU1B9G|QVYQd!jOYg4{!8Lmv!%7d);{}zV@g4@!fBH z^>2P|7ys~!7#Nw++Oq0_PnooPVx!g|f(J;G1Zhzs*A~hHx$!WBb?fb+PaSsr3BR7% z2>grx<;=hOny-G%>+tjM{v@*GUVZ^lwA^Uqm8DRp3 zFm!WWO**WDB@~8wXy?H^SwJD@mtx@NS024r_xv5GqyK-`bNAaBwZ%(=iPiUia(aCH zuuN4M2uW$Nb)ttY<6TT98e4ifCW;EPoGAxS#qz0v-Ba%>h zrUix^rEr9zG!EKRD840Aa{VjnWuoX*bOCEN;!)^{S%| z+$+ES)~5%pd+`sx01?+(QcryWx_C7_p%w&5mSb|VgDu;}FxKl}Dl5>Lgy_QBhc2OC`w2v5Z7Y}5r9!j zImg1u4Qu{fY+JYG>bw7WKjBZm@fpb0@ZhG0?)!Y!ncBY!`(rT1VrsgF$*C@;rZRMs z0^2)1O!g8aWr<8{6b2wnj1LSCzqi+!zVXuASN^Y}0|0Kk<33!mVj5?i_4f4#?X~Mx zQ)EX|=W3jxBCJsl35$9hA`Upb&Ra0q@E(Uf_v(CnL-yOhBgs5z7l9&654PFlXKJym zHybgW)ku0hq=iDaP$;c~b`C_y$)TBZKm0ErJNt6V1OKTnf9tQl=56;sibMBYtb(X^ zGoidR#_sFAje)8u8Olnw$O(hDj$3Wo1cYg&QDiByZU@F;LNX%v z6x>{Y&yERJUYxAN@WREfQr(F+O3s*M6bufGm?|9B1A+n}V~E25^*BZ#D^XdJYh=oy zw15l*2Ad)3F-IgRq5x<_6pfn1z(5@VH;5VoXtin>oH-9O=gz^S$22i_E*L}2;yRhXDckY+{o zJDQ|Ole{wQJBPG%75v0ilkQA=SXV_r80SGjAcOEnC!X>8i_@t|_t=J~cE)_F>sc>6 z)^6AYk!^QT6dG^TiJZDdC|G66;uL|Ppp3x-u%IxGps*ggZ%lL`FrX-`LlD=n^RA1) zI0xktj4_zJgZ1k+R9k2&Ze9qpOGt$PUE!!}wPB1iUE))r{YW+4WoEc%! ztQxj$nZ&v^V^GSV-U=}^JOCMn*gm!$%O4$swH5;dbx=y-tVgLFoO9q@z*tuW<}d=v z0YWGWZJ>>Y1BKQO-g-D|s)JuZ5ygU0YKQECeXn}?^N;HM?y@JZRsEmq`6nEUIme!9 zCzk(tx1uw3q%KQP&cGOjE>e_54qcY8wwhKLtDwsa>C`x~>1mX`1Z9!+y&VO-H+@-E z2q6N5LV!36PCX>8%yV%ZA*#hBs)dWffSnQq;*@~sqln2~Omy*1Z}mZ1l{`;Vbh|wi z##u|mtvqUciOSl?=PW((2AXeuv=;IM23q0Ba666$8sSkQkj9Pfde0kQb(;RsFaG+( z;KHkK#arM1d0Sih!uwcv(`s0^)H*+GEl^}7iZq8R6;z(1H$7biT4frgG7wU3thZYq zUUKkJ=WKc8zTPX|@d5n!2S53ndwb)Z_v2-+J_84x`o?vu@4IQT+wD9z&&#kHWzuSr zMHyHSAc$(mPKZT|bK{fO#d=`5g& z5~e8NtOG^$&|sYDTC;uj!hH_<*4VbK`rPyXwn6OL8$^zsa))|K2! zX&6rugsqPyH$8aO)Ti&p`isu~U$47#mpKTcTEM-1m4ulU3yQj=XaoYWU}%IA^|10T zHKGtf7(m;~=fS7|WfUGjR%#T+feEp(K0ND}jEk}RSFHVC?|0CVM`Ghx|Bt%& zj=H2O)Ap|&DxC19&WY}($r&Uk17cQ8m~+55nA7N(!x+Xm>fo3#p$IA{AW5Q163Llv zpy~Xk6D#fX`=eg`zL{@yXhiY%tktX6YFMYL_Nl6~_kQmCzP|MdWE8bzIry-~zdmU+ zb<}90RK#E~VB6Md`dLv^$xBC7d3=zh&E{pz(a|?oS?~S}uKe4|z#gbS{_aBFbJ8iC z`_<2O=Ph5p!J6Xn&ihdx1a*jM{k^DIqoNveDy$tB&QFZ3+54iWJ?@D7zjwYKO#SGq zpW>w_en6c6!;?-+2P`19#>Cw<{xdDBau#qLKv@y^)|>+-V9jx@GPeDLTFE`Q}d*_0^AM;%^yRmrXiU0g(kMsNUPNxy; zbmp3~Us}#?{#?=TuF8fv&a*LYj(%Wb_oKdEbf(XW(&V;aO*}M6d%urg7js&rU zNCa$UD6)(+vb53wZiw(DajY0=2-?kvk&y;t^OiC?Z!yc3EaJce_T}-9If4UrUBrsT zW31e5U((1kv2-;{cismTCkzJ_o7ZokJ3AmRET-sl|N70`cmFibSWHnd==I5m8P3+7 zCPKi68dhD}`kC|L|43KyAhq7u8Xy1cJ-6I+&u_24?XMo!zqTIng$KZ4M;^)O{6$-) zH*Y$~RAw(JB|=K1sM#G-352LYQ_);3ETYy52Khi#Y>cz8bYY9}`ST$-^8Qrq5zz_h z$U+X^Zw*K6y8}zcB3#ko+S@lX)5}@CxXGFw7P4t;hb8k87LO;~ci$vMQIez)3l=Y+ zF*1*Bo40ZGO`EvW-AG6qG0kR^W}}IYA|fs4(sEL~uhXu|qMXx@h8j>4C8TLeoW?ZUO`6Rn zNs`7%q!()?_E5p?E`(bYg4bn{kqoH#l*_L~oV@!Q3_zw+a7*=t_8 zcJ;AGU9VKMy8y>PIKOaiS)^s7(P|zZ2{)$W#+~2LQRk+A`yl`E{r`6X@}_rvgm{;g zaOV|cTKcC-{1QUIR3&cC9A6YUgF!}rFr+9;uafk-xIOar7o2>~X^VH;!@TLWZ~2Rh zjtA?Xc7BzBj27Ee=a#9W@#W?N)tNBG=-i>AH)_FH(&Q(nlyOT^>*TQ__px zj{6+=iK)A9?*HJd|2?YrqRXyi?RCFp??aBhWqRv|l;94DBCT~)b8u9o&`J_Xh?OL5 zG)WsHcn{9jaaeiY-)ZXJTd(`W8Q;3+L!bK!Kl?yHH5l>w6;>YY$oO<5FCk^}l z7e9<1T{JH$r$3g)@=>jkHY4LB#F4@l1tuS2y~DYH3!z@9N)cM^k&_oLTmHp(`Gh&= zi~qA<>fk-skhVvoV9I9;Z+EOEEs{p0QKJ5T1OX*Hk#saQv|>oLL`z>wu%su7BuT7@ z;`pw{!j(T#TKBHM<^K&Oxb?O>dFl&Z#FE|iSzk`x zrn<27h9yPmYa@J={-HTK_NLyJ`)_;VKYf;q&i?sde=-lU&i=^{Ip9%yA(!oOpUS4B z3jSy%(6N@pNgz?RH>cH1Xe0@-PEf+{7)-Hc!3*Ac%`wX(&c1m2I*CW`wv-#Mx@^DR zmizxrc)O_CYN4VC>jV9MMz=d4&kaRkn4TFh+Zi&*D~6>bt6XTb+NUmAvgBjy*WNie z<=p>$bM2ZNZ)d-qmiT!qR^Hm5nN@>s|B$LQQBhiI*^tLpj;PT@$4zC7-?Qw^u6y*fKj{%j}TZVMS?z zZ;XupXxWZCePrwXYlr{xsW0(hxJ;K`a}!T}`qSLbk9*E7{rhhFV=&b&rZhVkV?}?^ zr#IUr8w}`7&Cs2mrpyL}5KwV48I6pcy6*Zj*To`)UtaP*-`{s#d@>`kpU9>*y|o(7 z{!5W{mlj!pmHN)sf+g>pw{p#Cp})ngI_T(paOTHvUVPY7&mU{0qpegO&}_s~X^D!o zgs>P}?G;S9kE`-kc;7%Ed;n7!oCRY%MP6Z@AxK$AYWskD018S#Nlz3hgcKkpJ_M|{ zc<&Ly&!sHY-kdmzkx}jY2*G1aMFGdmm-HhJ!6t3u#>XKNL2-ioNF4}i#MwXV&XvK!nu_n!iVr;<< zj4j-OByBUXcm;|auq@y0U|Qn~*t%&eci%HjuUE(B4zeNLexIx;Fy3QBz0jz6UV%Tk z{2vHpqTZa38jWA?dceV78(Xm0-hRhlpPu^HmUyI3;DKrYd+xsad7Y_k-_w#ttZMyT zBo&dWG2v1QBBjtuqokzWYSJEQ(;gXNbaaFyidnsCoW1s3Oq3?%S;775)-koMN8B7? zeEv95tg)t|J2S4aoug3-#F;^ zcB?ASFwgMRoq~WQ_<$)&ieZLKTR(5F-1URY_IuQAGxw|uuRQUC+cmx+1m60x<5<3W z=kYtQyYf@R*{Ktx5IRi~CfW@~S`EfWTO?_M3XUjE?i^dT`b9#@KR){{pZU}0T=1)( z(YxjsG*<1q&v5g)Z<(xjNWhT~dQ5Mdq&vu&%{_y9@!@r2^t(Ikw)Z~|``yW}|M;A_ z4D<(AZ+^w`=s20T>8@Komvtsz8G@6E7Bvhlj&WX67M4iGjE;`cNOTRK^adp*;|rIZ zzr#LC2JpiRe^2k%UqQAf_NKq>`zGrjkY_{kydW9jC*`DjYuQSXftIb zjTs*uWn^rWR%?_fmIOT3tI9_sCx!7HJ`Q2(Ij=tPuRfO_{`<#x>4_iW%kO&mtIFBQ zuY*s85HuSJRgp0}HN_y$DV<|j7z*Pl3X9kA)y?_KU*@g7^VAEk{ImDU^PX}P&G7}z z>GgMiFYnA8uarbfRU1LQCz;#T6rqL+rm=?L(Q%BHg0d>-AgLZ>El%jOM)y7D#Xj%% zzx9Kk{@Lp~?$ABZ&Cvy}x9z*yluy@666rdyqLD_lnxZ(D(Y_l`lY%hpYs*}83-$!<=! zFm#6nr3Ek44RLGYgz1fUU$cDIM{&ma7ya#S$iGm}KITw_*7M8h$$wWqe^I-s8EwW) zv|G#{9c5zPd~}*plm%5$fQ;@F?fEbBy{(tO=sj?qfgwCUg(nK!;^@eXTFYAjrF+J@1C z@0iHeg~vYeH~=m>=QN~}aohDDn@am8R}G(13RBSoYn!dtwl6RLz%SYwe2f^(=y;e(<(%$e%; z**4p!pB1!58aQWIIMHOMU3X++-h5gkqcmFeoKpIN!M3#wx;;iF=F>_AMQ5spCn9LH zQdaM{2&ojqK^-wHrDAMi9_iR(bbAFNYEyLXXLifojIBHzTlBf%+H1M|io3{$8H3?~ zUZ=}oFr+LCD(C)WLy%!ESu9YeS^0VlJb)D0g2hW-al_sBpZ?d3=fhe0kv@S3YM(vV z5T}jHs@BN0#c*&`-v6Vsa{D!)(s)Z<{0lyRX>F zZY##AWCI;(4%u@lO_|f3>Em2YsRTi*-J%#~42FHiQ^ANxh(tsL1Xstb>9mD+nh*?H zYlM_@<{$+Difpi_E6t1i>rejVPxm=KJpUO_cu3=!&ffvu8oO)iy5HOws$z{40x2X> ztgw2HoUWTRv?7XP2!R0TIL14Ru{G^7iZo?aQ58e9R7h3F7^jVdD2|EZ81LI8LJ^`l zbD1pB!PafQAP@mW5NO??(eR8Ii%dtJvBzO=Key;k_8&OsSH1Op0G$5CcikaY?fy2= z*|aGeCE$;(7(CrtZ!c%OflEo`~N!g_?OphbU%AH?Opa;+PVL}PiBMu zA%uV{DrP3T%=U&1jh&P61vff2{?mnP_Wa1Ub+=5OeBn*}$4|Cz-{5QCJDoQ^=a`ut zR;~WTmS9#D!`|a10#|`nfw3e&s_?03aL^O?>vJ}muxts z)6bde7xc>t;{!$IL8|GocI#j7yYsd?uefK1pZS()#9b0CXjji9N;siw9Z4L;K z>Q5#`sU*={NWAl4gRJEfu5R-eWeu?s!gqi2pWp7k=H)N>i`TW}z(+H6@hMa7<-2}z zFuirR(p0;|LD80)NJcf3ELf5_ra6)_K5vAwC}Lj@Uh^OS{OSvO8`s_Vn@ca@@sD}z zLmu06PyG){p_*~teOD~)J5(!fNs-{4W6Ggc|KmPL}pTl2Uzqsfsp7ZGar;~R3<1Vwi6s0?;5owH9^adH;7}_HZnyrMS z83AGyBL6^Gv(Ib4`?(_?Hu-82y7eON=anMY6`cO-w5Q?26=|8t{)h;Le>|bAd^TNH3=F!K!f=f^T zCUK*Y-hIV+Z^=5-k4oDkc1T z_~ehc?h~(O$u9eMCU3uTQ0Aqy*5UwkR6l2~BG!u0R?5855ymGbmMN`1B+CBtckEwz zkrD9~*ZlHpH#&4%GdMuo{ITBH$5uWpsmhG3KdgdCemS~i!3Y2F()Znd#MwXR>HpWF zG>}pwRf15Vh5`qVGXYasY-O=!i8Uq0)VwdPG|tuky{c-j z2y+_|rYI=9`BQXj4SlXdBD9Qf*4455)?l0?2t!^NyoWT3h~orLz*Qb6^aeG)cynzO z`|IkL7hcZe9)Fbh&RdUvp|-_7UexK6NQF=lJ_M@56KoB#6hY#H#OHyc%9!a7=fWcl zahfvHNI+=jjiscjAPyb0v#2T~jhZx*Kzm_~+T}3XBNJ4;^^`4;c=EiUGh5K>l>`yd znHgZ6z`4M@Ucus}8L~G`2!h}$qNGKrdPpqYUO}V+Nh}!->v2`gNvNn3Nc~)*pkOZI z908(Uka!8G*GhPzRlRmvS4A86{}a#99=-71?8|y zZWQAaW9+)aC_C@GGfT#s?6z!-Mhxu*i}}MHlWgr)w8zG|e@ll}dz>`#3<}Gx%Tk2T z5ePcH0_PmDt_|Z^;cy5<6cbRyag(GGqjk-AKnP4()cV3m5rn7>n$F^Zsw$-vVX40R zip#^)-CIw;;GuCn&b{cj9JW|jQbxN|RgVsp5mjE04>R&CCm-ev2LlG(9$B|fk>zCl zA^EURuq7rRQe_$5IHV9nT9Kp?jaH-XibM%IZ6I}2vpfa^Y+2%s#S{hkpoR|?S^c-M z^+H5SUFWb%u`|K;+RJvo?$s}Pw*B@GPW{ul|Ma}y^U7yEVJMT<<)caHr;$9Y*+^5R zbrOQx4R0Q=gL$FCzDoJx`PlMkoLw>O_SDq2X{KiTY@6ya-OHKj<@EYFWohQ<$O1yZ z=J{^YyZH+iFTDJ?M?b3g#YI;?q;Wpxv4=6W_V#{bbnFjWianKNjZ{h~tx4h-O(2P* zI{2W~+C>T9W4C8q@zUo#J-qhGdm-b-B4LJ~6Rvy?!AV;h2K^!3ykt-YG9#%RL8l`> zTeS1u@7;9Ub@!im<}G~lAwtQX@aV^~?xx>&M#dN2fH#MVV3#T-iKIr0h$PXZiKdxq z#@lt*rrC(=ABSL-R_rph?zTT}x$?|=U-RBia@x<&{IkdU@~1ySvS>}CxAw-jSG}3n zIcJsen%qbRR?#z(nL>~`jh6{BZqCGw#(REsG;1o^?NKi=*fD^mFK>@?9Lt^B}$}e1FaQRImA^3g8w6bAb6zGw$YyW>b#xy|7N4n zbhqAp?_WH|A&=UR>ezRL^ro{8RHk}dE7FXl5%XIy^V^!m;|WVA8Y~%0Xvc!_X2e8G z(~bhII55$aG^0Qg*WI%uNv_&|)x2Mw{L4%J;&HFN=30(@{YUxc$=~1bqwjvJD$44Z zfQ$l)$^|lO=NJKgt~VbD&SR@;AxM@U^!W4d|HFm;*T1{|A&u=hCx0IAd{b2Y_fh5V z4Boa~s3%y1{*cM7TbZ8hFx@Sf>6i2iOO}_oAl5d=7oPaj3$D8G{r~*W9QUke|Bdg$ z|GoCvYY*BnW^9lZ6s4mm4S@)qj$>tvL4wvhdQ&w^r!RTNk^7pn zF8|Ah_~#$9KQDUrQ}z$NZC?~NUs~_d!*EzKIoVbaL)c*f6v!ijHt??bxAt8ZS*vawdtTD1I%d@x*4+Ve_h&FrgQ@iGR-yiDkufAb3E%lE`uqzFk-mUQ|9I$2rAA{T}@ zZC!Xx)#;ZD0Xdw2Z)pH08>#$a7kmWF;mXLh#F zOm{%9KV&edCCFu#5ln?vN(vzsN!%e3)zek5uT;2Ks9>HUU_VJ{gTp)5^D_N+tD5hR zll1=A?0p!gobrDHHkB}1Q z1IBu)GEfwo-=OX+}mHtlaBJ7OXjpc}teEaP?j+Sg|v4-1}%TwWmT zkjbr++;QiIIyul;Y?+f~8N;H$dSA~Wr356-iFz~0*H}h3=Y>YfDs44Bck4ZyES#TT2@otodPYW?wAu;9phqMshP^)9wsvWcjWE7=1(6$&QgYMn>-qilYZ(~N zg0YnQH+Oi{9xFIvujO?5CB57*e>A14Dt28pfm9JnYjk6bxY4G_h79wPqOvtOs3-`| z01hEQ>Ie}x=;nrAR-)n*FBDZ#;cVT$^v>c;MZhlz-gH~L9dg;TkJ!zh{t&g|Pds8j zqLFdo^6oQLBpYd}h$2A}Ns>sD#t|AYrl70}f|B@9>ji~WXdPqcc0@w(b%d^{Q)dF2 zqNIwCa&Nz>^z4iq*TCmF>Lou~91baxJ?j=g0lu|O*N~UonqbQO>3Gh|HaL{LJw$Jo1 zqnGDcAGS8yBc~>@&SM!?#!ApmV@A_Nwi3C|#Mnr><1V{j`Ko6gTYmfJ+fSdq^or}b z_U_FbbLd{PNuza@)@m;$<&JTjk|r_jX2R%5lV+<;qt#YYiM?g-zP0Oq^zz%69`L+* zz8Ljle@++r%E#Wy!B0Cz-g@D=FDZw^52B(GnKmd<+?!IR;TV{Gpb%qSGg2B*~ zUM63hzt52;?J(B#KidxA;@`4vzWGM(n1mhUa-$lVcc(3@L#m=!Se7N$8H%c+EDd>< zV_m%|Qke=m3J6k>$Ts5ww zfB?3#%+A#EkU?e0OG{B%jQ1pIdf~*P#UBWHr*q1$FZ*AQv){h^GV_HKgCC#RIV-B; z3JawZWVVhqFRdpl1KpvifsW2&ouf2$oVoJ>DJ9J~CQ=#|#h0gAop;UJt^ez>ufF^@ z+#XH^?@@|E*j|qXe?pn!O`cfLS zR!9VTV)w^i`xK<(^JLNaM7yaUH$E}J$mj^()?K$U z%dy@u%qx1sobDi}H!K(yB`Qv)S|elcf7H>BJ8gbK_|U(8@vpkh+t+Vp*QN9GwAH#& z2-O@8^L+-xA}Y#)EblSw_dy7vI6(+@#MzZPY5#Gs;g*?n{Y{7aa`-tFMa!)hOn3Us z4hlNCr86iQ<`q%WxN*UfW$!rklwbY!Rj+>u@Bi>;xBDEw6#^%W4sc4P-Mg;(CtLR3 zgP`e!sJS}MA(QxKl{9}Qf}by?7>l(Qk&;MnP+F3_7;TC?bwDTB{(W`gw8ht>=$D>WQ3t_Sp~l9{KT?|AqE0 zheg%>H~y2f=Gi)mh1QZdR)8ZoUwc%%#W|1nvi7hRC0SlEEJ}LAg5EGE2uM>!tm^li z))7e(&3P_7A{g>PpUzC5ndv^mVMbBp+;aPUY}<4{zUa~Imo%F#M#jfbp+YH1+!!NG zVhWWyod{(w9yAOwVv^}-QqZ;b#u*X^(Y zOwH{;MbS+wRNs7hdx4!*?HQ{liGD0DgVx)c|V`+u#anE6_U7YQ~I? z&!g&XW9>D!bH&Z~leR`!KV6}2y&qc^Y}vS(b{HaY%pV)U7>kq;H^v#AzmyOp&8)}d zmTgRK@l@6kMS{W5(K8lBEpIY5&>L1X8iukgKzNcg#W;@&;Nl1^B(aV_D~t=G8Wt}q zZvEqFetN?nAL@7x2R*d4*B6z&OZw{2G*&ooNRt>XAW{m|h!HYiZHZ7NN+r0;P#8d2%w{i=!TM>~@cJ(jF(tC-3yli8mF)-q)+5d|Xxp(M)8WWD;t? zj|gsw7XFj_zWh6psIsSfC0}H`)J0Y?Jvqas$u3)_yL7Ug%F43Y9zSX2&U<|F-s`TJ z{P5*Ba`JX~I{sYkCj{?0;RSr|KYw!R>yCT$+h)zwS4wMlwpxM%En*Up5J^wF(O`UH zg0#`XnyPNUTX#$p2Jh6n9rmG9zr9cXg4ew5|2c;3Z071yPkVer*^kEU*8Cs^)>(?I z#G!FfLXZk66wZ4eM0BbeoB!|k-*m10%(XXh_QP*FX4!`ij*@ zKX-h1@AdB%?Q{n*+CWOMRYhmEOS2I(DC3%&P&zULLN(U6#+QEl{=3(1{LSqTGBPn~ zGk@P5LF6+c2|)`l(#WGDi$vg@pmG-DAX1tz7t4u|bNYUTabZr^9ehZVuJh(WU*=CQ zx|Wwb{=nh*f`$J!oZh@|FxkOMDPkEBsRZvdu5zT!n07m2WF*EKvmgv--?`IX``mcy z=ij|uRLOHs`WN)r{0Y<9`U&Z)*CL6f0(>CLa<*-}pUzB=nL)v1cSygiY4AcNTgR6y z`qeVd)#^tx_yj<8W<^xFixYb?VXW2JNwN#Zy{OF^O) zahy<=70%hz7v(#n>8d!n^MS_vh zQR1k9j7pFYB{7LqG=rpHmUtXOLXsx4&C!wf?Xu@S-@EdwIqJ9E@iwpH zcNd;^zn{@BEHj-UK1!}=&tLeCqaSnh)fZiO*}mnldqRafKC^z2c90DFH2YI15pX6! z=?JCOoOxK_J$R?^QpB#vQ{Yc><=4ns?H-9@J!jxFC}oqMX6x}jSmxXMr!Ilii6 zav{w9hR#jKjiIWB-~+MN^>Hdg@FfaS%c+8|{an7DOH?Qbs`l1s=9CTKUEK-MT9c?i zqUU6q&ee%zvBCyLWnJwBp)ttR#2NpS_*dN@!}T{&^gDamZ1Cc;$fObxQcDOH z6&yH6nU^@B@E)wGP+DP(B+DJWK}A2W$gOAoyfz~v39VL2tJPxh@)eAZO`t-7R-Q-) zeATBv+r_$==~=-Qmt4>FcTEvTiUW6F%*usLc3o|7{#IJ8CTVMoMtci!B+!ldv?nIW zdRvJ{mauBiL#T?J$!*hYnfe_zKxr&_S>l}`i3D0`%F5OoUEbFnZ!Ji)z|8$yX*|Xg znj@`quekQ6dqN1?M;UL14v>OIf&V2a4`ywrl4pZ)n-A>LL!WrT zbDsPdfBHk9nLqgnk7xBW-)cHHUABYI29I%7iShyAJ;Hmm)VRvxtw%>O-g)o=EfuCT zn5rU_Ii?umsv+K1I8#y$``B^_z5-j~s}fV>n5x1UOIZ|u>Xdk6u~mUHB}&xkWIEO) zX-rc4L{t)~ozh4iI}&q16eZEyU-$g{#8(_Qic0n(pZ$QzhA*;J6$NJr&Jmo$R5``4 zOWy0yo0?>D%T~5*n_}DSpv;}RoG5*NbK%N=Puml>?fb58`|hv5GLofj@2hHA-!`uu9QfS+3D12R-~HA%w`;5~x#~uqdCJ{v`0l%=#rX1T zTxE8*WwqK`C!7sPm7sL9Htf1E-y3s`cjX~fKG0cbn%>k_`op}ALiL6uR)e(N{9OGD+aS~&~c)ZW~(OT8bi0!VUQ2W@|-NssjNjuan>Fm|M;O#dEw8T z8HPXJ`rz8?*IjoV`|h}mAoLm$#PJHLy%!K1N@^sUMiMd7Oo*ZgM6FL2ID+?-#?{>x z=W!@QW8|0K`>U>7`{33CAN}Y@x#^zG>^4?RMWYKQan-RQ_@`IzGbK z=s0nl5~t~^C{FT)hdlAJrDIl}n55O~7fXVcpEh^3*w_0!nm)k=sciesdT zD2trl%nXCUfXqOzn9K0D9;u?)iG_eb(x z+PL2T((u`zay`LN}7LMw0z||zwq6kT^hQZ_?ORq?IE4lWv6@|V2SK+ zexJs@J&NO2u%5iK40D4K>UXj+`quu&byvUjU%wNMK6Jk=aclGRN!Q!I}ucWpJ}>36>N`0$Psjvv>-9Nu!X?-eF{m$&6% z##B)V0asZ}Sy1J5On$Hx0arukv_L6|){;gVlO|E^<8U6FtrZV`j^JI^36b7gL=Z?k zRh3f|8O9i#tEGKKQIM6M!ql6t)`E55oTI8jExQT=YXhK3;&_{Gjea_nuJiNDZ+Xb~ z#m~O;IT|Z=je7Uo_8wOjPb#xY)RXi;Ss09kq7)b_sjLHP4oL{Mj_l4e23bi_8q!A0 z{D~H$?U>P)W~3#UH-91Rv2n)QDXoTP-uwlOFI>j>(w!KYw}kt*%yRbSw=gp>3`@t| z_s??oh7KFI_L-f@==UpTru)op+fvV|Tu#>60>L6>ga{S5f^};*vSH&E^1{&V4Vmur znC)a_g{7!Gg>iqH6T1+wu8z+Q-q#^5J`hEE)9Cov2Vyha@{ad^_+fqi{@VJ7T6h2) zxc|N^TCu8Abf$Nd&K(#lMH*`wafH^YmMsBViF&mz147oZVMtYbv;6$@?c*oB;_V;c?$bUMRFVX6 zPm@}-bP^NC5du$D{VT8bqELdvF4axaB_x_9Ok&YD+ylf5R`>fjJu z9Q<5fwGS9;aL!__!J67UZi2)hJE}SQIj=|GxqR;fuTP?=_|zA^&M&_FQ7*q_6Nm1) za#p78EBc-OUfr4AnyM&}LLz0PoVELSQyeJn{o@8!tzP%)$L+&+f4Y6T{9k|dqde!h zr?O=8u{4L<;%X%Qg-;Y)BiSJhf6QInB~>h@Z5j-=AG-V_~nLBRflOI#v&<*qzC7S z@Wc`njyTmwHTOrVW)w$zF1_*ED_>n8?mM-7?@KME9^Gm*Xr&RD zf}+>O+6qBHbCnfbKKJ(&zggdxud&z?LE0M zhiAr(ITXWTMm88yW*M%^k#i(jLcp3z*&^TFJF}k?>iVNw_icIazyE}jzxTuc@i-S> z@GI{A?di!3M3(D1hSSz_<9pT35y7IP=gNw7c3rIcR+j~ z1WM5uJr!HsbluwR`z{~*m`5|dVyAVXEYe`?VOpW%IAUykgwc^UNtzO;DRGh_q{JE{ zvwm;4VrujC)v&kj=fC;O)c9v6_mUp;l-*os)0ccXe1Wr88EeV2AyZqnu=b8SS-*Z0 zTW0%gnH{jLGoYK5c%f#-7A*SsZU-LvAJ!W4weN2~$L8-`hDF$=cr_q12?#AiOA6OP@5 z*4V^Ou^oP~p~YUKt#%y@L15VHGZ+lXjALjly}Tl~j>0;ERQ>kY*auhdzW;w#rLo^X z>u>Y^y8OyNvhU8z&Hhh#<{vv-Hr}5m$^JXf|lJ8l-7Nk|@%KrrC&Sq&17g8be-G^s<6Z zKO;Am(RNBR5w-8PS*vbFS`mpX&}dpG^>ve&<7{lhIh)~(;f z*FN=O8^tlfRZr5wHzNgUBx%RGj@Av9L@TsXwV}p=^MW92a3HEqiwZ)ra_60Ck4>Po zqM0fdFJDHm8B<%fuyJFT;jln!$sHRyG}D-!c38-wc_T=v**w*yUwGDS>2d$$5TO*0 zJMv)m+I=;q=rTRkBiM?#(I9Hf!=L0@Cf1=s{r+0mXi!S@Gn&sF3;!ZaH;tQQ< zWZq3l><-e>FH5x~Z8XqvEFi27RsNW;`QG58@q7O1$vY2U^orNIh`Ui0LGn2{5!$Hd@s(q>N=LJ8vo)*CuAGjxUp0~0YYkQq-E z)LI)i|LK&=Zn@$|$G(7{pYh&(QnQVC%oZpxBvKi-&mjJ`4LrFjVY-S zLZMZJ2=z{g(t2(uv5w33&aCp*96Gksp|>CP{MW31?wdcte}3-&?e09|=>2KU-(h@a z{T-jFvfdF&*Ae2TDw*CkO>a<;S(qIfdc%UOa_C0;ibXr@{O+my?%ewQ2f=pv@3r4< zJ0oxXM{x$Bh?FLtgQ5u`hN3lgvUJtDYwy}F z1r%4`d^h{9TH-{~yg3{8*BD#vjjT1Gwvy^Ps@ z!7wufsVB!5F8=6}9oPKl%;dJ}8$Uebk-h~VRPD9bo@~76vf1XURd-hT@E}u`OUkO| zFj;GfqnIRZ5`0aZ239D--ZRP4D_XWYHJ*S?=1(+2@##qUr>jVbdXWOb2at-qESVi- z!Z6Kdeql9+PE^IrN|f9#2$x%uwPe|dus zVOMX>vN%a(BaQ2XCF*paqAW4i)W+O6Vq|=@n7?GnDXaH5;Jxd9@r~Q(t=fY(y!D^A z>-c9}H^taVpJA8Zo;Rkiz4UqB6rT^lKiasU`uzcu+osqy)1?gR?x;2L7TeqQ`_KL2 zTF(0Q|LJ<3f7z8>e8nGm(QDsOjO=pIwRG0Eq;QYXu|g_{w5oYw-p^gn+#f0HWGtm3 z5G@~k%ZCto=Y{w6reW|NTNaqIRwYzLfvGB_Qgv6O9vyH2=|vqiTNZ?}z?KEx83Ya& zeC_d=1LX!HJfiM|mmXtkd6lUQ#+W+&O?eUpBeA5FDB5XEGmU99HBlT0!4M~cMiXSC z@uPLiUwq-1-xhvz)kBL=f5vX(FuH8i^kzQjjXlC4=YY*-&XZyY!J?EvQoo<$B&lJ0 zz@RjAvz$R;2|^M@0x1QFmM8^dqfKNe8FV|iDr1mU42A{4=1gwd$W6Dd>o?DE+uHlNeeDJoFJHx>M;=9^m9k)m-AP6k(3zRSTET{m zUAE0+l%|Gn_69?SSvdy=tQQ&eoJk@Ca~rs|abAczJV$9gowVAYo!Y$VwkvO0yWJkR zzjgh?Ej&K@(T{TA!3WbA9pBiSnL4m6t38YhwVus-T&Ncs`CMXxMlW6ppQSXOSWA|#m|#%B6*t_=ytby*Y;os((>Uii z`p~^8EHqn94&G-+9(&M^jI>kIu_bs@Q8_^p2c(K>M17Hy=M_R}DjVi@RX~J5q-*|# z_XZ*B-F{cO`W*sjEo%yBRLirHNHEgWtD{KYYPZ~R%LTV=e-zq9zq^(l8>R?RbAE4n z_Siu`SHnS0RT)AM2o-~hDGG}%E4=k{EmlWxW{z~8p_QWE1(le})mh!%63ulxu$G!% z44#0HwMJCwh$Kk~Le9yz97S2xfFf7F$fOczt&u_^gsdf6bCYssT@5ZQOUgW_D(l6M z^B$!Y5XiG3)>xt>`MDfUU-ZiN{mt6CK^Q|9k9~kV-Z+|y(^0vX~%DwK@zC1YCu-bWz zvmR5GR7FWql=S+2`gum-s49`1E!zv;_R_C>@8Zy0;NJ3J=Sv11%bC~M6 z)_CcBU^wV8xpgDmZodYJ6$Qhhz#ul6c;tQC);$00a~2s+``Iu4F4y{S)R#W_N!H!) zJN7%~DR*qT`;NQ4sSdLypD?zhC<}(!kj~6BRhHGePog%2duMRQES13?dH=1~Z4$n` z>&3?%>wj?S8Grihi_Sis6$c!vCvUm*O@nP4UT=y_D{3HMnV0kjLwb3|uyPE`iqaUA z*7vl=#!lRJ->uj0_^3y7<+a-n);o0n-HDQ9K@y5*wIb$?wi8C0HT~KPMHFkII7Y{9 zglgiw#w&@^4IrIs|9h+mgOIdN@>Ra|y0x3P`#k<|>)jl%BF&ofm)xR)IV?E0tlsIf zIBV*iTI-NHA#JpYk{GZnjme5?>8_XEb=3v_CujUu$<=EfzZsA%{S}Su z9SzN!2xwbb`n?`gTPNAFWsh#>}Qo_GiC& zXo-y5TW6g9Q&trB(VW-1FKLWl=Bm6AyxUUVd_De%y14U^m44@)Bbuf^vELe{Q(nu5Q zs2^$drDd~M-kI!i)PGr&ZBHG){~~0PK0R{9dy-ftA{gRG6GsZI6*|%gq3cerjFD2} z5g6y_4ss?t17`af!^+VfY0_%NjL)CX*t~h9X@k-65z^KKDjB6Ywt$JHJ2Sp$6+*X| z+&0VgcT98hJyR?mi%BUs_xHDR{q6U&dY<6ugZE%$w9V$pKC5GjslWldI{tXu zW`217%`6ygu&rA#(Tr%>E=TV+K?t5@3sYjdg~*SQq@y%fE@WuiWS#rT`dyl_q-duU zPEvJ-#IYpH0xn2|icvB~Nk!UF#8C`h;46nz3L*7e0+b+%BZRW(Bt^$@JNV*+k$sQ3 z{G@#!xA_ep_`-INXRH;?k+EM_(_7wdja}pLWC4uz42K@6awLva)^W35X(|Fi)O#xq zN$DyAf=CFIs9zZMj=e?3F)E4)L1Igb4*{usZLD`WuB;6(QVNW&V}vCQDqkUdgG!7L zT!mD+elY?yyvR~!!&;YW456wB)*^f$)}c0@BV$c4iZ8LMnqW0MQCuAFMdr@%iAmcqn-o?2g1Vf%~So8SD{9T{9ubzAwZ<|=|$B%s3nKPIFu&~+emz?#x62*8t zN<*U=VVuR=DV;>GYDqU8s&a+&(esMnq%AA5vPXZ=VXBic-LaHLQW=j8 za+{0O50qmoem1`KqVSV*|M%pjf3*6+85i=RnN9xId)NQ+uszmRu2ILXsK(hxAh3=5kgQ_0VN}}4md*Pd@(6w`H;tU(OyUL z$uFFA`-#tg!pHlQ8^11uTORP3GN%ldRCs(NCiej$JyLoV1^>pz83 zIQB_TbJ40vdHYTTL+0q{{*~#b(DoW!J zI^Dcr>9P;+vge-Po7!-P{mOU$aphHiu72{PpYYVf_TiUT+<5*|4&Lp(xbS6b-HIZs z>fJkQXfUQZ3GIHz5y~p8+ z;^^MS$k@M(Em-#BVQ<=>dC4_@*K7UgS5F4uO;35$-OKko>Ro=Y?Jn=i4^%>q;}pVq ztaXSW(K?xf=-QoJchgB@3znUJ@lU>ZR@$1_s+R84anox#{7L^ayW^VAAIs#fZ%TFl zzIm#cJk%9~=UZzY6P#U&6my;XI&QVdb7ZVpuyo-sM|V2t%#*+QRo?NzKhI1VTe_OV z-u<0;)1{|AwHO+yy+C@2ujYP>gT`5f9~h+aq@|xzTR`O<*4CjbcwcuosuDZ52no*O z%5si4Uc&*suK?3PEWeOKpk=+lP_e9IZk;2qD!i%X)&WnjIkqeaZf=fX!Isq=2(Om+ z#9EP#)(jkDJk}^G@5rpd$v_%Oz+#L?2|=vpbX!Ds4CTZfqA|u>Pki4)`uc*073{g|DjFCTjTolx zyNPmqj3|;Mql=Lt(4E|ZsSH|z^p-NuD6%2m7?hZc%k`lC1eb6R;E`+OL$ln1>&1VtjmryYJeB9p*gk(R*|D73;Wj-Tg#L zGLlAY+`NtD%jPi>1*$<8FBTx<5q#Ojn*wROB#n_e;?Z_#q?$2Vx3Ccfl_}}x70EEK zja-o;$fSnF1Yeu2m8=P&m7&>gp`;{^n;`4gn6=^PYI?)*ul)Q^zN(`%y!Jhx+ODyT z$L6ze=e_Qq-n#MJA}`+LttW{kK?JfmsKjD%{b?v77+<8(E%ID*tk2%YE@V*)Su^ z2DD<$>~x3u3l}mvU(yZy-m=~0>r4dDR2ad{#%HoWW4}IbB$uFG5yWa2`cG%~Lv#$BY z*LJRo@>9l^Nvc4ODw-mY7ejOi#A!ktr-UHq`7-~P%|H0U9e(=0>n{5B88{gycJ{WH z#hOn#=NE;bh~uc1pO}K8-)B%(3|wH48A{_&64GXC)7Zl0A2{k+ulT|A<_+#EU;4)~ zW^PZNbLCAu`|(E*FWLDFHFK|)uKZH0<Hno{zIK)<#$_Das4r)svE-IQ;oCp;iCfIUNYjpHoN^q@T|J#d| ziq`%9tnY0zBA)a?R)`>_E624jmR2TP)EwTkzxU=b!sR>TZOh~F!C|Tj?>u=nV0LPf zsjb_nJcH(fCEr_r*0=J{|N8bnd)}8{a26A5_Lu9={`Y5V>7LyfYu8E%sR_PLj1yAf z2~@VC5jC*EAxuT)J(VkKqPU9Y;_N-9GFWR-Lg1Z6DuDCRlYW3+)60~z4LYa|chG2UU}m=9 zmfL6O=AL0@=*(tJcWOzOwT=)3xp7o3)bcRz@pEZ{wcn%07bI!=+mX?c%XZvt6*G6- z_lP} zYLAD~k~nQMF@FS8<;+a?5W+Km(E>)t=F^#)Wb>w3f-|hzVIKSJu`4^TUdBW-Vrr(# zP3yaCnC!E7$#QmCHJ=@J+J*MSQnXeeQ-aX72GW)c`onsMz?KwQy_%L%k{1;cNaL7j zEoI!|Ce{S$ZQlX`y%1Wx?P)l!?-FeL=7uc<9XV1F&&g~k@7rplN z{N{UKa_QLoGSBiSm&Ue&uiLXi)&N6e16j~yrKj?e!Uaqan0obrs1=1IQSHf)BA|7h zgdq{AC_)I0F%`~KxT<8(>yZx!Sm$e1s$&@pZ)0F z!}ovjx79}fxw`Jwb)4|AuXE-%|MT7*cUtuaYu#SX`4xch0i=*P>j~BogrbI|I&eaj zg;j%opI*1e%;XHy(|tPql3`U*2d2p$aqC}0d%;O@*{Obd&Lxk?H9nkm{mr){?z)Dn zuDJQ`6+0}tv#jjFy+OX9$Sb=2f|<^c&D*BfxMd5y!4P9j&Fl%5;A~T=aH!IZ$Vgu$ zFElOM;Lr+JU4jAzj8vF-l**|KpnGt*tVy&+jvQ5jE?rsq#AUi_YZXL{z_KmY6Q zmqQNN7pGG-YPUUI1+xpm0uF-3`C1oWmWHgbRHdyaHJ-UWHvozA9vMYj(b2b~NadH_ zwEiKV&+q?m9V^sqSg?AxTgyRrnJJ6?git~VRkKs_vJStPgEmGwB2sdZ#4Jvi?);k& zH)K3}$6JEQK5ENiL0RMk?=ZHa*O{g>J;|^r=@*7UUg8NTB}vm{{eopH{&~%w2mW|^ z{T=ob|MsOv`WE}E>-Kv#u-~#)Xsy`kRyXL)T5FCJAvBdz=r|%xVw9{q0ZK`ncOrPN z&D^g2px=tu1E2id->kjuw(zY}A32}ajUn*py$@v9 zvFb_X%$CnaQY=+c;+&@_Dk@{h%ZjQp5F}-3FqI*MAd@I+YbAD-geOSJQzL1gu6_2* zBxKJ|L-9gUWiQ5-FAm-wABk|dl3|IGA=bK907wE-*1z8}fbf!5v(3o({4HY(7M`?d z#g0>FT>L*n@h-gJB3xsk?q7TQGfX!7IUiVV&^UDc8|(ru)N!`qfj}1O39z>Y?`ltl zGv-fSj;g50hXbl2r^++>y$)rWQ&ttuJ8V@Etijs~tR;Adb+t)as({jUHzK$|RXL0a z2sDX|kU}A~#9`5qKueFJ?v8k4@V+96A&DJYInqdyYFTr6QcbKSv8uyntPg`+wZ5co zyzrKHe*Om!>3e_9!K=9as*872uJ}wNNp@&78bnIgys}s^I?|w#3Pwkpv>KXrGa-%? zQQX9%$c(3#)yDF)nKEyp&DdCr`SZt^m_I>#bc~UC%ZZv}G{#ntHe0x|OI20$dpY-Q z?sMslTUfee5qs{ulG)x6kL2Kec4p7zZM4gArXugp0-lkP21!G+eDxX@t=<n-c3LEP+;&#Es&uG z*+fcWj6-HQUZ==35VXf{nx&OGv^ErlrP)wOpV93MS$oeU-g}lVUx13Bvvni4-@2At z*G(}qD2ba*I)&xDEAM3Kq60|px|wDpV!`53;&cH*!0hBCWj3HPl3`vOw{;|ml&2C7 zsTGB>42y!%Dq$pT(`wWmf)E@~gAjwSC%hp9oD9@dJWr%0I+FWP<*#WU`-Ttx@Sx+| zOJDK22R)W=dF#9R&^umFIx>FM+RJ}+aaD*HRK`&u!Ar~>5R9_cY)TREu1@8uVS{xN zm3NkSn9*uPG@}@)quS6f1FEQyn7KUfIyoYaG>t^psTPAFQpfd=oC;{^!IZUP(FwFt zb(ct4q}Ct=LDsuGI*Li!4Wz35A4-ZjW1T|ih`8CTWm=I!z+$TG^Kqh9l*z*Xxb1gm zZJRpcX&n9Ex(EwKVtg`xEFW%Ym^@=>21I#;O={VJj$)+NL{Tjx6H?Z?Ga&pY-A5|#g<5{=g;l%F0=^wURL4K>Ke%=!f+2?iT)Z`a>!~TITAE5ggsjdx*liQ|Qwqy~@cUZ>ASewzYF`SB84dh#p z)qKR@epe1(R8^H6<|X}JpQ))HGaW-O_vFT7(D+EvY&UONykg}CH?O;U<2g6{^)y;5 z6f+C=>5Xo=w%bsWD2Yg-`sX327$4Q$%OnJ3sPFl#a)eM%Oi&)B0~Rrmvf*WT@Engn zSKs{QWxVDYkL`}mU-qevb!)0Je{3(SXgFDiUWj20wQMv|1hfc5*r!Hi??a*JRKgD5 z;7!>IRfTeOm_~nkiq7;Doqon_Rx#5r=oOX{jZPYO&0DneJumyvSAMznhO7L`U;2-y zjee;0(@U=BImb3bvS`QePv3pBFEO97#xHUff`hW~6h)w@N}?pLHyE6!-|NzyogvE$ zvciz(1;%=;3qi-ph3VLWcmCv@OK&>jB`5OT?Fiv`xa+(#f6QgO2+sWXe?Bsso%)=# zW=&pI^s|DjuylGujHgJOjZI~hukfObTWMNP!fnmhiZYGTQb?sngrsY`-$`%&&xSqr!P5Aq%=MVN*nGa9{s&5%ZK#6fAgDPV|Kyra{g_ny)t;WUyuD-|Tl(M%Mrrl#FaNgD}LDTbLv z2gi5{9!Wa75ZUkyX1CJK9LsISJe{y)JjEHye4XKQ=!k?oGqsmhDYWhp#hOqKS-R66 zw8oa;ouV?6Xc0)HCl!vCa=1hg2SsV1avmQPA=Lc>=K|JCyob`&@w%w^`XwuNICt5~ z)%@aD+n37u_pE=|g$Ka7ms|mG|KWSAJSFc=9~1)GxEgw+gCdGR1<*nfOG!Ic^+P5- z5hal*X{Z<(43paijEyIZOpGzoQkWuR&>Jv1e=!YJGBToAfBy{Y*H3fn+HGu@9)Y z6I7tr8PFXR#7dIH5pkj^%aS;W(Mc`6a;}E&BuPRITr)&zM4U9R#!7F?7l~WXJBOM3 ze*2R%&*p{CeDHeikA2}Ay!jai4aOHO`SH}od!MWWV`-#NvQDV5)}f@sy9nc;pF3O- z42-2))^V9iX`J)OenmqE;>1yTM^QNTeUT`AF8O=U>iCpZNrSapnb=J?YrP-aNf|<7e|h_b9ETj3lIyLJR26%&<*J zyj?su8HkWBL|UttBZTSp2T8Zrquc2)y>*iH_idyyV4H=0}83zvNKTR;Ex zwQqalYv5s6czol#KjOqE+~BehGN)_JxI?3*z?Bucs_J)D8skmC7{{=%xM0zdB1u~{ zjH{Aa?h_w949)ZhB!@45%pPl7=259-OgVdk zmQwoQWA7Z!1+rm5zcWp5dWI~o=wyb@u%wq&3`;`m>+ZS5Dd9ium0Xrr1|cPJv*s^&Ujrhosi+16LRk?i z3)T`TMG(q)!7qF=`o62rxpeJoU;7%~`qJYc(Q6Z%Zeh*)KDEEkx?gHGoBO0`N`IKq zFJ~#OB`d7;Dmty%82^GP+)>#ed-X7@c55|~W`9`F=?)klA7NtNIPLZr%~p#fO^70m z4K=M>%DV4wokbw(3AVAdkHT2I_jQNR%%vhFibO~AWP9SUu~)tS<`p5ZK?r_#!LRtS ze>&aXX=6&6{gf|Gl#zR?nm^WTG*ZfJNYNgUXH5pZ0khK`%Az3ebgW_nCcb(!hs zpakAKgg5Ar5etp9j-qF|YyB+O-nJDX1beL-WBzE$svX8@>I8q!G==pP4vZH}bq16U zmag83U3Xc{_{11mLe`sNFx#UlGR7xbELyb}{mwMA+cq&XThQwcF{Z*8!ywBTWI4|H zx_hH!Ew2h5>+10=t;0B3&y8dawb!kYpa1rXYd5+_^lALx>mPdI0dVlnOVLqurqJ;T zrY!bJwLl49KTu9!@pxCQ~kTCkes z3zs58Nq6g7Of}8K#00yn7-z%GkY=nHYsC!vTiIbEX0q?lt#L*sS~%|sq3#T%X@bV# z`ZLICmexoc6_u3vERhT}V<;0{OBB4PC~|x=rrAtrw_5~n>#T0&YQuCW2*#teCW;$) zE0G~W2(h~A&Aus1mfjFu{j>h|j%7&4kzfMJZ(sM71I)rZC zNQ{!;U7e%=b^M(5fqL-=N(qD)2&wCdNM-2f8EGVIgVuBxEfwuXLYl_JX+kTGh_s|C z97PLI6;g(JA<}FkJP2D6d>zN@y&)E!D2}NkOLd*%K~23DUVw3+4a^xJ1q$zcqNRPJ zlGWjn2){kDeaE?0BzN8Zhx4brwL$KC{JyFjzS*hyV^vw;s;VZp+5#*{D~XdB1UOSm zj1UlnAUHLbCgSkU;Y`f}=yf`5-8RF7-$UfI{r%yxAu0}UC7qXebgZZc$F+V}RazO|WcIRB%kZRW$zTl$DSe>bjvB1f!0 z=l)Kz;Jry0-WziF9)g(%ir^K-4$*1|!4rZb&ojy*!`d7f0+CXiE1i677%%+xs2!NE z|7iP?WB&hCKRojSzWKG!a>AS6bJc5~_vAO;f7k7w%?JIbh7g4F5-aKn{q*!?Jz-aM zSc5keu~rg7Qso6@uSe0DC7YQdkrtCUvMPWHXcZ4f$0t7djF-OpXYYCQtHPI0I{9Hc z|2U5EP{tCHxo22NhY)~}42DArQzzUA1d;McJT^Uv##86>pb056(xF#*hvBM2&3{co=kHbDJO654cwF1t#cO^P zbvETvx_?8sRe6?Sii)Dh866uXNn(^xG}^VIKCz2N2KB0Z2i`zok|jELxTXX*6@^acgS2R|}@-s!qI^1iI!+cey^>4y0` z?R~m0`%iVwJi!EUa8{aC(J*jFZ$aEjf}pBAzRq_= zNP*I_2HORPQksUY7eX?4Bp#HWky@O;bn7)XJ@UoC_I1pLfA|S@e04i5XWuMrwv&_s zZ+JjHN3aIG1u5n>SVhe*GX<3mWJOg+@jC|sl173Hj-sr`H@J%6JXztn{;nBr-_XYx z%i4`yRxNCB6e&CHxCR}?Sm(KCO9x{N#s!W%WIy)VXII8Xn~aW+BBf+>Vida~u;rdR zFl9y@S;ltSkFpfpcgckiDnu|;c}6iD*3w+#YBBm8pd-MXbbSsQ7$6X8DS8y&lqAXN zk6OKeYwn$T#Gc6qQ~$6Yj|b|21NP^{AO5!Y`=5M$zPILa1TShsIm8NL8PGzY=2o)- zQSY*fK=2TRB281YjtBw9=e3EGl<~!@X^k($Wt)kU2F>}a5yBJ2nvr&k+tzKRKg?)r z!5terte+avAC`=@6m406ceF_w#UvzD6=O;xU>~1S~}2@fmC@Ku_jiEB+`T+upyvzZ7SE10tAtq zi&>>MiZxfVNLCCO78(6PpJCRgn4_Lo){qz3 zpHep-K;d|)2~tzJKvC7ZzsNdg3xn+gAnIr5$ON`fB;EEK~w=DLn4LW zMF^fG@K4sb<8-jkBiQE!XIEP5u(sa0a=!lClm=%5#tQP>l4S*XQIThJRPUlB%QEso zpM2P-H#^JB^bDIPXV}^q_J>xUTYC8^r2a~4(aMttSO4PTZ(Vdp=c?;(3eS7zXUOin zv}#N&y&CTpq2#`C+K?k-V~kCV5jUG4!U8G6Atsu*Z&}p6>-EP!j&J|;kx^gWd--`> ze&afxutouRuYd1v%Ov?ns9&9P0q=YJn>g#+Kc3w8sKc)s^!tnRqTIc*P6h;CNW`DC*$$~C zm9rG4BFl1y!y&y+pKV*FnC=W1mXH+!<3OsYY>!WTWrsDpeBtJ+e`mh=)3YDebKh^( zLX78_V5-BloSP)*2q`r-Ppg|E`H?c%B#EM0vmHxfRqw(IA@R;q<^{t+k72)0*6UIZhc%J7 zvU9tW9*YVpXQMG^SN!T*sjz*g3C7@F=O! zIfgo?0!}C@FKTGya6n$=_)sB)sp;E{;*&$Ea-H4^an$V($vXSA|OSbVddv&2c>a;;(CN2 zFh01Tfb|06Qc456U|3K8M z!a0Et3N0k9NRvvizGgkdQG}8aN@}D|ShQ>@~Qs&kk{gb50ZCO4MKl%$QAD2_-|ohTU=>!mwg^xP*M z-u>D6S3c;myy)_)c-cRl!1(eVH*MQ+@18}G@9l!GB@*7_aKyUa>69{{2}E*^LMjD> zdbdT+F-T%9N#a@#A%&z7$25`ztu1CONP}0rYfkaKxG0pXi6t2T_C6kCu`O~5E>UGeol@jg+z&37A0#R zeKR;a5xkjj)FvnETaf*~06{VzUn%s+Vj z_R4FR1s=8E-m~qA`Il$?{)j~FE09{?Q38~Zcx#c=-ma=Fsfq$`EVd{qvJC4Cb(&uw zilm=dxbWoN4?N;yagz4G{j*;`?B{*>UaQba`dDX+qoo$0Kt-BHqd_B%iFHI8H;CdG z>q9NGG!@2sgjNI{;iA^n#mKUARF&DQZoK1RJGTpea|s76i~W*44!)`C&BorCgA*M^ zah#&0M9MnmQV3T|gvye9P|VTZGX_P$ury?qXIK~t7mQ5WKhv$Tx6RCKyYumnKAI1I z@FRb(TjUY1qYl`OOMi3j;_jw+}j{W(Q2q^qB2r42!VQ zN6oL7z3#gI_~|37A3yhJKR{S7JBRN7_=c$dJ&ol70*9{(@?oDrR?r{j*dQ;Iar)+} z$nH&&l*_NZ@-tX7DXgoy~7?*;gR+NooYz3tSFC#wJ z1xe#1bv*^Pc!w66M9S!QGm11xs&Po)toysaea63jiFe(=fV&U8;;%6?bBDT(y{{H^ zeY~WJae>p&V<|OKX#xVJlbCf8B()O^%7)d!ggtA0_HK-5JapPAGFVa-kfBCP%hJ*! zGjnsKi6LbI!|OTnutPcJ}><49E2Q!}#pFKw~{+?I>zT*~I@p zscpLCmeS;?L48|&B!|Q$aDr_ za+HsOFI81zltYCIf}kp^Xu@hdjr9a)!CMqjHdc*COa?=2(@;#}W>icHs;UN|a44#} z#z)iNcC2474m;y}55C>e;nQFF?m^$nZ+-tlw(7DCre+<7sKg^fV<@3No!pjiQ~U zw3D1H%V@Pbw7P9toetf0hb+qpQW4~o7UDfZG&HuNwifF=lS##-sA*hGQjW&QlB%v~ zn#dLxAs~fB6>7GM3^|2+ulj`z4HE0_Odl+11UaXlFxS&oOt zds~S_#+O)>0nPuq|aNaCQl7oD!g^nWu(zmMM*g+7z_q9t|5s1q#WrMw{HFPiMP7l zi}qallfCb_kvh#MAHIYO;*Q0jf2X>rgstKMzOEZAk#JY}09qpx6a8CxiZ&XRq$q8$ zGWqsK_vkO@cHqDN(~Ezn*Y}+Zf6eVrI^3G>+)s6ITk5L0MPr>%%Ea$EF;q+XKq4Rk zawAE!M+-}$K?#pmjPjZJceWR{pOq!awIBTA`TXdjU;ozE_s6nc{>;bHo0*-hR<3?& zsK<{DPN=e~D5?oXT~akQ!*M~sKcFfnxT;{-AJAXlSeOhpPncO+{^bQ1Uw-|w|N7CK zf5C5-GX2}EPrv8&)Mb(Ns;eHReD!(=ClZKZkfR|R;|asELMGXF@^<&BX=*Ng^?N@3 z|5tSUuj}8x`F$RE-#>LCZS5TGzUp30QEazmMUv1Ml~j2mQMT_`vI)&E&|ts(kN7f1PO_leAKmWG!Tp zfl{~-Q|gg1ZqGT35a7ILR1|EE#;gy9toFxDss<@MbzMi|mvw-^S&uC?@Z~0hwLR>) zb{8Q8+HFG`3Nr24=$EYaC-f%`S6sgqy`B5^pxl^V;?Tk|WrHEHcw5rcmdUt58BNyi zvbI_>8rImxQ%ovGqY=aLh_Z_2d?N)Sh)5C(A_4(q40tqB#fSzaeb(;2^Zw6%`6oN= zoFDz*2fz34B zLIADQP&c0Oq@ZdfA+%x+2|i8&Iyq7cgpx7k(|OW7M@>z-NQHNSvaAg%JRw~6jeoED z`!2Yl_j76MRu&IA{ClAop4VSnd7=+seH=qVoOVFKI!lmHhnS8Ek>H_;SYt^5A5aSN zM4>Vh5B(xwM4*#rvFi{NjdghIky=j8T@H)FOMwls+kzB~Cu7Ew3W-Eblgwjz91*-( zD<^NY$l7fz4Ze!qoGi=1d7QPBWl5{up>`HsB}kbfq&kpvj`vaj89dHL{<;tWrR6jo z6qJsi?R=QZxCG8MO#jt*+u(wXxWUHKR2C(bO0)#h|MqwA>Q}xNfag8wsB4&CexVz! zE{(>;X@k*(PP-t@T1cJXtlu86uU*#mutK)p^vzGbYws&RcQs#r-wn%h`Ayeb-|$kN zBHzNtyuMkB>W$KfGo45&8%<3`T9^-jek5T(7);0Z;t-Q+uUMsCVmn$Kfa6`UUH&`>uQMzyE~y-AFkvWm$m^MQCcDq)O&#f_Ikj z<~qfok5md}QiO`%^TbHH-5yG7s=A^qYZ`3^#o?#=-o?K;O8387FL}!cdHvtqXCv8h z_}`6pUp4QG!GrK)>WQKn7YwA1ghQhUG||-RJkCOGA)r@#i;J&Zy6NrTz5CY}jUM#3 zFXOEr{oP8|{wJ?D|HCs7X}jw;t~*Q3_MVU_bUPbGiDU>g-cl3=Y2#xkoH1lZB3x`j zB_=(6CQRP(`sY0QSsPbh`R&)A^;DkwvbX-;uK&yLeL1y~z0?=a625sFu+Uh+sH|CA zS!3VYI_v!rRpWkXIZLXdRRk9SXh$j124bax#9Rz$a0HB#}~6mKD}oymh!H()VoB zV6?$HC`T3JVaev^gweRdJBNs+5ZXjI&bC9h(QYMd?7og-RH0Q=d!z=^P~cRX3x9bH zS6;i1dz^Mt@=k~EUGQrzy($IsRkhRhKNw;7(7J5Q&zgT4JjgqZ%x&d7lR8jZC@hp#GHPOP z1F5mXP+CE41vUt@(g+d%S1E}$5}g_HR!Xatk|hcwAxWdRWv0`nlSV^&mKYKp=|4hf zgw#wX1#4?-42J_oqanl1O~#`UbyZVL3P$}g!%@j(9OWyc%|4^SCZl1W@u;AvER)Jo zloiAAh|#E`vX07H8t-UqG|+lta3TnTh*Sx09j8zyEhR{Wh%p?3=5ttw&z%ox7v8ZcY$L6N52^G)qVlnHn8#V?upmT@{yXKWxY9 zLmqG+|LOB?T%-1f-|}*_P|0=eQ|~!r+?x_5PQ!Y}!!fJto2;&^v$4L(#>OBTIR_&~ zgE52tkZN4ueIQ9&Xk$e1_Jm;F_Hk{#cV@RcKKCpC?@O-suWw%PEADdVyZ9Z)-t^~n zRbN&N`*(6p({s*G_22;?ECNqz%z-~r1Y{6&+TCk+9Cg&^&wA4bRv+~sCrwg}P7r{tDzJ`VYsyKg9jr z9k1d~?{TISTAh@J@?~1OztLLvj4?EI#bi8UFdQ))45{j7m&|+5J^Z0(eM5T7-@oj& z|LxEJ+_x`aeBpUWo!;A7|7diQ>a;^ZV?%5NN~w@WMXWm!ejqJ67BmeMlZMf- zptSBXmt?P4|H(IYANR<=<8z<*$U#2`A9}-cFtb}*)xK-a3RQJ?yg<5uZ)_|cBna1_ zrW7l!BzpRL@;)#a7pxCPtPjSNbwidXwA(4YZkxH8E?FX(?qPFqAdLIdzQ)!&@ z!AX<8{q*}k^1ShI7_Pc**NwfC{7%*%P2q9HHP?~4(&w%Asx2mGDB%~iid~6R>6l`z z4QXOfT1^#yQ}TmtV#=qChU2Dj%q?tTad{r0QjjTYd-gKi+@PF{DJB&Q^DTNa9Y&)u zKf8P{5>Hi@{QSyQuG$*|VvadvfkY)tijp7xY7bw(;5t?Z4aXlg$6`BBPHO7Lp|!yI zh;Oq&kQhT%H4q#|fl>jZW7wgTYRahhNMEBu4ZcR;5Fw(~l$2-%MyVsU)K|>E^q()f z_gmk{r#^ddGMvwU_VYaDxi8>FuYLQjTb_878jq_plu}CTc(4{|1S%${0#XSO@k=a) zL@9+5;s7cy2#HpDYT$wp!1_ShI7;g%%E1< z%y=+jb8{aX8!PPFy@&qBI@M&1ws2Yn44(B8ug-KG3 z(rB$w#?WfDXtmqYB*`JiJmBHq{>w+)clEs=`|k^b?|lC!-0i*(V)OWiUENx}Xhrz? z%rsG1mT2-MCCgigXh_ytgcFRCXU?c9NLT;ri4VDZ_5O2j9NEq1&weFVs@bhw@s!z? zenp<9#|0!!Q!^?iYz&8Nj>Zg%l2K(Rbr9!T)HODQqYzR`Jw-L!h9pf%lT@~oXo2{s;5{LZUH=h2K^N+dJ&H3Sl zzgrp37e4uZPG2mTJ>;%rTbJovpdFPY)w=W;OMD>q-y(6n2pXr)<^Y0b*=^}*Gg@6rE*ubc=jH9G3IHAAe zg8WwF>=D-aOh`?V7}7+ODk!8$wf4kH-Y-5CNL9j;LSjsH>RWQ5F?NQR2MA0qf(2L1806z&eVu#<^(F z_EQ&gl=DRRjnR@MNk|e!kO64~X{yPxgw|A|m8Ka8ML?pIMks|15^JFZ)NljD_>0h|*T?UG&;c!?BwX5MqCBj09rxfHC@4y~X91?7sTa zec$@g?_c)#o31~a!UN#m4|p&)z5QKQuKvY^+g)9rrj-Vg<;lr2m^7m`GfSFE>e^B} zMVh9VG{rhk;~ZK}Jr8+A*lDd05-icMsnQgo611M0lu$I*#*HYD)n#?a>g`YcO1yJ9l#-_kYJ(MCkm4lyh9?UxZCPyHo zI#pKVduoY}#FP-_0b0jo^2Cs|a)go?lar(^k~E{;>7b29oAlrAvUAsu`ZvENKe*th zzq4x#A@Ho5-kj4OcK=&c{eADCp4>glGO4AUHacKZHW8IuJFF8}8>JQ^IGVD=H%02} z@$skM_nsGg|6Oms{)?M|*WU;Q-2bHYh7Ww6$J{JMw3lY<)$3n`vrqTdX5-`GZ8(qm!~w{-c)RIZDgJP(lPD2trVl z6{DhHQr3*h3Mb7cW!`&5*mud~Z(sK2|MoNP^2NZn?qk_7%S);mzciKN!6r#%o@eMp zBP0Z+NZUC&O)(}#=>(g_-X6Mhb+t1lu43WJwnH3vDoYhRITrEG5emvQ|QpXp{np16Skg3HTbL z1x5=*XuvzHvowvv)(+oz$|f+bJdMyaUQyK%hU4*Q9neHys>dOPr)?x{qv#}xmeI5l zO)pEB$y3rqk>^pZkfoAtMhZDx*lK!Azbz{ih^&xA60td{_c1TS? zx|;c(VPRSvr*2g+&$?JIu{@SXf%3)ovj~pzsMlzW8ePtdHsDn&r7RSMMFu zpt;vQ&)^oP-iFzprrVZmKjAb?)+X!DkoT6U%MnFUG9Hyo3Qt`*>awIi7%&`eP)>$; zTgNOi8K&7I@%hqHprk-cG0jhL2q}El>b~<{Pk!kqTkSSK{=pxQfAWvQT>+h(9}l}JjN)53`jgm2#k@WX1XB+5JJ*wDduy-T&`)i4OLOIYu6^J zxtd~J;#@;HDd_Y%ENhRsxRuMVTSpnq?QeA)w>WkyOEWn? zz2th%|M^vH3@4m;?2(*u%p!-(Nkmi7>MoL`24fVHQA3s)w(rB^AHl8Crb=E)dUgnHfj;Qvk2mWpseg|Nb)CbKl9ONz3=$b?O*)O zvkv-x9({{j^4a&kW98UWZu$E0X!B%WO}2H?gj^}Kl%y&>5WJ%_SZ`^ZV^T+4pi;!* zKq%_kMjYT&k|ZP;A4r2B)o~hv2m~!aDXI`q-V*{$#$%LHQ+O^U*3iUu26<|b%1=#H znt;a#7dPibp|k*Blbe9lDG<$N4u?dtu(S}7ph_X7q8mUU@!rwz4;hb!kwq>-3_g_c zrJw|8sZh$q1D6nJ5xYN9N<`yub%Rm@leAGdgcW2GXWK{y9_SZ9;ITHdRSA8AXnKVxn=*c?7hK6c3w8!<|l?Z_Zhr zgpa@LZEyJTY^Ll(|MHo?(Rb^k?|Lnpg;QE{>)RfoA7mtt61Z(m<0&R1`fF?K*|)-V zd)Han7&9tt<>98JuG)d zWAEm>zqm|3{-0lPH#b~+tsFy`7`s~>aq)e#aaLKmK%J{?|s$N*|ZX4ho^|9e5e%S z;kMz(L4gn|N{y9NBwDk%w#IlkqIM0n_d7#6^9CVUfBvi9{oj7hrQbdmzx8Ud_p;+| z*Dm|dbHU%oBUSK$s+v%gC1nxQ!?o6AX+j7dYa6V!M6G>5A`WDEXroX@BSgS?8k6bnXBpCH@jMUft{sM{shOI9 zKue8xVM@0YDCMJMJqVICW`5x%Hbh!xuA$dzq0;=r=J4Y$ax2#Ymmc&parQgkK)$$L zU-88c|CJ8hRtQCEK!t#Gg1WAdwgwN)1XN|gR#bIGQClXBWTUjK4kz@Bn)$gNiS#5& zpoOQELOZkM#*#25ZKdSxV@OhkAMa$ab`@o9_>T)NW7paQsWcbwTxV@qljJQP`lq+! z)~6iDOgj*&fSEap-ux1C3-h?9z%?Z@Ie~gIp!&rHT(fI8lX1mxIALwQ&&q0_sw}|U zs06W5RtM~lQ6pOA!!)!wAhfyEWZ6eQ`N}7SpYIy~LHC&(y8fsN4}e3CzX`|O>am)Sey=3hfmS?w8l_f{-*uv?zaxBgD=;?|pFT0E{Uc14*VZ))@mbl+NZpU0wVeBSa zrffgywj_B@Jz2*V>(o`w%+ey2lXySn-Zu&DcAy-WYz#*vMxb_ntPM0(?DTjSJuJSdkiH?w60}rNN|wp^5-Dq{qCwUgH^tlrl2{x` zV%I0j>Gry`x^0pyCow5mmSR#Plv1ZloxXowubyK?^6npe@!U%nW?gg4?H}@g{9Qcn zIZx(|Z~7;0dG*?MAwy~ty)s%Oq!3!NT??LJTJnLJ@t-ff z|Ij)A`npRW`&qYsXitR8KluFr^7sF`*S&_y1$D|6H9y-)PuwwMADtU=nb zW5)df`}XW*@9G+R*ZQmtCsdXztjgYkPCg*kuit3y`Yg_Uc`O_ragW;$)$-wQaKk-& zN40&i9~MUmZBQnOftVUPGc&T4=?8eByA!wlnGfFfq>G>QuV3dKPdMWby}kTS*Ec@* z39kLo=UABQ9JTY}pT0Vj!+VLkVY5G=s2eJ0DQZXUgg0q^F+x^F)toF!n;4}?$1oWe zRAqxmb7tmR)XpAmC&M@Y;v1hmG&|(PcfaMVzZpF3W$*l*T(3jogVata0RrJQ5`#%H zyflnTPnu*T#?W|&RFa*wZ@bQW*X!{`)!OrwcR$r>d*xZcNIP#l}E-t*L!H ztj^BQk>#!VOpCwHQ~hV%jBho*{J<$UJM`zze&OHmJ7H^L|LPep{x7fh^{;&;y9S7x zZws@_U46t>YafwFalfVx$NJzz+`}!lsi?||aaEJ&In88@s~V(GABnh+1PcIq#)jYWzm8zaPt zjPL;?qKC1uQK^tk^+GsG<`yFIIp06443}$A};vl z)tqwTvCPbLx$3$-EY5bg{Vk5-l;f63?M}p4(VE#td+9K|9Z-+<;#~>S;EHu@-B3+x zlxtAJfv;$)ag^{mM`L3iop%l`;`gg|F)T2iZ{t1p0iQ^H&RzffFE9Vz=gxy&KmJ2+ zIX7tiQSP>Pz0J)CKHO|=&vl<@>iW1;OERNLj3CjHn5zp)2(nZmrNjei+M?a*F*`d) zr`ILRQ)WAoxmiUk*Bo-hVTjN$+1MMiHEPRMSMT9hSFE#lvtrLmpUZacWAEmK*_j#c za@!MFY9LCE0Q0G4$ryB z(EWVU1MWn3Ve92%t?GyHyCXG(jgRK}uZkAjQ<=CzrZ@Ep5*Vjj63JzcH@%5c|NWWKb};Tlb;LHyh@$%_zUg#KNUWD-v2%>f5@NR9h-JU zHrcqVs7EhLRk;03I~R*HEtcndY}vX@cV-TgWeBOyFGTvByDvWL{7A@HepzJjdXImY*QzrGr+-*>RSDFz!GY;2BMAC_#6Moj9)d7XSV>&>4fgj%XL2T0;R-s8kiuIL~4Z;jws z;?@8B+Z99~f8}4IyGt`}+=<31x%W?ICHKg4vnab8u=I7^W_c~;)_P5$wj(A4-y$}2>zxRu|*MlGXhkk5+ zC+oZqpH0?Wk5~E0vKqa%DMt4wi&CzxtZ~h?d)TwuXH+%x#|5~2Yli3 z3%+YV_L=`(8v5Y--4oO9iE3lzcvlRctMLz=?d091*&Z{UoGr_XY&~Qgw%W%c05m#v~;%< zA}nZWWPHE5nEO=Klw}#iAtxnORWR7>GuRvxJam?}zW#*!Kjwuy|KrQ{NiX={y&d29 z$gBC~rr?wvTaPimc!;z0BcHn zqoc9YWhBtNF*WDNEt|!j_;}vu-3)NMS#XT zs=C2B7hwS7in3@RsCdwKn}kr1B^gqfDC227(!}>_;~MIwg5XJ%Mg&3QYpjd1#Na^- zMb$XG5Y*mJ&CMP`z&nqUfQZdO?>+b!6ez@gDRm%AQxYX3X)9=~a|j$p%O)h*iy!{! zAHDWVFa0}S_zwq9Wqiw%A4+HWkojGg{rv3p^?iR9salb=>6$v4;nM`61V(F&k+j<_ zv=Y?TGn~}y+Sg}wP%x=1owlK!NqU`xEL8;WIP}nYmKJB@??c5rHm#$y$%~XptvKfk zKjb?iX&hm|)>}0-|@Zh^1%S<N z`Q*JbXOw38{ZMxAl;R2q}N{ zz29M8((h~iQ5GIgeA?4E>hPoFhu`|zmks-yFO*UUqa;5fyEAWp?G=~4zbZz@CQ6c-X>Ns97^6{2?B>U0^q4$O z@ZvvB>()iHwbGa@i5LqsLPlwd_ad6&MI=KdiJ40AqVz6mT4F~-OpS99#}_+2wy{9$ znE2rE-o;|YIfRIYJAsNtjEp1p+Bwoz8=d9MEiRJgZIUdd)$O7agHjr$jTcH@X>IsU zS=)0OFModdFkKON`|t-ply|@LZFrQKbNf!#cKAAMb%t}X<0aA*DI`Wk>X%7O{D;Vh z`qf6q5H%rU(JfW5fuVO{kCtK`#JFynk;0pHrduA-wT?xPSP&v~1WY%!0M1dD1^xAP zR@OGSe&0GP>-{h)tE(y}-fWZJhlMRyKls^i|Ci6y6CV8tZu-o#x!@oF+6%6fm_ zt$aCtWi#G9v#v`u*jT5(F<{(S#+9YA%~0o^_s(rU>h;(D>cZ=ex!pa{iOH_|$@lL) z8En4LRg+t!O32wxi^aJfbF;kz_bo{if_IHD$yY^^z3BMAe%a4hqr#?>web@ z9!(SY$GJ3|Wu%lb2sD!9k}O3j6(@!vkR)j&2{tuNQ(;}eHJ+78c+KZFx4!t)_J%#{ zHNP2V^(&wLD6OOKBnCe@=NPws-QP9E@DT);)OAIFV}m`r_Oh}*WKcA0j!Q}#kUIHQ zcX9dk`P}@+*Dkwcv~buCgp>=eoIJx$hEK}1T%4cDneVmeWGU@#htAAArZaQC=`H_V zNZa4RTYKNfJ?9Vo`21Guz3+Z2d%~=|nY#AwGBhvM_*0$rVmRotYxf?mzJ4F8{So7$ zvJUlaA?5Qv^UVt``1n`9#Y6A*Ul*f$mR73cJ@X;>EmDM@Qc}jlE`W$eXlJoD z(xU2eOg$;EjU`O;7s|SbO__G@m-)gWPaWFM_ik}C^$CyJUnALr}I z0~P*E@BK;RQK7541_FLM9SAPs*a!U~byH2LhLN1f^txh>f~vank3# z!w3aa$c+mQAwY_BDsA%kVDZi(Q7CDVI7HA<$}2)tM-cV=&RT2}18#+kp}3K96+c%< zhYJ>*h`A$G9n;gLqOKj4jX75TpO;`vHYboKlHj7Bv9>Orqm+uWX&exzErbxMT0Xun z8W%G!gb--6|0t;sytiFY(*i7zrpb4c`K=FgK{N%ysOq~6R^O%3|{lwnqKkO;Z zVB>W@giI>Vm8Gc~gn<5Ffb#+CV-XY(tPUp(E64hkKn`g0?^R=JvX3Knszqt3! z>2+sVnosF<1XbzD@|1Qj1EE1>1O(o}&Z{=K_PRBSqGU4IWOIFk(O|%MQZZ>9^%Tq# zLiE^%5Q~rfQaB%{Ro=9^FMY^M-uBWx7ymfC`L#DZ_5BaM{wNC%fRhegBJXyOo%A>V z&DP}!O2q@MQ96>Cgg|RWnkbBpMM+{bS)MaLzre!cB6%wsBeyIi9Cy?V-Zz9`$?`6% zd-rnH<=6A2^DpDm-@S|^Nx13pM{x47hwz>6|BTxnJIA5hw{ZDN&DXwvF~@Dqx&NI{ zV7}`){Mh42W{;s5u2GKH8LeN(Xk1b^fwHU^7mmTOW_{r34-0nf8&Fj>vz?5^Zq8gQ zWu}#35V<3ikk2(b&27)>inFiK*l z+;<^kN;4v$lt3WSLPagRfYfNBMUOZZb{-)_#1#qwLL3MH3PDT*>Y{u~NFC`JIu<29 zNDz=@29+9=PEpdttZouj6ul| z!-yJ(vqasM0BvF}wMtTSnvu3!v^u@Wf0rtz)$0T$LO_VZiR2R<2@rYhc~0KSNwYi_Rx)P5C>1kHB!cNb znGm7}DyBQfPMn+SjkSp17hBg%hJ6N`>#VMCvTJR~%I3(GR{VRTl9x%E9}q-%_qpFZ z=;!M3_dXq=(>ZU8zcbQ5ai){^<~kWmvptp;7nqr!r`72ql-}@&HxFsQS>o5veCQvi z=i@7%{s6kWn2av_-hDP#S6*I~#fjx;%)Y&QSzq5|T$BtaC0-_1r=8BLmyS60?BIR% zmiK=efWLh3{g~~Xz)JtZm;yG1WJ<=%EZe@nqR?2d(#r*sXOWT%dcRMI05z1Vo zTJx_+mX1Bo7aP@|J?81Z(Y62U-#rE&#OqS&pR1)NNi^PhY-2HrnHC?hF@X0NAkAZk zyRIGH2lh>xvwyl8o^(iCl+SwUe{b&nU)Se9_HKf1scd-lpSWUpmT$^ay=$bcD>hbF z*|%#CE9(Q+#})l?MPWmw($;5MGjp%}=vRL5^VhxhHQ{;Be*pkbe$@R~Jo2REnje4t zes(f?NuH_`=Q=ryy%r0zU6!^kVX`(l&96~e`_*FRu(N}Z<(Ut7%pdwOy20x`=X{zi zZZ}q#ZtlmA5w>`dZ<_6sNlAaR&%Tva_UzkW&&oPQRaY2uc6)Z=RrR>KroGtV?1RiQ zdD5dF$T=T8m-|2H?kCEo`ujxq2Y2&C_c|>W=4O~*oToQ8N7l-t&R;}Qqta6;tBeQQ zc=Ct=dOhlr}Tw#GGsfq&E_!#6$1fGNd ziv7iSK$&>9kOYE{)J%MwR7fdEvxGpPF5{FuxIpl6@`3!%oF74?IT8Z4sqsjJ(MZ77 zB{b0xE(3Tx2#MD5onO~ARaxSKoCf4NlmL?$@;pTXO;yMDsE?tCHbfa#W5oe-rFSu) z&^a6Z9gV|TM<9AttPgk();nAfc!=rk@xKNXF={N}T!cLcN%UbFvz~S5{`xED{>LXD z@$4t?_iy~ygL>A^`{JjWeD@qgt9NrdSo?^#_C#yrzg=sM#L|={RZ#(ox{4kc=VKV= z=BQ+Ef5N^&!TO-2urN2*ATmEtFt?ctX@`)rPC7P<-d zy6qtxwx!1pE*JbCGHqW- z{Tp&W|K@Q|;Boi68;2Zm>_@BWKaTIO?R!q$xI{~j(Gjtyf+tauRCw|9t}v8XD15Tsd#A<)V+X`-e@ zW&Ayh5OYm*lEed;j54n@O)yD1-Gm$=#7T@YISxgTn#wwa^vDo5PD%992nku5p}QTF zHq$SDlwbu9O771haReJZCqc;A&C(L75{ypJBEdB-78^Q6sYv$FCJ|EVmJsr`5N?|# zs_{WLO^tFoq?wtg?4}g+y_j4-ncyl*RYobFQccVhah7V-&=g~mBu6Fj1@48zBq>U# z@xE#ub-^OC+lyM^gC{tT#Ko5l@o-sACXD(UZ1hL$ULUd2pVZ@q_c)clynEqSc5UAI zFZjUOH!MTqy`TRsfAi<}-9U6-5~|^4ld^n~aPF{7Yg$=KyPctWIa!{}NvU6uOO9Ec zh*yOWe)*Y?yp{Vu@@aqQt>&QDm*4+7!f0*Y4|hK~SK(=*lP-^Zg%ts<&3?9hFnRAg zt;OvxSvd5_Zx_Q&`cG!<&nG{_IGo2_j##|(sN+w1PJgiZqruwR-wuZ3X)W5(lxt%q!7W~PVM?&M%s-zu8&)}piUmUI5&qHE3d7lsEu^x&mKXT9LLd~AJ2 zJ!!|aTeJX^Bq&A93#b~LZ@`sk83Rp&iu+U3Sn6>>(=@T$TiaufNJ-N`c|-5tS3dkt z1W|9*lhw!Lil>HPw|StcYc}?+vuDo=Ynv0+Ck=zL!AWy{d$#lX?v@=N^46~IKgjK0 zZ~xF|xHgkGkjWuNqa8mlmu2Gt0&$R%l2@%q=8z)BJGh?Q6tj?=iHO-u#t+ ze)7)b=sR%dKYP?4`jI-Q^~iVqgvZbQ3141$!66;poTW5>jyBy|1l*uxG#aou95E~k zgwZRRN!~cKyyKng*Il*wiEsSy27T5$K6owwsZM?s^6s-J`g_X8K2eN|wowW-rS1^s zNU|KIO`Oak@Iu9ndpc1*&|o4Oqot2s5|5*HbqsoKDnec3>IPReSnuMAcKUEirAVeY zbDe0C)a*!%e5z2Q5`soZr-S#IRGua{go2<1!3aX0qPzvCsY;8j3aVj+F^Wt{hGR?R zEK0^nxbr^ZnT0^gfH83rC~-K~AY_zXd20~}(#nz;g&|&}(4OFGtZnd30Hx!!J;aQT z-~+a*shS2gJzO(wzEA@f@9ICxk2(USWC&Pjp1-COW|fN0uwB6L?bm zNTDi)(gEjVF*iToWq!U*D^Ho}^_X3lCo_RH*GDNq(%Z%_FIl0a&BGsX8-8~2Rb241 zUvu@YA)PE^$M&t<;pPeJ!!h$S9qxMiEx6sSZ^^=Zmrh%d=G)PSp8#+maM|0E&hASX zPdq>T!OvM4j46wXVSmK>#+dbu2}RKW0VAWQ#!l58fq)Hx+Qvd|`XlQ!N#6gaA6<3j z3;*N}u=|F6fB&bfKiY{0z@2Y*E4+6{tz5tJ6Sk>uX_Ss#b`f>RN=fuI$u@{6uSpV3 zE6bRhonh|7ml%%O8U=w7Fjwq{sNS6Gu7miiTlfsmg{)DKQ2qJqT6M znt#TN-}v@u&Qzvw4#S>3nyAt88DqtGgPJ~AcIGSHr4 z`)U^}bz>5Qln{JO0Vk$qV=8}&T~jSU_-KBULJ|k40hFYecC_*=!#glC z8qtl8rZ>DNF|ilb*fsqfR1-DDi0K3gq>2+2?*j;r(h0%KShODKs!4o+ zvZ!NMOi7f9-N&-1*y#5ej3$hW38Tq`Nl`V8lkb>>mqZD3(>cvb?|{7TgN|MKFGhiZ9&y{zR&l7|G7bL z7k%ekTxXjcUVg#p^oP_4v_k7Ch4(k4W=ELuxa>{i4=ACjCo~^KV58fMZE#uLU z)xCRITU}@G>IQ3@V}?aVgStRx?H6r3`lPQ8R`>X`&-=>%_2)nBZ=S^9s_&959=&b- z+RL94n)Ms=3vdn_%?v9!F%($+1sI$bAB`c0wJH@M#6Uk~-TYR&XG{mlFSp&zmT z>-y{m-$E%mvK=<>nuPL&TFSe6XJt8_u(`I*?!EiiyWVHDKc;lyXGz|Ec{aOno@*-m z?hk$DH~!ogZvuItyYe`>D$i@Pn;n(4Ng>9m<&Tq0|AqSVxAOq!y#IRMBI zLQW?laoQ@UjVkAC^afV3ZgtjStD2^6aMn)E+)>gL{S^XZ5=@>Vl_blvnCmc=BgLN? zL-)M**rtwCf~usfDypi&IZsg(*ruW`Y5+p8j5Y>LhC{TL1TQd2PSDZY;p>L+sKEL_ zo~Q9zgP_&zlBFiTD}+KN8kNSp28AL~67RzFU|%7KK29wp8iB7Wyl*JWG1f)ot`CB$ zsi>OaSe4MnGUx)`Dl(me38R(FJMGLPY@Z~bqrK%Mq)rGyAf%?)+yyG(+TCNm z`tRRhG#WD)j96J&<@()gj3;A4@FY^81X$;(_cw0iFE37|{u6EfgDr<0@ld7Y&hP%@ zmw)K(=Qmz|)P)B?2HfUmM?c+;`>&Tm7%c@_NlZ+VCP;}uVYDWMh$3re8J%vM+1XiU zXJ<+C7OgDhkV6uZ1cVG^d5(7=RKjGq$*8}96qXPIMWgu2k9YBJU;i0v8v_<*yWH!v z6R~x{<+~@`{-hahb^JWtZilqD6=XtF4j2svOvXbdlL?#sG3y&8n%Q5n#QxbZyjS4vvYG)wPj;a5*sKoru53>7xO!IJZAIC zOMY^fSG=1)yY;PZ&^>*??N6XPyKutl?wubPj|QhIA!sLt+-MRNO>tQ&K`Lqw;802s zLL8v^;HL&u4O85n4-lM-QyK+{Nf2Rbyb??UivnH>5~FBoNiy}Z4C-idQ^X0Cmj9_! zjTl~u2%c1F;!Gsah=xSRgLSLjAOPd?4Ur2wPoa zn}(*UD2g$*u~bDtQ`b?7;HRT{vHyBwK$Frs>Wsa|Bnd*PXw)ZOmzbU(oo%9Yp|MyS z!yC&5TQjtt(gqrpd@kwDJdqIgy!Ar|C&M_H^^8Z~hk!g*`uauM+lRIj z)!H)GVcXV47M7Rk&CQYL89~aurBff6lICpsc{Z2~5t6un?ajR@?=GC}o!xlLht50b z&w1oMZi`gv@M1Fghh{Q-SZY+On`bQcT5Q>}$o50F(wm(jNmHa$1gTbo(%(QO@2o=l zi?y({nlteadCU|3k3avj?|mg+=&eaJdPOdpzd%SKgo^lU&lDQrfPiZpB7~{L3z9TW z7}F$;v^%MoG~o+7H{H|k_xO8U@$R>LfxmkC3;qwE8VDibiywL8Tq_ADNLxM%*F1p0 zw#LRHd@>xewz|sNdY_HqnDxyuqoTHG^O@G{;>-W_lVAGjUqABxyz3(ecYwaviHjU@ z>Rs}+>#ur5H5xrP)8fRrZqCyD3|qD=u&}($%-kF%&G+fFb&g0{Z?@gTt`g0}opGOs z9Mot0zSgJS`+6?i?YYgN^2lB)AD*fa?tPSFVhr431*B9tOYQ;adO z8!uIy7FbJJRne^N9CcMu6(xN(1u(icb_gtklN{XpjIvTF6i_+YU)lJ5u zF}+?FtqrRyYxGAoDoK%AVC#5T4BFF78%FVa63yAJsu3a3?&P#Ok+cc^fGkUesZ7dS zj1J&xg7+wu;+(+SSTN~C;Dn;G4nhM+oKx5^&EZf25hTufq>8BA#yWJO_8XP)PGVi4 z4iUlYtjCGyXE7o+x0F_lYmaj-f?rhh6~-w^^cy-K_XsH^GKhZK=|1y~|Nf)1U;L(*uo_NIZd{x=#rzXbV;cww0f|mi(lkLSL0!cJ z_l@C%>sR~i+Z;10Yr5TpPNtaYrOfsc+TAuYog944`oJ?7PWZ-!*PwLDJx{wSKf7cn z7ykNs+L>mqle0E%D6MC6bHGiHSm1tlJep&UTBO@fSzMSwX@&QJyxl`81JZ!h2;Ii1 zdH&hJV2IM_+i{_2x&DwL07Tt9w3C7Lz+DIW* zwM7NT*ME2^yVl2?e8en&a_j9JvUP?dj@(X~XEb%mWL(hi4=F3lq-Yovg0hCeuwpbU z8BH4cgMv<$uzhicZHqH3&38!>6*p!VIn~a3Osb})Xh%^-;)PVv_s}!}m9#&&aOhD_ znhZ9^fBUv`ZqPk_@lzkqr{DQrZgs}3?yzsqu79n^lM_=VNVOo-G7_(hKr0PsYHz2r z!70vGL~d2p1gwQb333xX2U1CFuw<#8n&TmefJ9=XMrs2A+D6dH4MsuZ<7Ss8F&)-> zk5uu$m&TxVlr=&0fq2(YyNcigS{Z_u2r2(lTP&kSRV6ZNgO$KYi4u~;WF$#~k`YD_ z=^{}sCrkoT(D*>r#1VL644(b|lxgBCptOm_l9V7L%_PlRq*;#AYU)*q6B0zgI~!4o z-s9?;rYW(ti@{OCM=y%AksBYK_XHtgA%cuKB}z?yC*Y^DHK8;@#c_L8mo#+=L13$f zVl<3N^#W?^8J86s{V}6)+4rsahfwst@~V%0?RWZpebom)#r6O8cCu|p&6F!UpKK)0 z@3zvJEekVjU0z^$+ZKAg84{z>T6^ck`IAPxZP&nlX4}%->a_BUBwTW9aeCX>oFL8o6#JF~C7)BPTG{tv(Y zdH>cAe(DB&-V2`pLiS$vLt3-Tvuisqf1)eLf2;6IiooJrm&3L%vHg&3EN$6JlBCfC zlxVb01|IceN%-P~^j8JR&$hRl-9-t~WGpXt!X?*k9JSEWcg&Q3h^QxbFUBKXObY6% zjxrdnBRS8xSi~|l3_FWJlH}6@Jl`*SFbIOT$nO-5`^d_~f2W*gdO#hFU_(*$AGJ11<@P>m<7?c2-Bo)y*xV^%gNtPjT&wH+#x{>#jk9j{qi z*}Hn~*M7kNDzEzQ)-#{|Opdt2J;a3{c;m^%=Drsg;qTvTXL+xaGdt5`%eE~nE^ndT zX$O;LKMy*4r%ih2rdw{lZ*tAIx#z>4`UidXKJeDp)9$6zHnbNLd+${9EUm(+-lMz^ zGjS6N-(q(3T$mE%25m{(%Um3MyftH0ChdE@&&ivPvuK_z!geetn8QQO;j ziqWwMP$of16B{Md1FSYCVq!zgeG0)(y?h>~d%d;s*yXIHsVxxq;kv2uo&)c7rKZz< z6SJD4q*q~*xJSBau@OQ<@dhiVUXB7K1=iX4Tu&Q8Zra#Mw88nf--fBF+y^&3ls5!# zv32}TC>@i<%c5Y=pWvLEW^rj68yF5MhSNev8-ulurfD#8nsMbFHuxwX6GVTWRwP1_ zC_yLD812bT^hUzeW9Xy*0^uR(I32P@oQzL(=Ryix5KJmh-GGt-qg4EN2`Ft7y`nN^ zZZyFYyg*JR$-)OREy&Xdnu&&XN1|msY&$=Fx9<02!nkrcAH2+Z@9Z6S%jdhg7`^H( z2QRVu=qsK~FzvRQtiDaz^0A4D5DOC~3@TZSY_XquWdO-a%WWpV-%WWu#Ohy3c2>m&7V zIArh23cL2MFc^$*&Qn=WT~FZyM8{0y0=1p`Xs6ORlcwLAU6^}#IVtvBy?5;ox)1%f z>yN(hxZi#Ml+XOz7rFKEJDxZh_WwEf(1Mu2olTLjTFL19knx`zjpv4TyUqOkJWI<9 zbh;h#)NuHrEsi_!cx+S9Ox93Xq*Cm=ZWqH*MZ06T_PTX829~6|Owqv4F8USU{PC|D zl#aQXF2muF({FY-_c-k&7CK|*=Xx|vO;tN4lY+^l#)=N5OBnUnSzlXcW22;~1DoTx z(a!hUY+LBDZE==vCnYliB?D4wf)~-qHuW7yokUrLj9!GgsKJVH+MRv+U;WcL@BPTP ze#xKT`6f5$zMgmf&$<8YZ^nJ^c#C^pd(F;w)@5;o(b0#I8$~`Hdm1Ciq(u5aA|zRs zB8`dFyR}&F2(CefK%xvr$#|$Y(;Sj1{X%Pl5`xsk4a5mWmL#a@Aty|yC?-jwNm1+g z0u!+SO4AJCBX3-L{!i8uG*IJ>4LI?#?5TwWG1cXG$ zfIt$0oYJ<4(=ZW(2fd5MhMekN!xTXXf;35Dk)%v~{)9y6{ga9`N~B=g-HSp_=OV?# zH&Od3RHUQC9~rxH&iiQc6%d4QAD7!@Vs z(TLjm%TVTYyx;e-N(i#67c$y=31fCxAnFk_P!r}@}v8{yYbt7{_~&xRNC{~%;o2Q;r`WR z__9zH$9Gy8OEYbj=Xz}0zK!18EUj*rG;5LM30f(%HeQ%y!z9V|BKY-ysFc#Ad722p zR$Dbk$YArjEUD`W#i*d1R5%|C;ozr3e;)`=p-qG})OCYYf~?&}gn%)Itlf@iT~3-`~4TdpE~=^SnrehmN}`N%BLGC9w-PiA$Xj%6q7N7 zjZN0pH(1@+WOFcCD}#QW%(`y}KYaba{BEWk|Lc0~KRy>P^g?%d`Tj2;Q+9ZgkFJKxcCE0shBu|LUq zn$Z-HQsI1{X=;L-PUM}9`(Ow-+u*%BQ1pZlrNNDBrd~faop499yMO~yu$ZOcqI}hb z12UyZLIm$EV5u8Ru%1vl>bjx1Go#b4*T5!RycX8_zwsX^?m$`0b z#D%}!#e65_Pi}uSotYWN<1rG!Y%izNiaB1r*$!!z;GAGoIIh2LLRnN4;}L6XYpkxW zv9>v&t{OscSm&qt9f4;0r?!E{I`*ev2O)=9r~9;DU%&eP`~BIU@R`qk>7eiF|HSo2 zUw8oA=N@;(Tf4Mx=Vc$Yb#G;@(|m1xp5C2O@=T$pEOc9y)A z(d!zHI=n+?W_EgLt8vvJCVD1|tnJ;+-qnI}lkn4DT*A+OeGR|9dN*1t&baMK9CySr z|MvBtFrHLA>Q8UN)}Ccyp+}+xb<-e(Vl-)RLE}Y>{>BQct9|zDonSrGPO!2*CefOu z**4o2=h)Kg&}$jew#Ej5_i>EeG||MSq=`sX;bLiQEzn@{?B{xJ`Oky3zvLg^$D?jw zckr(t_vgI+eIFJNz2j{kw)4u%{;4X9twKcdLN`swQc0(!>F8K^geDr%O(L;EP}yki zPlNyu$}7AK(d33et60D&5pcobhzDd3pw$5aiV8j+_6a0flP3l(evmF)9#AW z2BFL}kSYEhQX!N@n3baY$i*D+}6G9OP=&51S`$!WBzQNguY7}C6 zwfp}r_&D8(h^qs^l!Q3_h~2Kph!@@yoW*EKl1w{!Zc0}Xf~IMv?iz=$Ewyz!OVhne zV#ilK=!G|)x8iwEd>EaXd9k+Vy2tpYe(g*|a?_i`FJ5$`?l0dv?_D&)h=AIf%kocEu#dvkcW~CGRaG+D z++<^YgZ0f38-p?Z(b!razOLJIuQs#WzEy4P_5XDC4V!ZO-`3+EcsCCJ#aFrZCTE;p z3^v{vn)=jUD`T$PVtHYP#l<=1mX>IBy5#LPNt$3x5($4<7RhW%#jdc4P=;uNY$zsU zs=B5ak0?eX#*-mU+2CD#ZlatZW)CQ>akjx|MVdxrb*DFj(ECYq2|{bUi`{DP9d%vc zeK?SK>|K-?IOiD;2lQ9>v9Y?s>iQZRqk@f5!RD}}ZoPd0 zHwa;OQ+DMcB zMC$Y-R;K6eUc3I9N3>qVr}PJT(1UM03fAAh@b9_XosQz7OIOu#+wz;wwvvZKc!(C^ zupp#}63j?{8uT|=Tisw|Fkmz;H!H7S5_*Tc-3?djm%ZuWJI*(Bz4xDA3PK*140oMl z2)9cTjW%+6F!vGok!8`-fkX;oW5xUE$53*LOAjFAbi!sqi30~)A!WqOLVQQ~7$PeK z2x8+!9Ki3oh?1QmSDPl1nwrMpZAEM&O9UP%MNIehHWoQT;%tMp704)^jWWZyuZlWm zK%pYuG)`vXvn+{T5T!+wMb-6GmKZQv5W=+3Ay8LkMAL?N$aL1@tw(q$8;hN$RFBGr z!UiTqELtRiX4;Ud8yjhdX@U@rJkz97LgTS@gH{2RNa_H!iv&tZh-xe+sf9a0OVTQO zVFi(^iPXZvFCrrUTiB=e;a6sL7CRKyAo-E6*Svqvb6E6PDcfWdvr~f6d ze)D?{>NyG_aPBJ}i&Xk1<@(+aSH<`gWs+F@cuCVVDD5Lcx3mYQEQkO?GaQ#}PD*yK z4cW6XW>7Y?S}8^YM1V~ z$5dsc%dPEOVPhkPR!ahP-QYrO?8L&uM~~_Bd+h`9?^XK6LykM~QQDaGFMZ>CH`;gk z_qG0?C_Dg8(y;a9V;)+M`|s64v@>JKj3!NVG@@xqVodBf6EhvOR%B_y%*@O*RIrCh z3`{*T4`=)VhjvGefhF&XW>wKF-mgZTS z?a*#(v^EGSX}n`J9Frv(nQ1}rNaZkE$BsQ3DRq!(`~80L$ft|`?%|_ee8cgU&wJLB zSX|nYeCfPTJ@)!*cfO)3tEEK6Oc5mM2!A;2`OkY%TKYKkmBzkI|jHdk% z0^UOOyTp6vMIcB4LKB;Ofgqw5R>=5r7JzSTl!t|w5U-ShVDZj1A?y7^am*R7RsHMS zgCBn51*<*bA@`xl7gRd9?yqubUp3Q7X0|TPvSY_qmbYvp&D$s)b=yiygcA6`b(5n0 z#-waMwz@gK@cY-+`+BMLFL=T4t^oP$xgWuyRPINMnRJIs$pciV&kWw5$X{%Y0>L;m|Nb$?9wu6nql&sEX!lzk)>#@VhG{>AT;L*zM(88 zQ@m(NU6&9Vgck%akt!lZy%#ta4?ngkQ8H%7q-jFliDaQP%SqB!G+6+uKvlo8zoYD? zc?7ZB?3|-%D!g?RlQA2sYm7EG7>+CUt`FI>)~7$5U|qN{>CQj%)W<#NKd!s*yZ*JW zeD&}A8NA0fxW)Y+v(Vpr?W0{&J}uGes7{{KY3D4=&9Jz7Vlts=EXJg062M}VkJlm6DQe# zLogiGj7rO}tf;CwVr`v|@?->6RREUCH8fUWokR;qD}^KxD4Aegl<@2~w+G=-(?CQ= z%+6}7sWDs%@DcbTW%M6-AdyiyB1DwRSs$pZplG5u(4hvM#jVf#)2BTDU$6Vs&)rM@ z@!*33-*eUzQS*m(!@eutY<&G#d?U10_^HI$`v62hjCYQ*s!&GaLW6Ar`_=}m4h#0I z4O!`r(TSnkj`Y%QuS2KXrQJ%IpYJidutaJkd#?OBdv~qSUfjaBf4+xbT(OsStHsfW zZDW0XowfB%?s>bDIArSryEn(k&~W|kHLhPBam$;Z#KQb6U;WMnJmNmL=k~WdmD%|v z@=gaKHS2qJ@zd{}&&Ecd!EnOfeQT_&tT7yq3EpF!qnYNbG|o+n=Gc^hh&c8Hq*B9H zZ{{&OuiSIqAGg5jKiB^=g~wfPbs{FuXEyijdABW#ha^VC06e8hbQ~2L9d+;!h$i%y zAUZSCV{U$dPHzE|rp$CRwr@*W+P;mfx0TV#6%04_GU{*gi_7-$x(}Yu%I268k3EdL z-tMMcv-5f`zx*ohb^BwOnOkN!9&^r@FJxFWJmk(ta>P=`@^Y8ir6uadA$1!UG~@mX zgUtpiJ5gpB{4~=;MGSAQB&{S;GCE3Nq@W4z06jxa zCs=GeK^EPQ758~hmL7-LC zt81gkvUKVZg5V|IIgA$5Lt2O-i9*Ejwex7D;-T0E@D3pYTF0pj-jiktO6e)=AQnHm%oHJ`sXY3YT%3Fm$K+DPX_AauwgaiBQfXX> z9Xct+ftR^bQ!}PeSleK0i%RpIbeEoTk4HcAbD#bA`}nhmJ^n`f?*9F=o`?$~>xRJ_ z+L|Z#Iys9Av&=3mMo)~48egLo%0!xtR1({SN!j=dgw$W!+b=H|6z+%Lz3`gNH+|uf zaL9a%tABX#GO-Zg^>6$qanr3z2Ho10P~A!gdot3TsT8M#;AWj|L^&zwuWz!px=w#I zW_`2IX1`BkZJD+@Urh7%+YY<=9lv$`#Xq+H{N9h>X!k6Hz};@Tjho&24#!SbuYZGZ z^?h1dBD%R@CQq4Z<;-tcqBl2BtJfoIcQ9FoG7)kRy%90TB}`xBby?zELseE3lL_91 z=oymxi9=E98EuaqC8jWj5Kvl?WC=UD23|gmgKfq&cgR?QLE%+D&T22Zk<1w2n ztE{hYFr1WZjtcg!Y_KsHqk8k#%2l1~T{@qNC{!i<*&cZ(CrQ$PkmDfLMN-PIHcnhrh4jKq zg=;SO#cub^cU~TDebNj+{`WT?)aM_*<8ZFoZF$#A|5_Y+^w#!FTi<%iTz*S}xQ9{V z_TKxRwVvAA=z(Y)jcur_l43k&&>u1!P8g2Il-7Ns63JibU^id*`m=Aedv^ZMe#FM- z-cGXPCU^AXwRZ+>aJE2(>HEpscu<#;vWYTSX(U2wT;q{0Os6@4vMdO}lVxfAPBk$Y z(D~TGZ@imQr~=m4R837~Jw+YjJHf?gpteoSbU@-Q46B-9)i9~*h_@$ZX*uUeq`^p# z!9}#a6x22_eHEazh(1dREu&~9v2m8G`1|rqNmWXrMU)FmC74tVqq2$9lpxSrH=Vh~ zHy?S+(_g&z%3qcL@^9Zh=x6DJ=X{JuJ@jF``8f}L0#*5@EX`X0Nt#Rx$LS<%+GI0Y zQkNAVXsl&W)LgfBjeYAwRyIczb;JBzi*`Gu-EA{FH^=PUEWMfenAwp?(o{0s^K16* zTA{nJ4cdpYZ%{{4mVooV_)RXk`Z{iY^fFuKbA(FRy*9?=9qxSUF&w#^(dx7*tR(dl z5~G-z@6zdZk*3RKzq+1%yVt48g8pWo)zvjt*Vh?OD!lhp))A%?4LdFD_J=b%=c#Qh zwvs&m@X{fNJh_}q20y;!nj7u={QF%0&lDbKz2QxK;cc&^)t$Y^WOMBUA=oX(MBTnp z5viJ}*p=2Qj(fAzkZ47>)27?)F}tuptJ9&CB`j~9@j! zbAQT?Ll5Onw>*l}ZE)GOo4o(CKjOF@3*7XW!|?TxFaPigc5M{g>1NwGyreRG?dt*s5)w_yV^oSs0GP;)21NAgPV^5N@I*UptBV!VtTVQB}o#rl*j-UhZkW=P?1Qb3Cg=Cc5fkd0wb!)0zgJ@Gow(@9*1R_*hDjozhb7 zGu=EnyxYp?b~?1WZPH|#&7$LVDkWn+z_cS85Z;BbMhKS=$K}P96F;44XFt{0^|et` z*??aiH>D5w82BiV0@*WUnF^_Zya?(z;1))>6NC^)I%{_X=Vv_#YdvK-VbI@TeRZAn zwLYus0|w(U65;nVah}KQ~LO($;Ae;3>Y@H5NBz{3{h@ zRzg6Q=a?*s-`fzSG~R{S1w88o2H5dNYh}OhSBDb@#ZGO;gI2E!eCs` z9~TTKHI)rNM(2O?{x5#-g4e$5{XFllZoH}6|9Ab}Qy)Ws-gsm6%wX%MlME zNx`@*qkph$_B14qmd)g=!=vxc-~Y{xlST5)kG+LRYVs|^SYHRYl;{|D1<^Ja_V;r2YexM1#BHBm5}!{qA^&k+G2^yhY$pG8XvGIV1wxc%1c=bu^pD33xn&24wucG&W{i z*#Jn&=n#*MJE52q4OJbmG*r&T!>WrCGG`q{857CNx`_lw8Q-Zw#)-&*sSL3hrIbX9 zm=NpLNT8B<==KWi@9r|47+}eA;bnxihPki)4TyyR# z=^cBU+f7z?o!!*U@kyQ|iJ`sTx~U{-`tH<jO5&7423^ zH&=AJ88dV9%*@U)HS)B7+#H^XVSK9PH#aeKDRw~^DPstP4d<5)QCWF!@#8M0^hfS+G@36t@d!EngN z+B$2iYm7!?s=A3L{owy|x-kuv7fdC5F8=XSUe}&oc=Tj2_~vW={Xcl%t#0;v*#~dz z_5V!a0r0p-KMY@6{p$<9|K_R~Jt>{0d}}4ijG~(xjMhjQNfL63+Rf9HPP@hO@+@<6 zb7Xm&JkK~}N1G(|OvWQrl2aEWs&c}vb~h%Ahcg(Axa!iKcm$o<8TRfQ(pX298v0|;%D#1m<2q8I z8qZuO<&ecLhi{u>+wu&pETw6pWUH(yyo=rOAS@b5Vp5VUL1$@9rw#!xTj$pK)+dN^ zbK|dG@%G>7{yyeG_u)3D-O>EpCqMSsJ=b0LlBz1VXNjSm>L{Nw3Bh~3wMZQ+_|gg{ zlW0g&TG7-ERb59Ut^hlg5=r70ubmpYc^bLy-XD0mC?!!UW(K56kQj~90v8-wNDPwM zRtqUW5!vQ$zlPW-gwzqmD55DY%~F^a1d+-SB0bD02ohrqjf>aqghD1+lrA+Ef=8)9 zkOplsOcIG{ZmPN6PwU!G{oQW{jN|DLCnnR0gfWWDXtXlXC^yXx@xDO`H8uCT10EG; zBQ9~j?@TC#KtqV;!lrUq4^~P{5^=_{BN)?jm5wi0A=Q+&l_9i285IxrCP7I-E6ZZh zBGn#~wcqaYxwqV6^6#53n|UvPf7jprw(;gyJfB9lTkT@s+uK?^wwt#|jU;bnBzYRU zZCaD&>6ApK(JD@!q?{(x6Q4Xq<2w_-zHVC3Y$#oFUx;WbQ-YaXHh2Y&>aG{3~ znF~RA=Y(@pX&{!SX&Cp1tgo%JZ)Jt`{sx<)no;3w(GZ2_Svget*URh@6Y|mYn$7D@Jr8r{!K6ZvlsGZ zPrP$wRsWRZJHO_OP{!Qn=)$?*NJIbV$*5&xeZr)1Y^+ZhjasJb6Gq(#Yb@)VYgng= zLzI}8KF~Fkgp>W zt$|_DGdoz&_e*BI=Wsc2unZjanSgnln{Ix~&DXx^_4oX=$Meh2dhW;ceE!7ue>2bg z=~wZL?|Rgke*gTJk>gicl^@YHwp*J{*xp=cI-Rnyz0G(!rRlok4WqLT>uhNomyFg! zDQbpNdqt^>MypT>5h#Uejoz|Kx7Mh&wsV^)rzWLjq&Us=KC<+l!3X>h7<$=ogdqPv zt~MUaf!_Oizfync$CrO8O8f8m!5_WT&&{h|@&ZiuWbHP_!_L=~&_BcX{pDfk$x2ro zt%urtP_jsBtwk%OUC;*S1cghM!B0U+_JORJu?`_$Wm!p-BEXKEfn2Hsq89lHD1jV% za+IRp7$@>vRecRvFzUJ}`8kTB3#Ftgy^;XTlJ7vO8z_Acm*sYEgQCwbnjZLGiWihJDaJ9cin`L-9o_TB%iYwB0O@2k)or?&Ei z8=r;mzswkUW)91MF%3CoLK1mdR)(UTNSp^xN|F7eIlFVu`GW;}M{}$-oZ1>OYBd|{ zE!!tfu(7$x+6bKSjK)*C#t;^}6t}^6($Y>&bME{Rzw(@yus%{eW=N4Y`^1tWi8*k%izGP>2 zkKNr}_V@NBFD52Zg(C=;rpl|dI>t=r>b>d4<6n5lXFu-yly1^7O84tRgW-=y)B^xI$<;uHTjuwUD_qgJkYnU&3!eW;g7c3_YAuCW3p8|V} zNHdE}XPS26z;?7ov);jURO$`dVwKQ1LQ1&t1Z5i*^MM!&KJ;JMlv}=8J@^Sf`@6q& z_xQAL{+@sK`F+j{Ud&Z@y<7PFFa3&V|ILeDcyQ~@ANsE$EU)Q9Atg;plIAJ~)`HQ6 zaa-tIgHIV}G&wtb=pfgrOeuJugzym)hs(mje2K9dYa}GEwT8xI8Y`ZWP&77!@)@f| zMi)xK#L8h0kuGVps&Kj@4E0rJN7FRuK7rO^&^LzhXi67bLh$&|W37__z($B+*+(c# znf?HAD8#&ESq>y69Wl^rV@Q-boe``qC{jw2h_1wJ+tg4&qJiQtS{@`;D<~T48p$Xr zMMzcFl2f8o6T4P5G_ayhLA1%n)hT06pyl^M@MyrgmQ<&KMq5Jf$-| zrXg8NBWS?0&8T@MHa+6njmQ7uWBJQhf5|KUuz$d3ebt@%+0~;hK7H}(#fQdse@APS z=#s4>)Tl)G&6IN!Q56sBvsx zGiqCsFQgLaMlIIKB-yszv3yS{@rJCfDdn8V9Agk;tTGf`C(x-77XzqFDv7KRTy)G7 z3@I5@<77|~B&f^}9#Ve4YB#?3DbN4w*Z=vy|9*b)zx~rf2=C0}nLqto05m7R;`WXE zKl(Rz-t>|`O~d@r3m?Be9?TvQyx(3fmV)+-k?Hz6v@O{=AqWAbA5`rfLPmTenDmvWQbNUB= z%zxWQ;d*@Y%l?#%YC9Vqq#q1N`kjd zMZA}~n#W7Zq~MP?6{#1=NsL0G(sk2Y$hCJXHPjVUks_M9MJG!EA5%Ks@TOFoY$cMf zq4(mUOQ~wDB*e9(RD;_T$~N-;WN8?*vuM>;M7tx9C@Ru6iE$gM|0z>)OJhg!Z)l6| z#tcJbKI`TAH55`3GNrbLPAhyc3?UJHCK6GmU~S6~D$$WDdrAR~^lT}4Mox&Ga~~)~ zGLDpUl?EHM?SRNAXQrLysE@=X&y<)tyidnls#K`*ENSp&ec^C9V63jn1a+KDUx2{_ zTC+Y{d&$|m-0KHk^H+a!+YkK4b9nJ<|LL%W-~Y{@W9!Ph+iU;qSx;Xc9(;i@hS6w( ziJIOQf?tZP*f^|dDLJA_LZyh#fkp2*Je+ZHf6ngVf>;zA6Gsjn(>ioX46|L9ENJqG zXcd0hGuMT-YdCY+<)p=Vc5Z$ji^BznM~4_%Vt>Gy^);UQ1@{x*X7P+W#hN>U*%3p3 z6LD`u$>3Te8@+ghZ8y33gSRj{I6`Sf3 z&c0W*#|i3|Bt*k@jsbdhQSRv!fr@su-gcMnh{X z<59z?GlG<>hG0dZbq(!!o$4c^+#JHff9VHh+>Ln;K z%T3<6M$$-wXYd{$<4vX8_ce)o8EJ@e%8( z7~76$MiX2&!Zc1+@Y+z6-oe=CdE6fkOkUqQAMX3eFXqm;jXd|aeu1})9%?>w_Ba2r zQT~Te8E0)}n~QwVR1~wZ?pP)#n9?oK>PB}?BZ_Qtc zDb|Os;4owK_~nwrojvw<580i~%Km(K?U2=Pm2Umd{_C$kd+%p{^;h}+@BH}YI;@WG z`L3_ytuOjxPT&1r1cgrt004jhNklnKQ649kabYWo!&a7mf}ONJYqM zP1~ZHmZCM&af>sW+2M?04c&AN?HXbfPg6G@(Y6gMI!xmn1%14Y>qcXg*39QK3136- z*>zC=0U1Oex5A2`LJ*($6aod;Hgo+_-$< zBR~Bke(itxiZ}3t`A_qz?rD14_7Ajac(PXMYmCa5r6ls5801Hek)dDU`<{{$ITu10 zFiP&Xxky2ra>5uZspiHA5z4G!8bbL?NfX0B3jWwv7h}Q)&oB&>C^&IBmx*P+Bo%1e zuHy0~IeRszDJkNVCWU~i8wixhOKr{g5oHQg#B(pJj z6akwJA&UAyR};L}mZB8Fi(GE$2YeL7uR>(AN*)&ET#0(kD5TN&&Aq4Sh zsKVffilA?*(OWzyAxiqYs_6Ax3Pxo@j1;AbS&?GKT7@%OCh#fatRZI@DqekM#gTI* z`BgC_ypMz^7~r*yjaOcC&-;AG?Kj={uD^Tx`~O+j)^Gg$Pjd8`#4J|szf*Fa8nkVXYk1gvXtUB?hShYQd7y%`tw=j_jxOePIoX9zJ#c1LF^DKZ{8 zHaA9MtWSx?8cu9aIDKZ5-JL_u-F`qnWM2H{TNzU3OCEI>?s?4#F1cir7z%s)0~;G- zF28(}ZZu}-Jr^&`89Z#BTxWB8osN{s~*46?>G3pweHC1+{=(-W>>r+miJi+?LCf#_?$igIQqzD6)HTpV`6_BE*n6wK?JPGaH=Q9&vIyrL!7S zBCZ|Fl!8c6Mv$)cCbrVdt|$tsXN33@?AfJNT@J-H*@t)K7WH z;lbX|EM|v~RupUNM8sAtzCt_~GI18(c&Uwr(MpQl`d`7t7Obw)q$0@Mlrugid?h|u zBdPRCtD4LzEcdoH6fp`)u#^Nvq*TsIVY=GNw9aCRnA93AbSQ1Ku*t0@mrTi##%i3_ zx@`U~UBVXS7^)J2W#V@+cZu#-2KJ;IFJ^lHw{0Me5 zzJKxkA6OOdBSus4yOgY`78%hXYgO}GY6EB`NX%%1GUByS#>%f5hc;9qQ#qEp<@z9t zNhZg+WJ=29#4(<9Sj;#$IAZ^B&fzlr#T@s2n`QZ)T{Yn+e(BCZy55<`w|(X3a`JwU zQn$bPW#5s8#ZOsXrekM0eR7-AC$^ZhEz8-Am^`j)C_xj8Y|+}TrR`e!rRV5yR!uVBwyuFl5yoM_c>PfB?T zDL$SYuE{B>QgN%%3sox(135&N%emyS^n+0H$QZ4T1Ng}5SazF>1i_)oF%>XIUx!Z$ zr7}j#pg^XZZg@|s$nR&rfv+lEs8976XKkCU+)RRYE- z6f&Tq2)SxS6^y~xXHwl25~)&5lG6}tLjWR2 z^uZsac~{;w(L70`9%8~r2)U4p6stanXQm{Xm}@Nofhc)q#uO!BPt0wElrhy8Q7VpFCifuc47Z^=0`_yMg8tuqij{j$fSne~Z_vjw;B zFW5a?kP56%8YbhG@!EvV%{A7hV@&B;+d9o;?F1z*D1IMG;_&?KY;3M`^2&R1@zxvg z$?~eV-^Q!o{BAz?(;mp@J?fscYDP(l&C_QXuWgc-J50t3V>^<&gwf_HF5diZN?cNO z!}ZtgvbVcrv0QR+c*y?l4hMUCEanS2-_^$YWaWH)18+ihR^WUbAO zuYSwBKHj&!JN@`q*?6puhu!O%Z=E0R|DshiO)XYI3em=>YD^YAVb_XYe>xs>^5ixr zPHwZlxsGdEPHc@heWqo7W1D{INu{8}E=PMiXsub_+TdXCBJ*DJ4{y1F3%f_$`>Jg& z>>Tilf4r5)e(F7V@ZG0$t!2D6WkpL3zUN?XkNILCW`n{ozcg}tR{ zITry{6erd@&a8KwSsSxCv8<09Mw3kv8lMvhg))v&V?`IMkcpcyG6~l+HWEgmRH9@< z%!*j)Aq7ZTBnMU-c}P%>vogdRkz|ThQ)y+wN8L(9CanZb$%EL)=6r=mto{v@XeX_; zXkgk}RF;i~Gp6Q>STGu8Ws<9Mrt3PoQ41yGy(cRPxzg)8D7aScPQj|rRmVkXho}T^|75kmve8HWYu6<`8 z|K)qWnryoJwuBc>T6_0#*D~%}Ce!h;L{gewDU7XkGTBB#RlN`5OVUatR9VP2D=!C? zZWn^+@l}-qQN$CMQih@{*hA!|&YC*xv9zPfo4V=rKc#l39;aBp=FZ-td z<7@hfUwk>&?Y8P09(rD1ddb8V#lt(@KQyQOxDtm)l~Oiyl%(ts<(@MPUcx5l3x?&A z7>Amk9T|oJWenO_$<$D#Zcgja2{BshDut3Gqg26I8MaXBMpvzZRil6}Domu5x*@NP zoXjH>F~{nwQm7f%Rgd(_Kkbv17#kWFHfIMS@pA)<% zg@QHEIE{4^X{19UWbyDBrSM)POVS_;sgYHZlm;JU6S5e*8K2wS%DaKov59pK*EA>; zZEHzk$?myZ@V)26i79&HYHq!-V0Z6`58ZT*SH0;y-1lym@-0vKTuz-Bv%axO%Z#-% z_av5<*~Jeq8a0g9Pf^mq-uat|j5u5<-ujm7I5?bfaCpSUor~;V+~M$GcC2zJHJ!ZF z27@|CT@fZx1%>LzYa8G8hU;$sy^r_3?xS^l;yyt?!sFrhy&LUx?d;KobHAU4{&A&b zoH0yBEls6wC?(y!Gx`|UYK&oheVx;%wmE(31d}z9U0rd-I%|`RG8RQ*xmYsVxEx)U z?A>y$Oi=ZRXZ_(TdDWXg#8sD_;qd5)*?h^DeC9pbUNdw~an)s;w2idAO*jFoz+~rJ>jaeCpddzoiisl88t0X{RBo6g9qa% zS_vtt8-cbpeY+4t+=-_Bx8M6&zx>>veAP2}%D?}}*!Yio$UV5~9{1aP?>qnTn-32T zzdwd>!Wo&Ox6Ux_Rt9ugzoQJXD3-miI5~BkU6HEBQ8dvt3Eaqo21FO_jIAbe#LG}* zMQ*L7bJYw-rgesKE34L=BTg&EofDIHDukG6tdp%uktvj~{s?O{B|+P^xW>{rgRa>f zrUp_rBRSvB3Y{xO!3sOqpjS6<_%QRNWSA%vA&NoLwX*65O|JQmK6*kJlZvcW zOeRyN>l-v(OEcM z6S!;|IR( zi@})tFyDQVN#SmzwxM%^x3;ZiG+je$ONhQsg$vnOnzo~Dx?>Q6t(h;>W-g^F*8oBc z#1e2#OQRau_!VliUl{y?e(AAR3c}UatnYim5Sh(p9334}lpz*#OLF6{;qdlXa<@nE zGe7fBL(qP_j$izVAE4Oogqz*^dp3tJY@3G0X}T48O=-!OsNf1?oJbWZl{ld*j$5`U zxfXOe3ldg>@nka^QY8=7=37ET#n-1bIHzf>!!;5j>x`pqTbglq!MgUfxb81HXWp!o zIZ~#H*|fbjrWmc1)xI;T*v{gbcC)El4Kdh`gt>TlO4RrC{5dT(zN;rtTqp&%0tCt1qzu534kP} zmDeLyxu^`E3X&GCs0|1TXT&=aKP<6a*DV4`o<-h!Vic2g7-SpoW0XNhDUh{n`e6HbVeqN`dxFpHW5(B1c4Jf>sv*}43THK)Q&1v9l5I-LieU(p zTJ*lgS@larIrF@%+Df`Sm~kAO90S{-ggq$@Z`R__y)LKlW>U z^$$MfDQR)|OUZ{58G+Np5b#lk7>oJhxIxcJRnla_hrs^PoQsD&x9`uGEj^P-E1yB9 zxa9Pd@z^pM8AhX)oMcn$jAlM$K6uL^$!AWlIifPW_B|IkTm;UZKEcNNI`4e{&G@0` zDW7*g9(eb&j3*ODBg^hlWIUO$z20F>6sf*#82ZQ%HSfFbfZHw{v6#=<+uP^d?H9PX zv&*6%j-TzKq>!y!K@8-wUAqH5)3n`-PoBN>X^W%7i`U+C?&E#C`=}iMs?6mde%$9_ z59jrN_Q&V8FT3KGm$CnplFNz4f-w>(R19@WRBGk|SR;(=`FzH{H8idfpF~!iKW}I* zbo%mzVPfV3gcO%8E4Eq zFiDE8Yj7o^aYPg*K^y`(2b{K0h8n)4FmS?{_>7YK4#`y!`iW?R zQyq#4BGwp%QLBl!WD=xYNV%Yuter!K6y&CEl*X8rtY8)jjjNlk##Fs;!DNLl_2H7B z6az*ke8XHc)+rT~mW_i^7OZS2oHdNPmdRvPLm;Ebx!RVt>#){V--=>9ZUmXD1uHp( z!qN|Qs;MYNqqV`AR(uzliat$ft66Wv?@>yw0$fc>B4x`Ih7binpj;&k#kxs>qO?c` z(G)DjNP7CEku}7aP(@IZNCO&|Pt-`D;r$Htkn9E~r3@AlFAp5Nx-2dATk>A0nHwx*6t2x;sGv^7v*@Vx{z zHnz$DEVgNh0?rpiYASRzl&poSl!6RURY9u&DPpw9DRmAsRwy{R7<3VzSB?=?BI8ku zb(^GASPbc9%QN?Uy*+UW|M?TV=>QlJC^=1(F=s{EqAEUIHj_pdbiH1YM1_jarZyhc zP+f};5GkN+G>QyiN~&0LG41Cl{*TqmQ%`>FK!550L)S9 zqSj&AS$3_l*;r$=R-0O>vC(R3RbFpenv|7}QRIi>%S)svIV{qQ2U3u9`#5+~2-TDw zDIpMjAajRYR2z*^pjD=%h&Ga+Aoy{SPia@TX-F|Go7qH8LQRTZk&O%m{UD#~gh$~d z9X$(bwUk2WJ*q@h$uy0tDd~c$r8X%kMS&sI?P*A%=6Xb;7ma<=8fROSF7z>DwW*$+ z4vfY&RWhp~I&JWwUcwh;wBMN*DZWFyiW4d|G# zA>g85f)_qua=~WBGF0?YhCT)Qm{|CP50ZFoG$>Vl1Tq~uhH+S>(JITp&KOE|h<^(v z^&ZePg^&t9Bz!20j2T9g$#d3Eoqguv#oI0b|Lmp%!0%n;OP=vf4;*^`46W@6-C3-b zhSHis8Ibdw2gxMMTEY}#&(Uno#e*d~N1ltb1&50P*E(9K(W)?NEnR0BO*a{@tj@95bY%Jz2+J9vUt&nK zRuQXuqtv&oD1}y4@>?L+rmrY--i^k;`oU}8ckzq9`J1`+w|?v6eY^Xp9RF$?kKg;f z|H+p>>4~HmUV8o8-t@xg{Wr?2sX$X`w5ktG;en@;7`vA7*r8Oyl>r?WsMz5e#c~D* z2V>4$d2b2@e|RIy{af+;dlRpI z*KM3SwZ)?!au=fCXLj*=nr=kvT1MlRqr-i6E=ER^j@A^M@=QjOYf*|k2xk4j{?VMy zX`ITq&2>yE;515k`d$jzT!Z~e43ffK{TJFAlp5~q!_hM?tY7+_zw;lz^Ze6)^jAKj z^ZkMszmzZkg3nH${#l>($8UPw>uqsL00rob;1(-xcaYbsWi@LCeF!gq)@ab6!0NoQotGUV3dNeu|*~=Q#74% z28^;;WoTMWh%#l{5G&GD9t`!CC0JqYP+AP|#z4~;Ok*V9gUrb4T0|#Q z>rhIJ^EpY8omC_&ON;@9rBJXbP)NAOp>;#ar4nfp;czBXw|>A_!*n{O8I5pF!_Y4< zby6rO*;Y7{CKt(AGOC{zQSmlh#!`UdbQ8dk%n7KL{ z|7GeiPyg9Je)d}aim&^wkN55E|9I@XvuvIJ$Kk=Gb88wqPq?gv__V|=D6HB>JqLPcwGV(tdU5+h*B1ck$aO7Lo`%rO$^6|;}FDymSVsU1HnrWWUf-Y zFbw3FAxFXIrb>rWrV`|WY;M=&Ch zRCxe`3~G+OBL=6HkfCZSw5sI7kRqvWG8c=b1nad8wsoSO$_0%=D|<{FQbo4rvbYYQ z4X$a9!}d~&VuChWd@k8bn>hrm&h&$qz}k`tF^iF+$bB^FQ=)N>)+v;h@4G98)<9KIRhqKJV;hY>J*)Pecy zQ^;Bov%;rB)R~wHNlKn$(xd{KuKlBXJm3MZxZgu8&-qu{bilvj#slE_FL)96IX&(- zPG9<~^WY!reYi_b655lUVHh&bDLUuKTCwa4uBJVgT(PURPN1#fU~j>AW1sC)r%1LT zERV3=l=Vw4W0>#D*PHLM6%IfxxN39E>`2isXRL3W;EO)vZoK7PHxROA`;xn|fBpsr zA5mqX?OM*BJ;`F`nfsQKmd8|y@yL>6CWgYavm7o02g`xcvSlknF^0BlaaQOZ+G%q1 zXzL`w+iDUbaniOSc$xZ)o&Dlm=I`7eU;dNN`On`KpZdeU{E?mSzxZ3qi(i%>d7mr( z{)(%vcptBzo`hF_Dx3WolBm7C%7@)z^_IEKyOetr#-J2gsU5ig??i z@M3b#5mf?BYf-MDRB{bzm5Cz21W7h;(59etqHV@#tL3$;DnZj)q(nyHtRs4lLo@Cy zWC+F4M?=n4*P9}Zv0xI`6s!)AGmRbzLqF7wLEX{}!$4CFn+m4Ykwu_}HmZ+h;V)$P~5{Dsf?6~6STpBOZ&|J`wV z&9eXI_u$T4-BF_I8;6~))eyIY(uR~5fI60-#U$r=I$5Wy`9DhgYUURNY0fe%kOiK6 zCUy15sZvlXqf8_h`J7niXu6Fmf625X$5s~lRfvfYBg5eJJQ??YgXZ_y&-z8S*~b_A zKo$qzuh*NgQs!7vBn^a2@lypD6iW5Pr0Q3wnMG9EPz7tqHd=;5jaD>DHnCQlD!sC` zGWb*pS*mKp6D7#~(g8UHinHWk@JdmNL&rk$gWNN%(6*FbHJ9@J6eR6CdQb3@ z1TH~W z1{N_MgCGRs47o&%wqoK|no<&4$yV6sh*FW!q=AXW4-46BrYvBPl1U|szavU+#?lYO zefe5B$u?c+d5WxMlif59r-goHY++gkn%2tJx6$Z9OzqvMkzhV?W#jt=x)@A}q+Do? z#<_-NOcbLCF=Lct*2_?^b&d_^I9f&y=1W2^O-&y%bc*6m@M$2a zLfdLuNtY!Cd1g9WP*vUy8fWU3GeIoXZ=jL{+9e6@EjbaRn9|#}eee2-lRx@^M?L0+ zH@*Ko;ZOejrT?PO&#yh>X)Ly{wBwuK`7I+|o~W%M=~WWCCdEj~8QVgRJ?>a;t%)%+ z6xoGkZN+2hENkoQOeT)WNHcP>(QkDkEzW~=#GHBm_1E!+H{Z-Pm!IIOje(QnF(0~q z1aZmD=l0m2-O4Su-o}F;a6h&tild`_KJ5|r;|ZT}e-=l(EN+|=RO0cU{RlqiQTIm8 zZ^Gve)r|qgjW_Oa;o>37#e#$VLoQz2Wq0>bLJyNnjANCOTP>0+rNI>0Y&j*rj&bi9 zZ><03wXgoW;rdzpc;D_ms>lBg_xq3N_}tI8%NuY)?43Ic9x4rEQHArOikfZX{ zsN`JrpGH{mMW5RX_M4l(f8L*O|HYCguH8A8#M8d~3uxN5d;fdh^;!FSdp|OpFCOYc za+;M9Qq$UMJl8@XNkuTgt+QAQf;ZHRoff@r5qaFG6>7{76F#JBP?rz4R*I2x@-?(M zP6kDbi5w;A=+0-cwXvS0llE zMr+w9Xh{Ia)D+p}QbL-HL#eg~2Fd$DQ5f5xfwpTYC6G&^ZCiA)6eP58HKj^P9%!-; zawH{4O6M9esg+El#lNDBOun>sB2&@|XDtcI6fw%7GLWKVSQIJ7vjXFsP@=S<8&4~f zUxLz<5vhZ1EV{APd~St#uZ=W(N^(i4YktWc5Smm@;wuR;5C%`*_rle$%|WS#@qq{`LqXc7>8c}y=cKrO(do$A->hbdrA?#zO$5=YLjO$ zP5Tzc+s|-o+kczG5I_4%KYB^-AIb5o|MCpfbZg3O@A+*LmQR)>aUrawB0*ukA0)sk zh#bVUBatpzP17_YJ*m`5*c#a|r5FjZ(6pUs-a`Uoi7^wyQa*2XBtQ!ir7S2V z(V3Q}>%=FLBFlbGRQ9EFd+O`Pd3NqwPyRao+dnZMOMm%$|Cy4_Lsbqxr*r&trH>V$ z_>jbOU02hq^ld6pFx4NcFg9tdtFhneDle4cca_Es2wkYiuf-@^z#J1wNuZ{#6g@OF zPO#G2T5RiRn-*&tl$9ZhQL;_eMni@eC6p~i$r2EVQN|CR&@b?VC#QHE2pHo)NHVQm z4HM)VQL;(Pg%~|n6=LWme?wVxZDtZ2hv{i@-Gr@hT7RZPuMKD%EC`DOR zj+41_DKr%)n{x$3)Bxl_8^7Vj`*NC&?7bz(*3~7`fm^j0p|nsXJ7;LBxjp2}a!914s!vcF zjiLm3Kg5KO5vA;~wzmHA&CQJ;`QB&!=|8^yFaMPjFCs& z_fo#%bMDV2S6;<GGRD_`&DJVfpEuGgu9c(KuH#9RNd}blaL}K5E(6ny|HflCA9% zY;3O6whbHWnp3A*rfcg$h)GMv8<&!oJ4Am-)SL8P)9ORk*Cd}~7zQR=SE5wHA6zfm zMLWe0OAhvS8NA#GFI+eP+tO~`jef`shv%3dTx56eke%HnN3+1ZPaG{pr@FN^=FH}l zvnSR$xxGf)Sec4cv$B{QwP;559dr?0?@S>i4vv;@I7;s8CXV;~_qRo^d(&UvIp_T| z9ww%C#Z~8SzWKWj4i3L5#;`7vC7FP=R-`l1j3}HHQ)_cvAZuFd>Yo=`g5Y{}B~^)} z!J0~1D&mPSTGQA&C|IdZ=e3si7{DU`Rw*#}Kmi&nN%zJ$h7ia~9tN#*G)*JYf+R(S z(qg3T8i&=I;5|xHk(t8hFGZeaIcE`fYQgB0T(Pco!;n#+MSE|oCFg{;g~nN0)6g$_ zQqE{Is+k?a^S4%-qtyn+*#^N9OD=~}vhlIjp_P-#zS3ydQk1IHuST{A8bBEE#>n^Q zjFWr4Y$&WLuz&x`TREbj7F2^&sz!+=vStr}cXOVIgTHS4p5? zTyxGfhr^(iA+D}t6d@(i%NA)+Wx6WJT+yS;gSpge$2eQJeHmk=a04pYrbrQ1^G_z@ zG2N)cSuGMYrNwh(o13ui=e*r`w!QWZd-1OK=X0L?tsn1OoE*PYc)|GRIMAETc7MaS zu-N}8O_{_H|++lqS) zWFPU%o)jb9$R5ihtg%(kuB4c84bC>CC}!lG0;BOHnr{5dZsX(+=V7t~Yp8G^{pR#L)Jb^2;`U2;cQovC>1T(4(S zk>2vYtnk})t6BulT`J8=p)l5nM6jlTyT;X_gF+b%TGKRQY8P5g^;KAhs=lb@Y{p`K z#Ly?a4}_2?RKs%-fXaNfWVu{YG~$=5nHPTWl{O^QMAx=7jV1da_l?G)YT;i>sv&;) zI1`DKF%;UyNb{mJ+8Blq1)dQzMai%s6^nKXRpm#OOD;0ABu4uX13m2#So%ocuVkKz z-ls|_6rY+Zf;}m#u;REPJIXYC(GM(qtp1Ejzp6tbjgYZQsveh=8Dgf7i6O{#rru!D zTGNM2C|WW^kO7+v4iqsLhE>>Zu0g4*+?ASBVjFufnXdoRC0AbaYs>lk+=njNvbv&+Q`7dhBJV7}~OCF`wHLP?aDqPyhUY@=kr*ED~7_UgO* z8*A;kSHAuuw%vZhAOEVQR;%N$U-?FWo9};@%YS*%hXTl zkA5j}k-Au%uH=@<7)nFe7N%<(te?II$sZBtx1pUwsgrDNUrIYYi9fi3`NaBp z%Hl?18gSh@O*`gr=O%JAjHesS&YffLV99dOj5L`37Dj3WHZm^(YhXHQ@FBApB8^iF z35FoFiy{GUVrZ^pr!Qi?H3ihgvFz$YzXMm`r7Xj*5`WUx|1 z8f3GeicIZP5q7yX8m*-{Sf$;nYLx4oFz%g|3>Rtwt5&8K^#*Gi*2k9VSc+TOXk>f< zpYC|LD5G(9DtQ`J;$S9b18rwWNqF+Q2&qZsQjLnxHV#PWBnH>nB7#bcCo+*P(c{Ac9~U&OV=|ti$d&3M z)S;$rfk1x(tPM16i#Af6`#9j*F+-LhDytoF5$lG<9-C)H=2k->W5z0( z*u^B?6$PXsRG(4{+7vRXP(3Y%oMj{$DV1a+lVYPXDhHrom4hntQY37nD3$1il4e|* zK~=Xh5VL5iDTT&4NJzjPg8KBm$BbIEt^tP5fGSrQ(oc@c?2FZd9{k(b(aXw`3;+G4 zZ{X=qeE7%h9{0IF_9wjLyB^6)p8M68kJ9dUsLp*$RBIDOLHPj_GddSk$~DJDvT1C6 zuS}horle>`E$zk{u5B3lAOM35M~Abz)sy$er!3pSc7$tSv^lPME4p$IM1LNKQ1fLX zO)H+D#%)wmU7_t7TPWWa73)WC`xk!qfBek5P83zX_-p@V)73lcC@*^^uaEcLXiEPm z75z7nd2BT8Mox`1TUn$&2`&DBQYcCbr7BC&nd@{j=RzupW1<@MKER58C4Wvr?KD~! z0cd1!rUr!CTtN#3Z9o}S8yNY1Q}!5TNCBZa^|Enw&VmuDp*FqzUX)3*E{R5GQVAlJ zDp5QY&d@qd=`_x?cwf=JDGQ3WA7rCp9YiG?g*ssHK9GaZx_t12+E_Se@xyS8P8U5PJ!qD^kv0Q#WVFND29#`KLUN=cverc}`CCJXwc%03 zl<9q9;i30cl2{5c3Gl*u$Or@@A@B?;FvMs@&I*gckmTPtFlx1IH?$!aA$m2|5{nXI zTrQQ=C`7s;Btg?Qk~?LKB5Fwqk7QbHbJLC9G+AH!>AT0vFHPfJ`hsIIaB|> z0A5y9LnutvPchw+YxcIiIZL0}np!rtw>i7EjcIP@+7I5!UGH)i9(Lcmb2M9W*~v{( zUgPq++?&0F9ZK27T1)F3-e+R+OxH$)n3#^&8Ldyb?n4I*gJ6bdi=N&68GDBd`XSWZ z7I|-nDDR`yvQ?BE-$E=>XIg9a)<@%Ce$AWT@vjqM^}jhjDH;!eM}Nxwn5?b;^@kRV z7y0Gl-y~b94}jI8A1{R(GH6K2qg6(yo-!1MnI^Uw)j2XVHCSg zzxN@+{30s#Xl>{w8z|GW>;tof#%G0VHA^44cyJ_1)?_+o=-Q?R1j!9aw}$aV616F@ zVhaV~R+6RUtXyNh+?%~?-`|}6>7V(r|HuFP|L_ZU+PQz%O&9s*fBVqkfBw-6pZ~>= z{nR(?Up)U!^P|JBABN#_U3I<{^&mR?dgG@g5)gbL7tu1842{Hg)5nZTMT*6ch#4AP zdG<=o%o?FbkrJr{Ts3SP(}33`W5Fh3gu{?&T`R?@78J0xhMa0fff2TRV;caWA30M4 zoD!)P*#VJ}6a`oz(i8=mgpXdZyIK>DWl`l0DTERXqSlHM8+3*cmo!a-wT`Z8@?{%j zXhN!d@C?e3v1n%*ugg?1$5=NHhE^9zOE*@UP81~;Y?7jA9+r>?a@U~?^frK2@(|M& zr5$A1hO~`=){9ip6hXBbjn3tG@(N;#)m92ox&3BRD)=E$iV+mB)libiqDnrV_{tS3 zp-R@&X{3%g;~+Z}2CbY-X>*|zF{oz>7=x<$EV^paWosshzNQEb#;JnV;`uOI9@a{W zBry#GL32D!hE))y9vP!POX>8fsNt1wd(CrR`oQUj{_b@zdET|Vd&L`T%IAIgSAFzv zYE5DG>i@{w-gb!^ws+3H?oYn<3pd*MZ$h6xEfsCbXhifP=g2-#$mFyn2hiZ ztB2hEcB3?xzj5%N?bPyfjY^N#RLhZG+7ePCcXq?6tP5xdM~J- z2=OZ<@j1mRK{8tI6FDVHP8e61OeS=r5n8*d?KfC0lq<=rfDmiwpdh5MCRLw86_b}h z2n|XN6cR=mV#>Js9;q6{IqDqF6jGD`)(B|Z!ot(lI-a*lK%Q}EFfa}+>Er5Y)c(CxIU+i-}XCq!R!Jd&il zrzioSG1kqH44hKRm0FjNF|aYoMF05rYSWR)WLt=~P(+1`&Ki>xZqT&GVvVUXI!Ws8 zn@sBqjWbxCfL?q#&H=L38)BwpMe7`;X!`0`(MHp?inVcz4*`)Hg$n*C_^2^J*I2aG z%$K1yCDO?DK@9T;y=Mqf^2nTo#fIeT_8P2GG}XVTv@|0tz=zf1%}G&m!Bi=fny0ch zYA=kZ)90+8y5yIi|B}DI;lKRkkMXQ$|KUgUnfl>>_szUtbyW()QXmz%yFrW9_wsS--g>wfz@4KzX-*gl2x&Btjk-MI5xb?Q1dHEaO$8rdK$>Sc% zblUOucg{Kgwj1f%5f6F5J-O=2%du+7>8tNS(>O};oWJP?_V;?ZUVUGsi3>syIbv2) zD`;DAin>u39JXj$L1nokn5KEbsk4{9Y&4njmiJ!&F}c@$ypK-`snzOuz&-Cm&iRoS z&Yl0CIfbi@2BQ?6l@J$I3e(myopeH!ND-$C>uV!UoY-P*eS@>7PqMweP20AtpIl>o zO>^e#X_U$=k9J6*XEbgh2a4{{ldFJCtHtc5@(i6841*^yB`kK>xo|tnvQ3Nw<2KQZ zxAFbJ?#0{L+h3rYby7;~@0{nt&XQTLm=B45=~?y(?>$@7j>}JPaoOo@E;+fuq?L7W z&PueRA<*}O@W`viRPeL9CY&om;ru~gzUzm7=THCWnNRz2e&TmuaHqcqU;PzdLen(O z2j2OQ58B(Ceb3Qs_Jk1phEa;vSsEwB-O2!0RKYoOTyXo47(zrVMdM^$yCNlZO+(|H zB(G{IT{TKlxmBr{#e%aiatWgit!Z#puz|xcFm4^rN$x{q4eM*uD&NUyG>wx0LhBrE z)Q}O!Wx`dhhA|aQu6cOzUfY1XPGWtPI&m@gb8RYW{(waj(1`N*VF$RH>RZ znsy{%Z7B=6YPD@hF^i`|YhoHO*5O=>u?CO_kI}Y%FXb4w8-0MPIj%q+@n~qt7wMM+ zlgWhX`Wk`Ea?zu)5;~`E7UGHSF<{l6`U?eF-)auQO{LPM_#2!``Fh2GAA3alxQ1= zZ5oVoG_9+dE{*sljKwzMe=)9&%II5C(eFl&U)8w$sv(q%M`?P?L)YIoGY|gmJM}a4 z=P&*i6a9S7QBBT8YeF3GLnIeXOuk0C zD|#Pknuc!F33?RCd@;tNjjqx?!SSj(S#@ot^!S0~Gt2n`trTr58`ojkQ*xjgHO!9| zB&Eo%!I*~8c*1ltqG>z2(fByGr)e9sZH7|R`;)eRsjPd^Jlprbb$|Qfx(nqSpYo(T z^<4hL%m0E>%4pp#&rX_h+9&-8mHeYM`Mx=qYjR3msu>hH27KR>b7nbTvRw8o=0ZqH zNs3G7oHQ0$wyv@XkOE(!pqkE2m3%s@rFhoz`b+gMsG_T^M;|9o=jy{yN<+=D$vHue z)mNfxl6lPss6?`oWgud$ftqgsg+P*z5Dd$Y>+$#t`liBUHFF$+~H=1kUwYznc<#8D!2ncU`;@&{0;tZ2Mu zeAF@y4I|SEE`8iF?naE-uKGe1UDF70QK@5@?cfEyjxvOlS@b=70W>}X(Ff3Y_b^OAZ+l$zkNIJc<+Z8cP&?3dWzX>&V}>mdD5e=rt3z$;kpG^ zUb4xV^@g{kyfZ8jfr!HwdsiUjSWtpJjt1p z+pMoo*w~zM%{3=Du{|P&o*ad5zP3KbbrVz$j5jU;5`K9=w|1K0@K*M2zll^}f0nuB z=3BAWaQf;`A@zIY#Vt(Mro>Q~4~k`8BMvk6ZoiS<7kV~1ob~KpIM3{Gj*oD-@TdZ3 zwx(QmYMm?3Zga`WEhghmxbH#8L2?ds4f(S2fGCQP3&96`pD0Sdi?yw%s(${D-}4)P z@=yJD4MNHZEM zuB@q{k0oWArUhLXH4U~Mp>2b)4c%yjwT`AWLT+k1w3VM*+Z3f)qU^17Xv1CVh5V z)CJ$3QDJzHjq`gK!^297pRNjLN=|x}PY{zNkN1n77z2y>0mE{D3@M38wP`yHGPEc~ ziYQw{?n0G7q}64BOtB>#(}?+;j9(sNT|?WAWE%!z#y6T25+QmT3!_fFQMPfU5J?G? z?J&(4*R_~NE-vScP~5bDAzafIjNQf9Hyi6-y9oLn(R6Ry=+aFGMj)l&Zu&k%g36Y{i%`*3h;x2*@!X zhYi+98i2aRlT_=p(&N|7um(yBvK>*E>T*PQ{%7h>rm=Ghe`XGi}h^a`%M~s3KPZ=Ku zVvO{ICxlQNlR|3^Ar3@UDOXVf4C~|q8H{*A;I7Q|^<`#*6XU^6AYdsmW0gmjIo)_0HMyMqdE}P!hfEqp9_Bpz1+V1Ba~F90L$Bg~ zcfSNZ-sYX}y`J~qxXb02UBZ3ub_Fke<(t?&objz+@ks7=IybaoF?bO`#*4= z_q_Ku=JN%+yL;Sr`vng7WwXWJz z|Kw^s0PcOoSx%fjeZ_4z-}L+aV)2+#m?58joqm+vq%}#3jRgkH#sCb=b*CtTD{?&vV;t7rC&r&!4>P zTHbyA4wsxh#b*`^HLzGL@S%`Z!(ujL|6oDJ;iF=IZ zNzyyphErQpE<3Zqr6)Hyy|rH1`>9UiG{FZ-%4laX+G4B|3}j9+4Ni$@+>7?2`F2gY z^}BxRIUlw6;#;5oR382rk5$k8^`F1w;Na*f^TWfZhA`YqgHu{zv>*e!#^H>nv5Kyh z#BHerWJ{pb9fTMeDoU}rij^3Aq~wB85=10xcgccioD>J0v9z`LQ5q?;V&NimR8BIJm-nb0I+lgXXr$dpo0y~6YwyD;dWv02xE zM45&vr3Fc8L=It%XrrY;(R6KFr4r^?H?5HT31em2k`fp*qOIxnrG_k00F%*Hpy-ZUxl5%e3#8sblhi0o>zwvKa_ac7xKCfJ%|Vj3r}S81GU zv5l=812Y(7_l;4zq_h_jw`65*Ek)gGw7O~1auc$n5-(Op&-!wCN}N(9ogt@9;&4){ zc&X9qOo;hZiE*=(V1oB`V=0DuKSca;!7wZt7Ck8!j*bpl9QA|{Q93j99^16I*3tJ% zaw)8DZeq1&Ih)l0L4zL#{9;MTg{EzYDONd+V^|zvV~_4e1Z@Q!nZdSW*4L*p%{PVC zwK&&cofN#ouz(_RyfhfJE?B2=O-DEGXh$u!Y0yR*I@5?WP$^YV`oJ~K1*P@*n7J)x zb*t8PsjRut8gsbTs_T^2SsS;KKB@VTuLvn#L5`O=l`pX>U#e9(9QAQ*y}za>l%*>+S@bk4E`$@T9VUAHOP;GC0USQ+^o+lnpMdd2e<&qGecRiT?} z<63$XX#iJE2a z>tIMcKa1XzimsU+l4rG8ZH($|8@>NM5kG1LLIKWV^WUi(D znxb^A`_S6@=JUqWjo-b?z3+3~-o=agPyX_6@3d$8ul?{hfKq3hyjI-$5%larQEzU!wDZ(Rd=ww`!U9nVWCAh2MG6TY2wI`<&gLu)Wq2Q(<#!lY3or z8s87xzPsT4H(lT{55G50eCz`_apn>>CNSzo+IW=9JS4h}hg;Q|*g?$9q6 zEM|*3%#vm!R0(88HcTOvdXEvc;eB){_o@zPiQ4(u{u|lLQhk*x{_p_b76fv;?#*P);2acapD9U>uZe1 z4R^oG2~MmVCex0M?K3P6ZXx>x&1e&?A|)s@c|7p0Yn1*RDMc2GfeQ1tlFE~0pWdCr%-p(HT`#lG9FE^}^Sf6xU zc5;KuPi}B>bB*yxLXwm=Vm1ph(sEWLJJyP9M;V#mC+|_YL?6p9o?EzQZn!Xf_b>gy zNA*3CSq*&FBOZ9oh1<`4wI7DBi{9VOiO&*e1bys`V$@o!4D7{7XY?JfLuW>vn2d*@ zYLDFjQt&<=|vDu@vFzHK`*GUMU&`@N`YHH?%5=3vY74w-z>+b|<+4oVh zGK`fxmQskd%X6-ghi|2L6r_N)jjLK^naEiUIeNLSjmEWBh&@@MtC2Mp$hoI!#2l-P zMH`2%*vr;Bbdfv}VqB!s-ro_elz-8|$>TAqS6hGSSU3h&RTWN;#V2eWYm|CpOnvroz!; ziHe@4Yssz=--nN=lq8|Iag0Y}pb(OmDKG`HVl-Z(8%;&p*fz9Xi)|gQby(Nn8lhmB z+Kd{bpD(QC>Mho^v zwD_46JUJz~A+pM*(S%B5Q(EMoE2p}4j*MkULe3G>x+PU4Z@*a54-2%)vXzYqt>yYH zMPyt~Ln)D_5(J%*&y)-nq?yX4h)^ph&>RPYip&>MNTlE?u}~5WaS(4uktWuANejj%OyMG*5}$^m95*DHLe_=S74oEzr+!Ttg{)QEWkm?+(8x9FQ z6`52)03nsoE$>YR5={0Sr}oB7UBkF*j=i9!ZkL*-m5oA3b<3M6O6Zs=RG&$KtQ1)( ze1fG<6l!*ek~?)wkysQ%ToJ+KI$V1BAx)+_ZjM)yDW4OiN+2Z#UJUa?h%AS?eO9V8 zZF@`8G=H#ta{CXS@C9G=me;-RAM#6H@egLCO#O8$76sDbGW5cnvwZ{6| zgz4BanU0ws9xyDITz2^>v{vk#-{~%Qe1Lbn>o(4vKVY#~uyb*Tix+p; z+uLWc6e-~`Ij#DMlmeq5zrZSv`j}WUFWc-`7oJQqOn>y>&6fgl$7Y4!a9-e7^4VYrhhSH zbP=Lc%u%Lw(KC2y80TTh?)rlHx~G_eG6)jVA>_1Gw^fa1r2r9B1QwuX8nZCirNKsx z$-4SHRF$1o`i-g+Ov>@^Ib(3P!PeKJj7&jS1@Fq3?p%X&t`=HS_$v*zZ84_7*j3Xb zQYT4%7b*?fRjGqv2wobz748^Gg3mGtMj1uSp@v5qlokXqB@#od`e>Q_YVD3q=E1KD z0|X1H6s}W}ZDKS=LyA>;Rlx-4gjPl#_$3lj%?Me{Nh#5F6Y(>J9v@^A&XfGE{Y5vhQ)A9|0*?CVdw|K5Q)RU{P2JnB<=lp<3ZK07b%pj(OBC^ zK@&Zt6lRAwRBxuQLj=6n@X%|yN0%Ul8D}pM)duX|kInOfy;$o-zOVWiMH;%wE-8>=s>$NHBJyiv%1Tlu z=x}SKkqPMz8SfCYnBsFGE0v6~A8NbtpS7d$vmgABhh6)o*S{{j>D?c=)1Tqr`<>s! ze0YQY7<^x4sV(ielYx+O#F*%ZCA<5FT-ZJ0mJ9n_ z*gs+jk&UUNb(-nevAH>6JaUXX&E|T;WYW@DX=42l$SHBR``nkw+7^>(OIt3IhZ#di zES8zQg=M#2L$&KnMk6*Rf|Fkyo+G6|J8Bs>iq09dYsDXF9XH&3#Em!Zu~_!(?d@^v zx%2Go>@u4P$tu+AJw(||kynjwC5RRzq_I%Olkv|z_|qT#L&iG)KmOpOBWeFcK0YZM z4}kk$w$0YbQ}^Dvc;OGDAMW2+Q}L_zSW~|$)}7Tf#)xLpTGrOrICbh2TPLH)bMv_wvp!*L zL&kD&I78*k_S%@UTWg${c5H2rX{^DbY1&SV#7Q2qr6in_6i`+{VMegBIdem=+Hbkv z^?&}7H{R>1{P53yY`XG?Kkz<0{onqO`rTjtse4_x{rr=B@K0VYm-nO;>$FVktY z$W|vSRIf1$g&7RSD{L^wv@TNv-n8O)>tXaNk$Tl|$c@ufpNy_}U~BIPbIe7fwWE|= zf6bzFrfC|$lJm;kS+_7qpq6c%G!Ia-RK!r3Q>o31#t%J|B!sY>>J~=!|1st|>D6`H zf)uT&V{CCzlJ2gQk#H$xC`F^KND@%4=9x&rmMO%NFh*!CjS;^~PK8(m9c-+kB+-r+ z3@9n4oNX&eK-K@seIQp#T+ZT=YMKUDrB$x6XeGf?-Dp$|>6WHxM9bev{)KBqf?>5t zHA;@8oG?b3zf_s}sTLrT^AZUm5PYPcFD2A1N5N$Kf%$Ak7(~`LEEmL~C*&X`uC|pz z&x`aW`A{i9;whqt-^Mrz#-zR{r4;)4l3}^Tm{PUkEg3lSxoJ?U%BxIM$!}2_8f7V|5Q>oajFPE)$&ucNiiXV?Z6s7M=jzRgWTFJU z)`Gm`TuD>0o}Y*xycoVyzC+5DRwSw_0nO=HJ|%yzO3ZRTM&GVHdn*%k(wWp0QuS-( zae+yps8Y8~Ko;+SvY;D7;2i#Krl-i>wR0wu{- zB}Y)bX7lEj&dV^zy1IRnB$N8Tg{B5TiZ3cxDVV{>B!#IK*{RW{1_V~hU)|^kfhrSg z19LnutnX1u@s8`YCX{OpAtQj4km@}ZQc=DgkKbvn{iCf@Xa4Fw5BijMz5jh5On>u= zSASHV+dujpU&j*<{)WF>`_#v^x&JM#xW*ch7;0qmolB_Um6@0;)W?F>9gBWo=>xa# z>~YKaJ#O1MWWEecM)JJwTElc~Idf{t`sOC%=@hGaEFP^3>)V&nIK{!vMXtR31eaWX zCAOQ0yiqyX4249ZAqK%L>k=i?X?zkpI!X33!t7>L>M^dRo1P$sz^%8O=eir`%x4RZ zj*hr^VTao6u&s2zMmVYuCJaZR^C7kM?#xaNW+)$E2qE zL_0odaJv8h$EEjr0Dt|mSG@b)SDyXlA@@HYL!2~43q}xKL+on7QVK&-QcUH9p7)H~ z8EfMadDM|y;@}{%w!Y2wC7075-bx6OrWuo?C#Rl_qZ^M|E)&Z|;fhPo@|t&@<8R)4 z1NXT7h}(8&>>drg`W?4(=F-c!;`IG!ryDd*lZQP9jtF>ENrYiwKJ)k?;*4V28LYGT zq{!oj-Ge!MM>DLBv}TR9zDS+}1(!1oiimL2bw#VHm}_w-(|w2(KU_6#!%w{D9$$R( zRy_Z%kJWqghPPeI8@}P+l+XFhN4@*f%P;?_4}RbS|6>@!7aScNe07T9fgz`9s>b+I zz`24m5S5~{8d9BV)xuOIgHwfscs(MKI25`@ChG)CU!nPKWmqm1xtK*x)HSwBy&PI) zROP|PN-^nuAk-xFWe&{7fno&Ns#a#Qj7&WXlmSXhWjNMpT)kOU&2a%V_H?JjsyV~ZP7MiRl*0^@@b>! ztNA$Ahuo-XaYo@>!O##wthoY~5Q`M5StfNUL{yQ)++1o1psjMoD*cEd)#-5E=IAQN zvKnWkxJyN2wZmvjj-n%vF=9-ivqrK_VxWXV$wuyKx`+|hTB33kC7V7!^u&IFF<_i% z;ZurLchQofeE(h360?NmDJ{f1nMMfhE2e;74rr}$p`jGPJcnT^&8>CPm}pIyjAi@e z8nhFSl54+yKqO`*GMubg_(O~}bfXa= z4A>fs7i*ZCu}*w66}W&ZHN{$dU=l$lO{Djp5CyARa;hGNAca~fwGht78hL2|fx0yj z2~rlB8&b%o7+K9VQ!T)yfhm~k1t70Taq`ua3U%IH>dZT#vOqCP0#h_{Ymma;s8UhV zhExscsb*ssD*z5vL+eZN^JQ(pp6e~jpAo&lK-~XoMgy)cZSVQz=Gb3Y3y+xMDt*PaT`VEAcOvB1N1jg1en0 z@}&AT+NzT`<&J^PATL$fy2UW{1{w>hk*$YunvLlx)~8$C?WzZJmrEZ=*Ni!_c?oNi zZJcR%&xhW?vtIN=gds=|Q*q-t2o^l)a_n_!HPzT&$U0)OrCyII9?qr;Mgo$nQ6;7|&a9 zWyWMAo6~NxK{MV&nUQ3E_@3e5W;`=mUC6_Xk_#iZMl;G3XNhK$k|V~B(AMIE;>L55 z)3F?u?Cl5zt5)oU|VlzU!f(in5tjM`s%+Xp`MfiL{L$8gzu^PF6Z_?pw9<@PD?R`PwjvK#GbPc*i&1QSI$?8r zn~f7E*xcG;)QvcO<`fV5v`;4t`|RKR7RI9vO_T?ZcAUTMJU896gK-YMekCt`?R$97 zyRYTK`HS3sFfbZ*9L?t3@ix&^bMe(5+r#<*STz;>Ivv=DEw{N=PLr+*PmR}fx zf1DqNZ9rSCWNUN0@i26?aBZw++%$|ELnk^ov?@3w1gR7QU1RYvv0UkM(S*ct5pR%? zDnns1>guG`pmV?(AtKHDo~0MQ`mE{MKfR=MwXjeh_QML+vIK>;QoLkDSANIGQ5`oQ zqW-5&qB%A|ma08gy+RENJ=5r3VF!&(HP8%Q4Xu(;Eh04`#V93&FOj7wl$HsysW~UA zdP%G`C?%4a#>uo=8;#Nhr=%G`>zY=s2thEBRs$ttU4yF4yALvXwnp^PIVGGG%2bG% zQ1eZiP7LbK8j&>Vgw=9wifX!$LpavC=UU(vq@htp!U6lG6nv?O*P!WI*;*v%nvPN% zF;pTZRa2x>HN1}qgM>d8R+?=oib70v!y|GSRWl#SNpYwrCL|ZB3bBq$yCOm4a6&u zx|%SpD|$9(s7PmN3Z!|9L7G>QtBAa5MVnIfJq9WCsSU9h%5x35OBIhxHIqw2qKf#@ zQkJi;v@}q$`W+-dTV8KtYFMfdXsFUD`OsTq2|3C6&-pl)WA%BY%1KcoZLEIvN@Y~X zBsWE_iO{ln@o}h0)`hGkJ0b;XzE<9^7!#=^LL3+dFY*;HTZ^1y^$H0dJOn8!1?OGv zkh2|2qH0)QExeAm8#y1#uyQ3Z<&wz=fFm`DG%n<^pl%t9TvO>nNzzc1BwOK<0bNi= z$xuTV@p34IE}G6xu*Px!`#+LTf9RKTX6s5?Hx^8H3e5XMQi|*y-p=dZ@nZhroqtXo zGI@X;iWJ2W7!>R08d@)S>Bd-!u_OhiwYXx)Au(SDLdhfz4F;zmmZ1*#G&UC*c2Vez z+~0=++BBpj)T**dJSr8zTd8Lnkx(r{#vjwG5Cua~{b5A*FN@J zUcP+EFFk^nzVZzpV?zqurR43h(>i zMc(nAn>jq13&rZz^W1js0<+nSoMOEXSF^ub(bY;tRm>+6(*d94jR(N}&vtBGa^<5I2Yb)UAzs) zl8wy`M&l7zU40psU3r?_bJr2WfYU$;49kIK?C3^9#ffUl`P*;j9q)J-|MSIfgi@f@c@8Y+tU7uUgU$1Z>UE?H#sY;`Sc`nz3QBp)$qZwjk zwiJogatK`PX6&EnWrb{39t9B*pE`LdU-X&Z#JF3>XhR5r8*hCN?|AQP*gZPOyg#I< zT%{xW*m++n#R+P&lDR{g1PJ>Slvk*kf+K}lDs<53q%d_OVzjP)3JK6$c|}$%`s1Sc zn4VS1Oj_Zbs}Bm9vO6tOt+sWN*C9qljVfL}Oj# zN2Y)-w9cZngb|i3KAspeN=fo_A(M&}2VE;(3Fn$ReYVGqPyy14idG3j*4ksDjf`kT zcW(`VuExYjN*an5?f%MpfOro=49A%V;xR~QRZtniFc5-lath^G^5R2cJegvxCB{JT zp{8b2_4Zi(W22`;Yuq^0O~c=SSXqpGJ_W#eo=+Vnh?Fnkz5qZg{Pl+ zv@uv`1WB!>$X!WVqaP@_FrJQRJJEf+x^cEnK9egcA1JiZ^%RPPLsin7(IR~dF&)#F zR#V8FlH|eUm9$4PAdC{prVsvDCKFSYT-E%86a!Q;l^~?$eW4~M%jTjEsY*tnkHbNw z;i&~mQB_hTzK|G%?54GmfIlOfhg8oe$N;Eq98J?yX_H8(N=0L@48<|7SOxNTRs9@> zq^o}@h+j!dmd`4*b#+fENVDKWAcjySex4j;FjDGki!l;bVSQQB)ce6#=})PN;H7Rc z$}y5S<$UZXizSmxl}~krJWk}15U6lPHX5s8P4y_1h?vwrY%7ccRR=iAf@>P4-3FJO zz6%e!=VLf~@)`<-Yc9VJ>(gyg%IqDS<3qPx%embTam)D+u=EG)9-iaoo3E?Gp86go zNP{Bzf>~;|&bN%Hcyp~uSYpx?ttdq^L|7~XJ|@r-C|L?DgP^;uHubY4&m~)p@2=kF zl{WOfWEokj1oe%)zcC6V!>1J~NrAS9DVGm+lgV4PY5rt9>E800PyEVrDW>$ZKm9Ww zv*+?J{@~Yfd+f&dZ0G-BV#;^Nl$%_)m)1J^G)dA$I{{Y^ZJ}Fb%kn-`QUVON}*N8D8VV>vtE;%(P4>J)3!24ym8 z5jnLv!IVPso|FRpu%y?SMXTsWj$%88oXDDE?Bjen7YdQK_ynSlj^q@qvV2oJKX=`& z?bW~W1K;rt@xT7+fB%@jS1Z=_3;*hGkG}8gp7@rpeC#9M@}GbIFMr{}`P)ACVE^c` z!{EPQ@WYuHqH$I>9M&jOQZzLu!&-y0HMv_s(HUPP$j&I4Ry(8X#9{!QKw`gT#;#3= zuy`nFVga8LvEEQq#b6c+$*#5(^&z>cZ!=8V4eo#UPiJ%e^zjPgLHB+Pk9x=#aens( zZo2)w+;aYd+;ZW4T-d)^wjpta(UdQwjAYClHzjr2l7VasWl|_zoq$GZMs!8@nxHXW zQ-^|R6|3w?9`Hs>XrZn?0BbW^i{9M1P&MgwI;s?dG4#DY-T+u9#&N9;QOmTrtATQ> zT$Y>z1|fBk2U5y(jYBDm#!#~O2T~4Io~4N)fXYPGLx`XZT|KdBNHwS}v4~1YS)F5u zEk#+ZZOC36r`C>1B{Q2Xi9m>jqBWUSv2BEhloB=bkk*L#*@uA?Bid=Kl@s0%0c``y zT2e+NR@O>3NeM&)&S?oj^w5^o21yY~G|rHcs`yM*w;Qr`Tcxe50>dy6VyJ;`RUTOs z!ys7>r5a94E+{W3*Wfe0W~Uq-9O9hAwGB|EF(u&|N7pr!XpX}Itrc_f;C*H8S4_3U zXG; zj%2Ga-H5pllDcfHm~FF2FN|wsS{YkZm8BHGx)t73P}{b`7h=q4y$a|PS&XZOW8`ZF%r72H!gOOsYn-KZ`$7b+J7S!!n z(Bic6nw3#lO1;0W%I zlmOKn*xA38b2}emu{>h$@I1HN{{H_Tdw(8oS$f~~p`UkJ!yeCEr>eTDdTyyTT9ReU zvt)xY1Ov8-feR#rIF5rcGlUxlFr$GOf`O34_(CorH(^M~WiZ&rmMu?`CAT%ST0M7F zbq#0U^P1ivf4uMB)i=q_O}Nja7KzsLRCm>>I_K=O*WPRWhVS?D<@V9@9G~B2QC$#( zM<{`7!e+}Hqe|j6Tz7&DNv>tYyBh;C!}{3ygtUvb<7kGLGKP4rng(YTt&N8B)_LmI z({(LYkit2mfn2F*n)l9A*%)f;JWn&PVT->V3oezs(EzosH3%5)xe8+%}Cs22Unau%!)1Bh!0D zf`|F4W47wJST>x`Yg!M(VL@&rWohUal0w#K>j|B~cR4CA5#Awu99r2{;A+ou-r_`$ zstJUkFugG<8?v{(3;7-X5%9`M$;UM24jK;|63@^RXxWqW6bEK{`AjOk>;Gj4^VzSJBD_75 zVZSh%GLz(5QD{Ri&rmWdp5t^I@D?p4xrqieA3TLgllnwOSz{z)v6@)ELC`i0u4`#+ zN87cu&T%?iG9R@F8Db%VK$=KU84kvL=qtXRw?6u%7+o+L?2uy4k*YYCo7av8CqHAT5o`aCHk@>YtJmMkf<9A!+J+(%ovN)oS_ z3n5eHN^JCX`no1F2pNl?eo^ARi|<1d@zx<|vOMO7gkT84Vq~29OA$w+t;_Jz;H1Xm zkUFKs25_yTX*$41jkZvc4%K;-DbPA6$Y|!w^9-#7b+w|GDM*D~k(D%c3sP*f?88Bi zERUF8=er~Y(jY?2_(-Pl5Cp;~j46^7CTekXLOFY9shbW=)a*z6vQG^BC>_#DM+uCL z&r_V%+O!Q@r(633$j~W|CQ*k{B^N?GPpzzdgXxec%QxPYdrPHm+{2I?_qm*K-_v7PA^_Z8HLd~oo%>#^fIsA`7Fz7O4HUX zRuh)>l-c}@Wj({X6t3$@*U<;pOxHo^V04kOy(}p*Lt#ujcS_Ov$Q%nHR+%w~bv^x# z+YjdgLB#BauCsJ0P2Hwur0!z#1~F%2ElUFtv@r{7-K$EeS4P|0Ki9SGN5)&@+wc6q zR~>uryC3>5|JIv!-~CVigKyyvUiydp)%)N4>j$~~t15(tgcKA-9)SbiA+^Q}x$y#d z8@HfJYP|PcOlBNiOu2J99VFQGjby^5_+ZPV6R8Nml1$ozsG2hGaeWumcgLM z`06_-i;`w}#9;SvY*(SX`{dz*u1gJ$Q0Tl*X9M+W74K0Y&~?!ZSPsV&gRO{dHziHo zaOd_pCnrmmizTz^l#`Pa&d<+TEfz8G(us(74nAFzBql*es=PAyz4G2Dadu4m}?cuKe>tN z#F3jG*Z#&BMxz1yd%GN5+F^TVi(XlB`O=6-9($OsU2t^!CCXee+Pg|Mxx?A{jDP;q zpP`>?9=*EFqLuvIXU|wpXM}pfGcTWV_q^izr4hgW1J~IdY94*dTNv+*G17v}xj4DY z+3^X>RZTWHV702aclU(zNsRy+Cz#JxENaVq+0rWvd)q_yMgz8b1;d_31`Dpkxu{2P zecY^QWk4IUvWJ(7AY=Z87T|??u2bc|TOZ%~xo`hRzrdG$0VQsJ+c$nCANd_W!w3KV zukFvzj(^4J-8(iD7`Fc_NjyF%Kp)C9CZe3<~bcJ3qq%3+2 zdfQw*e4NYsk1!Z)Q)DH%DRJI1U!8L?KjM`;pXTP>&$6f{IBR+R&P!aJoJGlrh?Fdn z4=LCrO-tSfY@Awp8w;yA(M!Q*h`1G5Nkk=(tweXJ8Ifz#y4c`Lm5kF~tz@dP#Kt$G zltQW~N|FjPogrkThsBMGi-^e(la8g*5f!;Mm+Q3`1#~pQ#6nS~AQ2gGokIkLQjRRs zNU6~(B+8k=WEw$8I)3P^OLVSi^i(2le0*my>2X`5u>_pb2&I@@7=#Cf*1rKwyBJ?(HV;*E`pnAwon|1om`bPQ-fia?p?cZK%cXishwQa|8wM>#GL7o}%6yPQjKHFpw3+LJnqg4EPWxxfGT8D@d zy*~&N=9LYKbd8(x#K+-XEZ#SbvLuQ8HuRZ5CYamDwR#wjo5tH2VM>ry&Ntp|(Dj8RW_*_X)S-jqi#|PZF@=msfm$-iQX|7#)n%(hblr#xp+A^6P zbMNFeKK7}9&Z{S%qAdFi``ZLBm@m#*)ie6N5rr8rUtQ344fDlC47E&N1K+Sg=Q|sL zGNKmjo#u>PPL`P{Ar(5wN+TLsCaq2DJ+<{TE`}opVvwK@5ixF^#rjCN>e@)4az4u2 zTI=at#3HwyBQMKeJ~-I>{_TUqUwHB6z1vrA-0;(jb3XOVC*SN_E!+wGoj>zUoE6(& zwj)=6v(UUfgn%+8`ox^4ZYJitRt|v*FYFz-OZkD5+-*)u!?T8sz%?v?BMKjRsO;c|4YVY&ey*C8hg8avhh{^tN-EW_|)^S z(0RCaX_vbf<#><9($X+sF}+}RdXLGh zVlr!~Ei9^z)v9J#7VHd5_QpMSM*{}^lFXP?Jf$gd+p=2L)U5|;XuCiQlx0C?6t-Rh z;it^j;lFQhf9`YnSN}%-!k_uGZ}#c?|2V$;yS{_J^EZBocfI40KeU=p|AI+K+C$1C_^VUZgq&j4=$#an#}aj($0$DEjPdA8>f^2saMj%HGx$`n@qbqeGO^EbAHP zllzp}kizu%^z$F#b1#34`)98(ot;osi;X`d*aul5A>CC&(n%wt#vBn4IyqAnk%8ZU zZv~yy@+`t$m9nR8wf#cLSmyL*ANDs5+Hrl#%rSy!YAyniWw?vbF3r< z4#uEBB@IfLRGGvg-aCszM&mG=5LA>nIUgkvDTob|@T*2+yN-4GxspWslGfx#<7^Z0 z*;*z;F=!FL4$egbuhMjh$=^ATBu?-UsVLGJf-DAfrs=l#A^APn5TOV%1wgtK@Fi%H zgd+G5v5ra+f(CpvSX+sAj<#!}sW?E_wQ)nSM#83o)A^X$V4VOh>4HavC>!tz3)^-s zdUbLW&D4o`pXE8G)GSvO%Xy0=P~i=L0%7k#uIGj4Rlf0TShJYjAAbwkV~d zAwDH?#&FPxRd(1!vvbM6Aqn0RQXY(7bEramet`sc2n1`Xt12}_YdC~V*)R__8DcGy z5fOpwd}=fV>3m18T+G-Au_;L&CIr~F-FPr;qM0EiosF3nQbc`!42tv*zON+FpQ7Yx z&58}{$DjTiGdp5vXRL*yRLnaY2~*Z#Q|dMF!0hc~1GXk}+0^7z`CU5OGG9)(d-e*iAH9T^j;xn49_%5!xosvO>1!>0T2Qyv&7TH90^QFxP1A5s}DcHu=E%$8E#*uG#zevAJsb` z+j=;jOW%@}yGT<|&yRqB?i~<93N^GX!7h+0L#T||WXiL@{2a$e$IRz*&Q8xcK0f8_ z?3`6q$3VxXi&8}wXj6z|%xH?9&~>8^qV5lRf8r~?{wsgrwHIHtKlbyVd9zR77sT;@ zd5_2X_>qr%f^Yg@X@`e5e)!_ft*^6f^C9CE-bW3sAs~W7T2GPbNKWxdHWFHVJ;yCI zZINRYbis3eV%gp^jJA96Le{o)P0h0}-6z+I<*MNmpS#D`y!QsZan0Gqgx0~AzWq_I zY?plWlP^)Vmebnvfu`eNtDs&rSf8{(%M_9qATx?Vo*{flfl4BgJ2H@xs!}YLtFLwS#h+A7aq+ry_936N#;xnn63WbwDeziSnkeD1iNq4= zMRA&ljFY#vb6{=EXRuKR--K8sSr-kt5qlUgjYPBpXX12GCbd^X{UtMB3K-v8|kdLzE}{ol%0z3DbDW*sNgR9|MI;vFM1*GcGDo0?^DxyoZGHLtN5}Fq09}tDrnGXSAfu1=2cPGUAgOK7rbV{e{p(fQDAcv;m5Ksgz}bq$vB4-v3g{gy)S#y9h*s zy^n>akTT6|WZYW>pZrlN_owS(R!EX*dE)1VAX1ir!*w=3=f;5bsi2A5G^HiZdD^za zIiH%6H#ACXc@*o0B>sNgP&n(;Hpg!oh;>%UIv-0Vh1xxR?r_d2AkH zAZi&K4U%?Wi?zQ+Ad=j3J(yD&vH8$|j6Z|X@%`O1+t`(5xmd8Xf5`P~?_huTVIIEr z4)(UMG3f80RP-e+mKU5)?l4=N@|n;5G%vmOahCO*#e9wrfx&nS=RAvMh70jF0>gL}kocy{@XoS;%qH8Ov)iO#8T}XLSidEH;mkJ-CsvWac#l4Gj?oX!7sv1vV zI4CHzM34~7%?ckRLS>w;JohFotEyqLY?yZiUo*PGK{ZG8cj@n5rB?)$om15d>|(~) zFVVey0J@8l=!Fwed)%y!6O2Jcs$N|oq@lC$%4-wu-kT$WrfrQP2J!F z%xl;_xPo&Q8!USVJB&vKPd;{qcfb8HE)U^HfA%>}7A<92@)MtUoja#B-|~&`=f+`9 z(JLA4U8P$M39iMqEw(wr*^V-kl!c~gErkKw%hQBbGnuWZ>PTK%t}05SD2zukz;=L? zWTuA?9Y%vT3Sr{pRvQB$FdFn34NF#a{Vk1O-fM@W|M-vnd*3qqlmF!xR{G|pN1k9c zoBX1@`RZpDv*|;Pi|g+}VbD_0x)viLV(%kfSS6&aOLBvL9{;TnQI?SFfH85}Xo`|h zX4K0XZzXjDof6co#5W4vN;=^XCeoCJ1mWY&84W=JL6GGILP`ex5wi^So(li~fB;EE zK~%*BZClfJ4U2jb70qiohJ`^nVraJb#Ap6FJL5w}gIxywEr$JV-u0HR<4d0Y5N%g8 zn;&y>cAKVM^2+UJc;Wevv0BdQTtm%r(@d=ml#z}DbOPCn_hP3va&&?w@$)0;#fK#2 za)Gv$h(;pnY6xN3Ld%kBdJI*gQO)U5~l)=yl2pPyyQ9O(MI4yi*PY5*tR|ufu!VaN!P9D ztR*;wcTkU3o3!J$jglOZAQTV_h%D1AE5AvU2SJ_*Y8{Z$#!ZhG1YrS*6b`Kd-p8Ao zO43fPpz9pSXwFwc(uNu>1xlsLR|TXARIQ+uil%dft|f=mFn7_2D`{j4~3-Vqxns!KAH`q;6~~oT0HIHCi#G z(FH^2W5Tv~GRb?Q7MqT!J4`Qf8zDPUm%ipzC;Y42Nan(3V$0fN72_`nBKk}OKH+U8 z%Epv)mlD!N^5%#nHMDK_AS^NjtgWz?L>cR%y(DEf_~iHSNjmpN>}p7!j2PCp4wp;s z*G;}a+F)$98le=rn*yJ~x%N8OlIxx#FMYdO=CL-X$=C z$^a3NT5@Us5yrzk9=-adZ1paqM1~ZKUO8qwy2NO(k1;vcwzS=n^XcmxoxH;F`D>h< z-Q@oLS6DO`RErhOs-@}b4d!|}IY#F>!h!G-5j>%ShK8-Ulf|O8b%DkODh)0~9B$i@vN~em zZ0kJAM>625?qUNPVzJuVz-Z9_#MK**-rm|d;Dw`~d9!b)o8$ZbgKuNv~#Zic_@u((nG%k8s3MtVdrFpMvs-|JGsJWO|9A7LsnJif~9sPcWO894OG-V;# z+COBlwMPh^r{0nCp%0b(%*Q^>GoN{ppLynG7ORTT-r(&|%#iH~t#)|V($to^_H^zD zrA{cyf~JX$c4jo4^(h@*AWe=BF@wrvImah8XJ<>gwq-V-b9R2g$@w{x>71%=u*oMz zBI2!oAFmWj z_7^|?$CmT?A661Ar9cTb#z(E!Ss$?oiu!U%k>`xJh8*ngGTz!{Fd9&lhQ}V+=j!!` z=(?8a$!(5LPU(C`XFb6+T)DJ`?^fJ7nzNYAc<#;p!9iE?-9%+pH#cxR^C`O-<7@oLwxLEhSyou&O&I7YpW#hN^Ki zjb%_|9E?k@9c;6|RkA(Wijtx%+pN}A!Y=y6!H-&FPo5c^>u_B|KrTC#|LNEK?hjlN zv+jHU>VNZQpT7Ua@r@sN=kGbX{n`%zN(n`o3HqhMq$1X(&?hBhm|VXs5-u?oABC3W zDFs{-Jsw4oMdr5*EEh8bM444nF<-UJS2cIDB^O19%A?FmrJJ&haR=yJbM5kDT)p%- zcaC4Cu`7HRn9Po`u8FMuCN>^|xRKIIlII1Z{uW!KeTuxK%=-)nyA1jxF7H3a{>}}G ze88Z$jWGqzb)25xW^!>vRn2(r<)7u%ThFj;&#rqxjXQ+fCgxRRrHIcuQ&nyAXfU$uFX049AO?DIH%oGMf8+%FyT% z-AgM)upM}dB>EQO#!lc;x^UB4+7u=zLNtBnMx%8Mn`~`|_YsvH2}p{zvx#Pu0{9@k zege`&RJS7FZEU=R53v~uVI!Z~Y*u`ffjRFsSw2xSE;eXdp8`zX=Gq*;o?oYbubYn4 zEX7d1B+*hSh|ffcl+&^Cu)@c;l8zt@siQQC^)`cO1UGryUMLmc^D=F*qP52(RD2&x zCBQ+y+~TcId^J}NpX9M?U(U|xDn_UD)Rc7)f~T(M%oit|Pw#Mi`UfZ98^$uCTLxm1%X#>EsTe8TR+F)^U1%kIKyvnb;(lL-M(W`1%q- zq=fQ->ry|O8j+o|jNQ7VmmA95P#Q&+8I&<`LLLN7=c(#wHWtW~{QiJCW_<)cNTOU6 zV%oYu;+$jIT2`&4Y8_4M>4FHOot;1Q@lU_>1K<5Uzl*>8gMa`O=d?5A$=+O2y$@z9um<0~KL?T_un z8m%{^$P3QzpD-Br+1?$3P*kgq(e{YJ);6ozgmQd<%Est?!1CfQ^Vx)|hP(G_R;z}K ziz%mPr<|RfGMP-M>zbx>)Rs*H-=y$dpO$CV#cidJM9g+D8vLpEef8J=`Fpo+c0c(G z&%D{E?+fbq;>P2DcKpOgKE{`Q$r? z7QUbQ2hb9ko5a`G^e^KBl0eW=Ha7>+6RkgZGZ)n6$D&A z3(X&RRGi*_#UK3FfAo9)9sB2g@biz^{r`4+?c1-T1pl%qic?!xmvR#)an3nRn%res zB;IHNnS@~>D07LSfsxTv8j=aR%s{IKq@wH<(Hz?aTH7IHOcd_7hFNLj$j=kq z#KfjU+}q@1u!7b`sTNHl zYt<5LByCA2HXD*C)rt*CGzN?03t>aWQb`geQwD?93Kt}fK)crB1HubLXrjbxeS=7e z_f|&qZzCRrx3z7D^RZD7LXc%9rEM zYhzO-HTWi)5j`>eTxo<%0fkPo>f%jpGF@SP0G&2(#1OaGwk63=aIsMiEw-ySGB?{g zcmo1fgxFLCZ_y%7`g5gdouyti(fDd(+PtSltQ?XTZ?rq zS}6*hMSqlUqw!xVqy~p4%MGn<=$0#tGzg{XT)?|P)kYJu)|x!ev9?1Kw;m#Z3;`+J zX5udsYRK9U5xi33eKhP!nWTD6+%m;pT0~R3boi#BURi9{fcGd7C0MbLmDnzRo#59V zkk|+aDXD^oE+)xa>(ZbiO2%yGshgJ0rlK;1KgO@^=@95F2B!t@kbqVX!vA8vLD*~{ zKtxZ8NCtR93h?tAY&0$c01;i7EP67yU|IXQ`Sg zO63ekBMz=yVJ}>!cQoL9azf{qG+i4zBOwS*5Z;gr5|XE5orUBbco)s&DaMJSmJBKr z^PRLxA&+sBX7m7*iOrC-1g#(utvc%n>Afxh=_S^O*qox0(L8Q`o%Cp_DYXaHA(f@n z=Cy|(d+fuHJ^9oZOw;juzw@_oq=$Oz_0RkkwEM%2Gs9Lkl{AOy;hCmV!q^Jv1Gcem{$$TDDqs>>lJKm4cOh?W4twDP>5(C=K_&Qqz^3SM_31! z`yF5Pw#!uW1<$^E&SKT_Yg@xR-+F@^m!GCkH79qE5eRm!JVCbe1h#EpbsH%hSvkOH zO*!62WdnS-!kaCQUp{8GoUvT2IJ=l}em-L|UsBbUx{b|~3UTlgg2%`x&-Nm|PgKI= z2S94`lfA8-zxl#*&vhUF^z(1_>HC5^zSwyDuN>pyfR}#yV|TB<^U?pLnVr8)cz+Nn zS2CrU$1#wU0V8dcTDgFbhlbZ%kLW zc-t}w(sT2rO3(CucK^l;kR|%Ll!ZECua(8E&{ zu~k$5)=We)lL{f?AG=rtrUNW1WFugeD2r!I_5#q%14O@@(1ThXg z~$)*tqDgxHksvx5Q#Pv zp{-IzX^f765eS(j`B{`85Qu;WP4YNou^_Q+oNil(lpS8h;YXJWUm+C;g>%stVVgD; z03IcxC%}3eHU2`7=Se%~1EK8@Au!OAE?7FE$yFFlTdy!C;Cu_gphBPxEyK*?gQ8l_ zSS2unQY9!uQ+LchGIH_krLNQ ze2_RG%W@t;0##MR|YjY(~ zCjPxhG`{slGNPapl`kdBD=A|TXrbu!`@HL&-@w84BV68l3kTazpk$V?!3seHFEq`9 z#p;~-;+X0D9<$|rR@DWo=7Or8GF{wbwV2_3hjTSr>iCkiAhpC~l1vP+T^BckJG)@>At!EM0|ILW z9t~)^P9c5F|1mOwGqUu$TFd%`NM>^p^K`<4>xOf&LG3J5-s6KyMQxzh?>%#P{ozmU z9bSWf_)p*L+bF;H-}+Ym)BpVc$?yO6Z}_HqHT_eaU|;7s#+Zn%)ta)Gqm;m>ZJ$;O z=b~Q)DQSXZ(OAxB4JQ{Bvz0|E&1l%Cmn(+Df~|pJkk$01piqj?bX2R3em{@MX4@fz zz^N>d__haIF7YMdN+Y7W3bSB3(b4)S7 z+K&10i%6Yg9Zb$)KA9qer>-kz^BME`lDcYW+K$fI)eB`JeI(%KPK-63S);8LI7aEJcRHGI{ho8P7BT9(Ti zsT7A-_h_n`4}JMltU@2#EGT=5S6{x*vbHofuv{!TKfhr6(xZ&8U4rT+nb8DO;hHJ5 z9lovU4@U?yq*_i1vWc?RrA0Lz3u`%>%;TshNvG`2kYSNwg^1y40klw9-{GypI)MoB zrmmDE%T2sLI&p0n!k_ueyceFGHt;gk)G5s?1?SKqPt#9Z)41gIXsG0v_^SpG!N}8Ta=n^8pap+uNt@mQ0uT zna+>tY!#D&)i&xVX4YZu;0uE3}c^I{p~5>K<)d zk(nNY-X42f5A)EKcd#|Q!ru0El*)MG;V+M&lkI}(;)tWO7de~UX1+ROSzT~+`U=y< z5v^MhL{zzlRLq2R@H+vo1+IM{rT1y-xc1t61);}gYZIY_$rKwPv;xtBNM?4?#4tqB z#b!l@_)8>H=$fV_Wo#tY&5Ba#+{qLSDFsTUzmqBvx)Oy;MM91AE*W^^*Y#3>jROZE z(!WbzBPO$ZROpaGkg1sRW|MwjC{RRsnDZ4{Wk{!^WYG({Cc_h<2FMztBSlTfweZ~G zyBLh9g~OPLB?ga^B1w@P1VbCvbQuUj;bknE+BW(Gl#n=Tln4l&p-qN1@v+1$QoJNY zk48(T1xD#~y$jTG1;L?>qzP@JcjcR#a+KwHIvb09=L4>@SQp7qmG4jpr1RLiMk$5K zGHh+}-cyz(N`dWa@PWpzkV=w?g02%d7Z5Tw6-`~of?CK#hjY=plnnmsEj2!B&wa`; z5h>#W=?O9g;dSxciKN$F=Ue!M%XBHBSju=FyhxMnwPzyul=HF4V2Hp0l81`qWA-WU zBKSAdzhOgF3U6#c;<_)s$7C#;oep^82^kSro5v77kwv1E(CelqB&>CM!?^%n;HqSr z4(kR2WG*%mP?6Hmm0oMvqxL<|3d*vi9Q3)o{{+`Ay@S2I>x>7NFgj;zbQviXRXt_3 zy1==P+3c9AnIdI+O>{M@>VmqRv8pea&R^$ZdWTgr!MhG?yNLS^8U1p^Xn2WH|1w7R zm@Q5?o8G3b7YL!)8Xs`^@GY#m8F$Y=M^()tsmn)nH9|!xGm_E_Xr0B{HYTFSSwo^& zCEr>yfrli`>n|n!Mv)oFH54kulx)=^REP-jVB%KHT5O0sbDNsNw(~d_Xsn}kF3Jnj z^~a~zqwOrqs-f;Ib!U@rM>KiS`)~f{5B?W+{$}4mfnV`^|1{tA()Tj@j&J>d%+;T3 zWbb;{S>~&nOgY-Ri*id9w|chg2+olghF(#S6^hwn!o2F3EjvyxmYmI2_yEIxMy>+A zTre<>Qg@VEp{WoquF}2C>3l}jSadOFwru(6^LLmvfnWD4zKs50z`y*NU*cn*dWoO> z#g`DKeMVQew5vJwVnV%~ zQdKinbroMnL13MyO}eVMhfIj-M6^m!>`LkE2QS@t;-|B6$cI1rMTyn_+VMq?$N$Fh zO<(`jc-vh%yZ!3l>YD1C;-pf4M;Cui455C!_kbmnpe;aL6U;5-nf8n3htJV9Af-;X(vVM`p0xv{8ztR$C zqmLy{Yaut$L$Hx!*p^%?`dNX_C0VXbr?i{kW^(b$B{6ielev}QJOU7-P5I#v);$sj}Zc^PL=EXQ4Qo|JsP9GwA zGUji1B(Z?*I*$*radY0siMous@inwSBtL-j(QvJmiRrnvi__~wA49l^eGMkIdBH_r z1p?Pa<9(dyxnz`%8yz1~F)HGFL2I!Yx+op}GHn~P9-6j8$#ph|S`Y2XMsQm8CT5g?x6qQP4!2~x#o)^{6Ib!=bL+AYN9An?Eku+Cd@$q*WeO(N3TQjp{N z^PF986&@I|gAZ>ohOdA2n7ZLpIAb7gPs2L=0TL-{Euo6%LlBodbDCr=;+rN4h|R(FELw8I6J?OqkG_Ca{=1~T$5;4 zF?t5o3HI(AM%GZ|iXt=gvN+*4%Aj-xS`&m|Rkh5kmd3@R)+G{}*|V?X&3{_#&f!`Hm)A>RJ*7AKRIWeYDKSM>UQ z-v9QieBiCyOipJ=sW?2`#SFISd`1w8uAb5x>@pZ{gFVA`aP#Jj>8xhCTyl1L%K6y| z=Vxa$bxmiZ=c;Q{1DRxq&HCd$W_9@V_7^7q?=RhW7xMANjmQ7S zv06;I`I%>rcCTLf)3$D&lEFW^mWi|uGA$@{K+1$_wSi6sROjhzh4mt?h>HSYGJK_R zLbJ2yab4UtNNJF|~RufD*iKK+Y)?9;dT#7idxsreOO@m9X(U03QcA4#Xp^CI0UNBOC0L6Mf#4(3L+`FouULWn?S3~t#_m<}b=xePI#UPbqG%vo@;u`G-t zLsFV}zP+!}IL(~gvI%aE(q{3)qGt#ZXBNi zLPAt0S$oCoi3q90av?S>A#Jo$ zwg9XpT@UD6v~6l$9%Pu13QB?4cv2z`_JPMlrfdf`Ur%kuJy9x_^ua1hGld99(L@@M zj^E1?C;jW3fOW<~5FyIDh+&N0#VP&TY^+p3_}J+wof?~TgY+r;gOrbX0j{TXA}-ri$0+Tn2jF?Pn+D2oC6yEoX` zx{}`eJqUrOUD9+*ma8*fJb#77@|32V(sVP2njTjFOxwtrEQlH^khewdB3@H_zt64M`EbA#QN%S`F14m3pcSQ5K3>K}RqI@jaxKKx@!48stRq6ehhk zeIVIy0;75M+r|*2ncl}OylN*n`o$-nvKRU|z0hQ%nj_tsE27@7kew2m=0(oDt zy^T{yP?*J^@t=u=X~7S zrXh?;fviaRg6S3CfAPXEpKU+>wm0?MeW4#;Y&`xqj-UGIr}#DB_#xi*HQ)FPKk>K! z+Fz;W)9>$GS864YA(B=qFX@~|D~U;x4(kK+rlSojS_EEa1Zfy;DFTxF_m}jCJ+drH zd32sL80W0!7aYI#0@-c8Bn1+08*1e#b4A-aY#S$inTF-6;n=&V(G-Ht!?tw{ z`UNI45eXR_!p7grR8(UvQbM5();9RIr6<$}m6U(#+~&XUZ~vihzyB9Mqj>g5-dx(% zQ&)Dv-r?atD0;o`TrHM+t&K#XE(ls5QJ_+K%G^>KP2*a!6ej3B*dSSU4y^;ZjxruC zpbL)9HEfT1Y>&5TniY%1JWXjGx^vJ3hAYjv>2PV<2uacac%4v`xu6XdjbCwT=NfVW zcaEQ@Ygfcii3Q=#))fx-9wsgAN-aEI}5m+ABfp&WI0&ivvWQekUUMz1$u zZ}*TZm)^q3{o7oeAA#!7xxm^bC#NrQa{3Y=(4wT*A2A&5W4o5Bp0hK0jO#bv&Ukkh zqk3!&9>SO&-Z>6-9_Ep&?@fiCqq7aGdctCL%Gt&1+&zAQ7he7-N3Xw5nQgILUSNG2 zHTc%W0x4-_WByt)^vi%3MBHq6Lz6Bb zUx;Slk99=WzgRxCYpCl zXUCkK-{kK7&jE0L5r+y=b<7s`IiJ49Vt#_7-Wbk>5J(ls)R2d-zL#EaL>K0`P@_$t z7>>zH!|~~JRP_ZA8-4`z`#bFKJb@5~RW(5v&!Rdd&wGrRH0_cu%($4I(zQvJ z7!)Qi8H`3;IeZHz_m4O_xl3akqzN=#&0;lUzM3aQ`ueQK2E*H!P96vd9WlCjBN;CZ z03t!%zNInr3&W_F(=QGEq9n^pbbo*q3fo!gx@BnMi@^JJlk8)5Qiysg5Cg;}-}tY}$vEuFWF1{s4wktspv;&rSj z6#YTLX#WO-;ebIeCsPetXGo=KR&$*GU|=BP@C{)A=$_)qztre!*af0nq}lB zBhNA}CN=l&&bW4cJM!P1XL9rk-UYJ3F85Dg~3-_X__tK}(+=`ltdtevxJj<8LKZ6aU1 z$a?IIud+2hB*>PkJLT5hUu1vhah`hXS8;a#4lmvOX{shRsZt0+D%M-K!ug82y})-4 zZw0$s*EyQ}GM{|)vv?QS8egI7T6`_Je&szJ>^+H68YMFFY)EGM49a~D9(jzXANwF5 zc-Oaca&i-+1}LRCy|~G}`!90$=tZW}6RLVn(^afibDE}2+npqrirA0`nBNC==Eiut zZZ2#G&Iv?_1%^m|B_TkT#9%&%TNNKRp2s7SP;bqBYGWT2%3sF)eUv2wQD(?-4xX*OFWQdtv3{FleHJ=c?z2%ASQXg;WfeLG;Urm>u|iapJIK^ zeF#y;B$79YZom{#67~}CiWie!X z>k9qe7Dnf2ZIZ#hrmp9_e&;hBpS;F=ae;FUuig9A#o8L- zMcmM7Ne~@MNOIHX^5N4QUU>o+R!n9ys>KDf)dhaq5gc@F8{Z>AAT?xW$oBRXdgC5l z*WjuJlZ)HwwV0i1GL`LTTpBDR+-Q%SCa_TOa*84i6sZmp}WH zoGtFg&0zAM1rN@$+4e+FT9RO9sj2H=Jk=ClVe)7Yw;@nlz$#jkVcJMGt5++6>u@## z9IOqjAr;O!YU{Axf(MC02_o`3q=s1p5RT4v)Yj7aKz}g&kze=QzvDB%>Dzw`fA>vH zEC0&sSx$U^=dzjq$D=}jyV1FfL6#j#L@F1qiF79)($CpZ_R1vnbf6*$Zs|G})rz`l zDRaZHXUMdo%p|IdA);k}!1&-%dZod*39LF+-HNucfZ*ocDgXUXy^6~F{OS*WDL?TG zpW*e3mX&XL-@D(!xBklaa(PrE7B7)+9U|+yD8FQK?=A<2V06p%5y=6n!Qa zEAHK0(KR(~wO}=yu$WC~t0lf`5h2z9MhZGBaOt`6snL#?zBt72As{pT4~m_=zx$8= z>mT+X{iRR8Y3J@=m*a~bkN+LVZ}`>U!2M7D48zNhe97|s{@?L!^)@X8d8Wt`=}JjO zW;Dh`KY&q@`r%P1lhGdz*xTP@YiEzqXiRU=6@xtl4maTrlLwjQ$+TY^R&VZdk zNnYepSDvQF-p5Uf55$7P+erA4BARZa2)UiK;;*0A`cEnmCV%8F{jE3k9R8}Wco!k~ z-*kHS&VT9Jc32pN(GsIo%q$2YCXYMP6xb)COiaC2Qh>K;>ByA=AITj>kilx6-+e)}G^OFu7q>pRuaX2rinduN=OeCm#BG7Sky=?|hWw;}>yl z+&oAnV<9LUzKaEzK#}hn%29@GYm~}xw6txDZ2J^NAM0yY%OzbEAJ3qFz&oD$CJy!= zXE-=uG`PfYu!k`*r7#fl2-;>v)hw7V&RDG`oSwhV`Q$EV7k4;5y~$*HMCaNxt&Vac z=N?RsgHHq*2QIP_SP#R)dSdNjp`&uKnKrMRm{{qjL4D-ZB8)(2QW|;k99UxIvc_)O zfbcRFiH=w}dZN^8y){W54BiPvGO5X7^Sy+4AaPquaZ+uZzD{aBHvWnAHX+UX1Q2AJ`mQ%N51JuF z%u3iirZ+TWoBIC^KXZI!=|KeXhM>Il_YIrJAC1{QGox67(_@$?f5a) z+X)3uq$WnBLR5z+Pf{tVTcoXogkYmzX&sWs=DB;&NZ`ZfwIfT&hGBEFl?spoze*Vx zG0Gj+rY3~=`G~N23OBzjWQ8Wn3wDNA*cu-)9_}+7?69|ejj|k)=RJn~J%)o_%5s=6 z!!dKFs^`oX$IRwOoSePJ@##yLuQ~xEXNkx0~GfXpn4jNP-gxX^=*y zT$+HAJ+AD(3-2|{=7iQ(2(74_1?RK-@!sVFyF1r;=aat*x}49w`Y~R)^=Veinl5ej zQsfD~jZd#@fh?VG5B-_q(nU_s70vmuW$4AC$EyZ6>SSyp3^Hemkx$#V|eYgbGCN|?CkaN7&_}Qc?oL3%P-w! zI%$~C=A56Ma(r~eWOBh`KBsA7u&WJ$&PP9oOamh4J=R4kS-SKhwfW?5ckj2J+`9Fd zcYNK~@lXH7hu^gG_pi(G#f``Rj^ls$XFtw2{mQT9pZ$vuKlAR#F8{^pa`jj6er&Wy z8_0wtSCU>9-&j(_f+bIdtyU{=4dYzk3ysSaPAg9CUZC|3d;6E@?c88`_tQ)-E;zh= zncwkmeuzt3eYCxx>=z7o_WAHnJi{-&aI{(5{?n#O1vlVJEb+ z3l`H0Ofh8l>Lc_z#q8)sn(7$URbU%j*U@!d)Nv;*`RTl-^MPe!SvDQBt&Z`aq*r7p zEeS&5Y(xi36%cd?VrZKT0=kHK4Zh0`jQsaUhQ+gs^85etANifD-}mR=y!G)zk32!w zG(R#wKmYi$Yd_d|j|vJQG{Gf(zK#SWPo!L>$Y&6N*2Q1dD#R=SAyGPD8b`jWQ@Er+ zc#rSm9^WW7H++Mjrv>vtG^I%&4b$ssn-_6<9$eTML4}SoqnN<0kwH?J9!h%htjCj& zzMsb*eJ{?{w00FGUIx5xnN5#4IiGQQev|fe5w|8LYV3pa+`IP~ylZ&mp|9kvZ~Z#P z!vl`*zRY5EO0Tz#HinCf*9qQ|sWA^d{5G~P?{Yl*Br9=F=Nd$CXasp#;#^I=oI8RDcY>B6K*Cf1H*cn`5* zajDRDu|kRUX7Z|d5gQ&WkUq(B*4vd-{I8`qa{cEw6splcol0I8Hr-a(tcl*x5TwVt zX#!Gvu*O8P`SSx=)`P+9gXUyIX-d!U8=ijNxanc@?bnS25!3yKf4d2~km>IPDda9S zi!v6j>-6d%QbX~EkEc!tk+v0xfY9l~IyHDIJq9N>61UA3Os(Z~X}D5A=qJPW8hEhw z21#O7D+9h0s0>U2CJh%h4T=x=JpPPgb6P^Qy9nry1{8UZUT=s|C0ZNuA|p3_u3dUN z*RH&So$(d&tWRbNw9Zh{;Ju}Db(Fq($6|THs-9AEs?4vpYR5?p~)Tis-dio?>)tLcHtH(x5eL4X%)xK8w{6Z8t;99Bm}l zS*qm)YfNou*Z#J&dh{TCG)5?0rk8`_@XFhG^2x732ua;7B27#I(lec$63%NTM|YVm z&T(A;EjL@rle1fV;&cC$r*3=|Pd)KokdhZ({1lyCZq8%q0?sBw`8pKQgJ}gji;TWh z^o^va1^qlWdFx@Cb)LpHtd=X5O-*Ax)<;<)X;`&x8Z#XSTR4jk4oS?>@y_B9w4FkGUaNE?k!~RUAxWi-zwGblD&Q|f+x5xzD7$! zhl+)_4}_4Rr6QzUi>7tdo#%X3aqn!#{nHs|vlWeV6q%03du@n9 zJ3q#G&1ml`!>u7jf1Bm(oRjkvw@y!RAy8_^?N?splh3`*?kML&@7!Z#Dzecw!~Gj* zoe}C&v}&=|r){Pn8|+bzuh6U}EEWwfJb#yarwdlg6|>0&7sn@@o}9Bmy-N zgm@2;#Or(KBMNy9J5ccgc%GN}_ka9_o1gjBU;ou#Y&!mT9$(ye{2w@e<*)b#f_LR} zpM2)eSIgyhYl%=Q5-e1bE(jn~0+Xru@r2wgOvc`5$ibyu4z65ccYBwzS8{ML=83o7 zpzIG=P46?kILC}1;q1bm3d%B{QSELIhHp3&>|(Z;ZtFL?RY z+kE+3xB0EV=4tvp!(=j}?jY}tX**c1D(>ID!}z634X&Z}c1FduDJg!cAc}9^LvfLn)!X?hU^)xiVDmr@gos5Y0eZcH zJj>~q0|w;?*S0uTT-v+NJKys4^v69W^IJ5{0v9^!dcwv0H74h;qeT{{uV}W0SE;KD zE~a-8-XnCJiK{$d@|^ML5W^6^RMe{_o|bYrqFFR_O-)hkVzwJhUvhHxe5Cl`qvXYd z(S1U2gjO={-C#7l%5c0(+f_{FcaT~puD@bYozS@zqtPL|O;`J!L#P zpx@siH+@Eav!02^kC0&E+w~(}@GuMf|2rWE@D1qhnLd2Dg@Uy}?V6V zQ)@`y&#ev2Z}3Qv%)o2OkOjARu#pieHZQ?~Zv%P+x_r>MtRK@RL?s#SL-O)QA3!Sp z)(m>T-jMJH=^82b;|-5Zu%R@u_7-d!gg0oM*2^1su#u7R{H>?ne!UG@HyGpBso-5_&CNiysSV+Z1e)K*;c*c?gM6mNpCCgDU){sa$V`;wB@x_tSF|{Db!- z>jp2K8>!P{vS1hKF-Q<9ZpMTccq=eFfqYD&pvACBjlswQqYL)7uhGx9aGgWTf_{I< zaInk%&NT-89Y%va@@#-HIeFIGkjvs@SKweWyU%ofpQfENpP$g$Wt5M)j;3A2!dJk2 zam-?VjtfnaR;n1j*fw;ormkl!=QBc(XjM>@CAl%t=&uAW)Rg6r@!%?X(F4&THFRCY za(PCe1?^Z>=Xl%1g4D*Qz;}X74=BnJK3E8nA|K;ypy_4|MteMZ?d#Y+xXQe~!*X?w z^OmZfF`3`xLMVtqSxD{Yb|H@C$v?P(kX#& zz}5j<2UIUMk#w+iX4tKBMn%q`ms4m-rXzta2#qd#G(OPOEz4C+)z}D*kP-Y~LrN^~ z;?`KN!3zSc>u^4xg`n#?tcR)#G@Zq|fXdAMgKH1}Rw?Db{Chw8^KZ(1@%z5}x3Kam zdpMha*FeiZrL-LS;L%Fstw0kWyECA)NqGH8e6yVc6{zZ#*|O#JqjPSbOgTPZvS=E# ziK*nhOmX?rE(e$Pqt_$%%+5}j&sOXoZu9Wd@24m-lr}VtXEvR2|NaU8-9P;qUb=I} zQ&)xzO3ibxFZjwYd4zBKx`!DLN`~8)$$C8|_wN&>pBcl0>jWH`fh&)H30c`=a__(%S6b>-^SzkGV{-aFf-{@R!Y5Q!CXeZ!Ul z7X*$NIwXa}xn|0Pv(f%6q@ zJ!e%<5I!KBz)MH3*kXJ8Dmz=3!9T?Q&f^@PevaAVPAsh27_c^X$MnjM#~%G4%Jq2p z=7+hMzD#H(4`2NX_Afuq{rSf^o4m;O@CLnNhsoj=i`6-yQ%LE^&4^;O&9HYFqYd{@ zKFP873eZRFb@jbHCV}a#KJR4=#m$F zQ;e^5=00xmB=1n6xb*nZ8Ao39Xv|LJZwoOO8bPSO@`` z6695gk6EON;<^cxIyG_YzgO`(BRn8Q`g2mub!n@SBs)SS;@J9Q>kShDS;PiS8o6H3 zB&~M*vOX{7a^S+siQ=0(}?&0Dxk+;moe>rfBzcND~|5IMwhqjA0BYy>f7lT zW37;1$kQwCxJ- z98wsz$A^^V7DYbBhnmImh~@G;X3=Otp6^l)3_&zFyGkfri^&vi*U{M}MbW3OEq0YW zKq99<*r6x~bashP1?zMG)Hw)&A3%btYS^-{T z^oXtT6}Gmopvc)8U+4buYur7332$Qxkn0@v>NuYMB9ZOZ0jEB08RKg#Hbnioq(4_U z>8U!4Uk2*Tppyg)CD3h)cP+s-h!E)MNMq7c(sr@&2|`mTPis9k#Gf$;(88mn!a8Uz zw0@NSk0{_RAcSB{NYb~9SI3F2} zt@ZdisT>5jHi9L(uBNep*|O$rw&c#qjFXEcRoBuhGxAJP7{%W9kiFdz!@^R?j%ra; zS1o0sIJ~sYun1t!xqtVJpZwS>eD1YV`nlra!y!%?E>;bp?DL_oxWNaW+(C(g#%9!a zZnJawGLOCQLv+jg;61(Z0h8lbY1@wZ$x9ehaC9``^;^d{+fmmw7Z)>*PiNdeUou@b zDfcD5y_FCf|Hfva#l}h=J)e2@W4+z|AN=Ug{6h1qfAeqtyq~v!osTbWJpK>q4%cXudIn9eFXQR2ja<*K1;E0(hhj*liBUsNN-~C;`lfU{mewbhNWl#T+`SkqHst}}73N1Cc(Uh6QD1|l#rDdX8 z#ij%A69yEF(uCmA%43v(AkjL<`V`0|9Bo@uS5*v&bC%A|^KD#cSS~Iwc|o(RIG^68*B>+NU*ddvlh&GbKu+9g6ts^y%jT5XyLl?zN?B1}m64r~h4D3t>5WB$lm9tAPDEqFG? zedto(p<+S3rWmc$?tKd`E!0#VP@|YQy}4uR`29k|CS5)@B5CrU8XZKGjS)md6o)rF zJ#TD`k{<@r<4a*{Qm;2U;Jbh{N#fKc1GNlMf|PPne9H#yy8d44?;}FoB83o8nTVdJ zlrw=`8`0C}ZL%uT&)Go`A*OIExjDV-4NI_5&Zzq!v$UZR;lYM0(T&ojEK#LWGIwDR z2yE-4M5l?U;w;OMMzgbh$d$t<8TNMxKA=U;Q;&WX!393@i+_i`!>c^?=m)sG|5U^t zD@mryR0PM@U}tNrYjMu9T%EIAO$b;P%QK#P@uv~Svpu}R-Q#DOEbih(O>i+Q!Usz~ z-)3ucCBCmspZVmRv)K!@eiHL^EcC}Cbe^%CofBHmaJ)w$cac(XF}uxbI!DMDn5t#L zV6aDC_Nl4~Z9T(wk;aCuF*zVT{lN}{!7i)nglZMPm(m(9Y}|%+0-y3RdgU(0^jIv9 z=-L`BbM_9N;G2}WV9r>BD~X_XWBx)+|B>to0}r z@xaaplu>wR={iemJ+t|O`zI6bo-Me4F=tV?m`qdT8D*~68f5J5ju;Izg-KflGXNAa zSaybj?VSPh#fncof5PAY$(K1>v zKG;G?#rdsg@vg-OF#SEst*Zn(qngio@wFwVC+AFOb52jsxqt62$H%A4=1V#o^M|4r z2U_Q`ehu7sFq`oSNFtSbWovu%+wa~x{n_9DC;mhJ+@JXV&;QB$*ZBBi{FNX2 zAs&6p!7=xpK(C;XeI- zkFw0Udf4OojYFJmX&vN7Q1rHN?UJ)QFLN<%+1?p2*xu#j&Z{h^bNW4llAfliX4T6Mr`S#fdnDjg$GIo2!YlM~MFy~boRr=`cD_AI9p&Zn^f={iSKHJWpx z(xg>sxN>-%_rLd7FdlC4{LP=?-s$IP+c`o6lmdZ|CUgJ?nJ(!UBM6q(E(yNDsDfc{ zpYiB2yIYU2xAQOo&(Z0#+&}&dbv+?cilUjcEOvS1$_E(@9%98!CaYJd+bK#J>UP3> zd5^BPDBq*Zwvb9=eT@i;qS(Ti0ZL>bpv-n~t)TKJ2o;!LoHJY8!+ML>S)7__jntaD zo|2m}R}SCJ&gcf`7q_{8`aGrHrf~~qizBRS0fQ15F0{Bd(6lve)lwGQjE4IZ*^n$7 z5JI5W-(ob}r7T7i*)YoFv}AYZD!p=yF&Vvb9Hn(SC)d3<;AuV1b}{74)zs~huC1}Q z<@I}?qv`vV*{WpA{AP_2SwjaJ@lV3ynQPw8&jT9@z9Y_-!1*u|FCUx`( zY`*9E?$3JLls1HGX_ZSdEfG-t*bpj{!XeRUnNbvdj44nei$QoUB0!Zi6j`6G@nuH+ zJ@$5PpjE-Dn$R{C%hfr?6kNah&In1+1!XZr>l}#L9ZG2Gdcmr@pw}BAgrII_Eb9wS zrmr%a9wX5R1kSfCs&lGpnua$y_<(gac{T(qS+++6;aJWtn9lEELxV9{#3L(((T2L6 zVswwIm)^mp-6vQs&bWX2ESeIJWwE-C^ECpEmL(xn*w)cDHLkTtkx>*qlrjh;ovq^Y zCl#3~P_m$|7c}kyl|`d%Ru~4OZQ6QH)7EHFB88%1IJbg5ak71$=AdVmth4z`XAqh&^!Yl_TZltM^NP$lRR zFBKhuRc$#rpRuYOF08=;Yss4+Gde;?lFvhn2=U0ZB%{T3o~m_JT}R`hH^h1EHXOpI@oYpZ(5n;YUC1#J9Xp{pL|G`-^3kT~RXPw%7DZk%BVYx{kR? zDr|6ZQV16F6|+^t@kPb0qYLhx&6(F7(n$KbqMsYeGGoxsxU`!y9ORgyq#Pa6ACAzX z;pnv&saF;I2Sc8J^_-vlrMt*-#6y=4cCc2@#_5aKecWBZy6U(BCaHZeviwCd+Za_1FZed!1x1;c?s3df`G`bvu3 zr)cM|V`sN$IuEMHYBA;H?rSWUE2Pe-RuyMw6ONAO_#i08hb$ILj_w|DKCN*<&Xkb|uO2fHH%WtNm8Yx;vlWg4Xv-nqE-5CWqVN=1X7bph+;oq5Oa ze|}~D;f`_c|NaC2kI(zL3nB1p-v87eSzes}*>%sb>*+tTaYvT-tq{;b@nxTjFd@ z)6EH3`uPr84yc-QE-qfdg&Ly@3bW1AZ~12W<1Jo4{TZgKTeRJrrkN9*i)rLdq%;*p zEUr3R(KI#67<&C3GC3e%84NBnEG}_6d6{{2k3wzJ&kr$Xz+`cg>Fk&+FKD|Jt5r0o zn!F?g81=8PHM~Z(nsfj3b98M>zrVxb{@c(d=l=2MsFw|Rg>5|6HFzvp(Th!AU4x9~ z#j08oHAIG9ug73K#(77t-)C$0fJ_Y78AT~q+pg&4yX@~gN?8n%D!vAad_ZPOd~his zw8oecqYGrx|5(@Iy^W|+*I>IAYdflX&ayhkx+ZQ`+BsD{<9u=l=WC?WbX|=Pmd-U? zOz*H*T*S?(Fld$0G)rt7{V95&F+GJ)JfH(5le=vk-g<-tc=0v0*JD+4)B6%&s+7P`CXMV#rCFW2FTocoyee#wloeaJq`YCiKH>4x) z1FrK34S8S3?V|Q%Jq1=Gyh198mKnZ-x>_NXpg$Nh=lUq#Y z_Xyr0P;8H{5R~I+@;s-rSD7vEb1{3J#qyM@UeH!GG8p>(EtFC;?VPGwAf;k3+NNi= z2*NSw?Xo+%#>MP)&gQRC>M_IofFd8WtdBUK+$JvyoNHJvFHq8u=MmO0?C-O?^$51> zI6D0-ZL_4;8*#AvH2vNdr{^zH&F2KG@HVEw`_N$W0*FPXP?oN#sOx2vl{!sU8f0$q zzCmi}jdN_%A$&%j4Pbtv`hAPp5~#eUrJu~IbS@_{qtXDvAl;e zB~7=A^tk}edZf&ep~PATE+Yt7E+=%>Bc;ZB!Pf9$wzsd+xQ5kg!E|xPqMlPVE9$Dn zI)UxL+SsJKCgyYq10lVJ2j_~dlZ;#{id>T$Non#()hb8i{SnK~vsl#FuA-?MtXDLh zqjNUqbTl1n#e>_>%|r_Mj7?eyegh?Q&e7O__kyy_j;=p^^BtHzm#Xfazx4EmJA@#p z=BFuxNOAMl1-I`^ST0vwTuiusbj1Dp$DE%}S=G%Z6Ue3%^7U{fN>>G)^AWKf=NVpS z`44X$9Q?tqZD-HD`ugX+e)&Jc@kNivzvA)f7hdBl-ucwD*xmhe%gM>p!gcS@gd@)s zMnPd%s{u)Yvb4^oNrI+rYbMhf#$;%fV}rqAxOHpBqmNxeN^qUWdFWinpm0b#!P_~j z`8jpt**?6)&ZX=0w=Xe2dWmj!6VVA+6vn_^0H6x zExtOTnpU)JN0A%4&aq4v8f_%YrsHT5r|eomSxAiRQ5Fg*1;%@f2nZjLDWw%>*+iWQ zfz&2yU5&r$h5JvQqT%m-`nkXVSO37bh2Q-H|KRg_?ta~uJnUx zS`V%FBVc5&)mzqo&Bmya8;&bMG zBq3?Z*c#fE<6_t)je3+Gd6|eiDz1FHDiAu51F@Nk2O$tBLWn_qKC}cIu(APfQCgFk zl3p=l(BER!oiV-t8f88r&-+o55)46kbSW7YyR_{Ebv|ABmyvoJ#9je(1ykRuBjB=jo;usfNL?y_J z5k5Go#WdzZAQ%n~I6Qm;>*t(JZy|+dUd=ea^-|0}@zKANsXlw#k8pVSI1p2tGu>l2 z+z08HEbemW{%1f0c1G8^bm>w02%e^!Gh3cwZG#k=O!b(rPB^=`No}X>?p%EdfAvk?|?ENAcSPGykNdKL2JX-=rX->ix50@yFe$IXRjEOWqmvm zM3CtcDK%?}UDs97%VR67ZDVGRQb?hYQq$Rr`Ra)I@+6)w1Wmi3X;*ZvN!JL+{rfL) zesPDqD7k#_R_>hV+@F1xctYQV%R^xsGQ041Zyi;dGd0I44P_j9ydoG@_d_vOHU!SWwy8pNJQ2Ek)-Ec z%W`pw7ClCThuGS_N)V34>Ig#4&ek57j+gKKV^+%vb+aG@i7YIh5Y6jCA%!7`7Uw!_ z@Yp60e1moI`OC6CnHkY5cc{B1uigIyQsy8G)72?;TSq^Fj;~ke!F6vyMXw1R-%gZpK^d z>71wSx+LF;#3ZRDS%378E?!`kG34 zI-QMKE9;CNXS}xc~B}Ilg_9(asjS91~=pqkE^^Jz7vV9gF3X z>14uuGNY>+q<0i55;I&%oVPOILd4}-7ctE7=ZOOxm6@O4-rM*!%Nf+tT!`5Bz=8 z8uom~d(XW&XI5s-bE-;GsU#tUBtQro+zbuIfiVsnVFrU4+@>4j2@nmYZNL-JrUgwm zL^GH{LI}-ssZ^>dvnuDzJDzz@Yxu_g<6GxuIl>+N2bJXrlq=%Ix%b?2&)Iu_d#&}o z@B2K@uhbeB0!9f61Jd_&+coRLFdi8g7o4827>yJU-CUq_i83Rc^Z0Iq@LQbgIX+&~ zZF?Sl<{74kPaw(#qtS@%-A{3T{}wJ}CPxT~TemN0+?XP4Ik-N>Om5))mzmB>Cf5#F zoxH|oxx~A`q}EJ!_BlDZpmjNm+=qbkfzwroivo*g6D0esV{bNMR2C1s2u5k-5I>_# z*3?@cD3r+^g%HqDQwa00Eco-k?L!~7KYzOX2Y>dD{Eqa;zVG|LX6ttIz0dMj|N7r} z`8z)P)Zgs3;XOE%iwPGLsmk3ip}@sNlA5B{B%iQ0P#R6C1X@ZuXR?95$aHF>hoAu> zT1e`$LZm>@3i>S}1yoe%7%)+=;|#8gtVf<0hm@cz=0l&2yQ0qBVBZZU+9IK~W?b*k z^-H|(GZ-sC9HNa8phkwI66fpJh_S;ML+dZ-!zEHkv?_4jii_1tn4(1M5%bv%rju)! zvEg*{d6w%}a^jND0Js!HHm@fGBI4tQL_nm3R+^$%EY-m7$mL-eDO(x@Ayk8N8<>t{dAR=d*mkHelnFRCk5!2BDlj#ms zHRfXdDi_P!IM-r|3jan=+h20?@O`}ZiSMK+M!2x$*70YswxK9XimGN*?a+65qG7v^ zLQa^B52(s1qk6&a&O>OG3$E=wj#dRy7^Kih`JT)^!6Q%) z_#Oi-1h_ImK4{quU%N6rh(RI}@zIiE92g!U>+%y1XzHC^(c9*M)G()*5ViGcoH!Mq|DFv>T$;yaiK-&Jl#oy_A2`~BvWHA>f1e$7V=Pizp$f>jij87cW_DZlhH`zhm-)tWx0ZmX&uT;qy6SCRA!fOpXveQb?+D zf+#)9<~72$rz}QD1+&>6%l2hf%X@?_VA2GoE3Dfv8_l?J?R_-ODQ$OwP>H@<6GH?v z^Z5;?lS72;38`go?=h6pth!g|e4d7kt7}Z^BkIwNvYOL$r>r-3v2KkvC9ON9-JCO@ zKF+%z|E(0o1RK_zF26|Et?0uRml}+ju-TsAx;5Sge4nQX!RKh^su&@p1DR#~Z>ZmEpoXDuPMoNP&E9%7pSyyZ}J>GRl z;VDXuRN$i|g}f0JGEqu_6o$_AgaIBTq+~QMu-4P`JuZ5T&=?=-lfw(BC!^Q)ukHW$ zpZ(|mANR8_oqWyieSh+MebD)H}p^M`)@MDyp2;1Q*A1JQejADS33gd7M0L_b98 zYDH27F$C64%h~0IyBC)nU#?hgTZDvhRiIT)iJXo#laXXH9y6NnQyY(x9ky>#1S;df zUm%s{<=abs`Q>wZpU^mzOuT;Da{Kg{gPG>38;5+|dmrT|KK&v;^|{-q(H$T_f^Y^g5qwiW)%M}-wOP1?3>n2MA!_}lM3;@zA zQ0YKrm%~LhhhL~W<#_sEeCCDMU;gO(pZS`t*}sa%+fu84$>XE%e;1?4ytx1R3;$u? zZ2y#m$|#x5!BU{5pwc;-6(JuaBuD{;QcNZzCeta`t{rf6bi`yfWilP{jwcT|Je=lE zaxn#WL9@Ki#o0Ni38paArp?ft#WiBT<^1#%A0sYmzWDhs^0_bFVmg`gj`w_oNhOKf zSDDm;&1OsIBu(41yjZbbb=Xu9e2*>+tF2|*NiLRKPEMC}&ePdMXCtG^Fs%v>XEiqu z_9%^}stSs#Ko=UNGDt}hAw)!iay&xnT;q;RK~ZQV37^u-D;xjA|Ln8nKlsD%vFT6# z<^Si`Y~6n2cYO=d`$tb+fA#OWuK%c#xo{}-5U;Bhg;tbGGOeJ{67Ppt-RLn&qqP76 zLPA-T6v7PAiV2LOs%xsEM3Hai+s%@u?dWVC(|6X>M91m0XIn*3Z~8sdXn+e2(YsoL zF2sXB7llS#ncn+c5GV!HdXMpRMv^VTw)j0<;!$_h3_rVM?#k;R>~O0)h?s)9CRcGhtk6| z0gv-rgjAGzN?}GMB4sh9EXI^(%HI4*s_~d@f5K*a!nD3dRnAbd#HgAyytZ0ZD5>+t zUTL(hkU|ek4oOH3AF{Mdt714Ca$%u{4BrqPT9znP;B1R^En1ZrU4gs`Sx|%gLJ)#w zy**~VK4mt!!FZU&#F)>G{CfmSmN?sT=l&NV3ikINM@oeYT~0bm9_zOF*yf4P`Yvs^ zVp8WwSsyLEU9)Y^28w%Ry*gprol{o}9((BPsK--Q?QOQ*8A@u59ut#i(;Rbh@dAy# z!1W!jci62bbRf0JQmpZUq#bSu5Qx!dA5n678WqUzb9u!#2Q4c0XHPNTJz&+}=Jfn! zKn^~P9A_=0BsmB^5Mob~30)eDsD^(Bkqq1Jl#p7a5@=IU6_dfel~6Lj->u)!ck9gi ziGdihgf|=WBV|?R4R**=P`p4D8rQeP4wN!T3dZ9dCX+q(_n$zD65DJzIr%c@m#^S@ zPf^Sm*SnaqASI8BElKoLqZ$Y_-6<)0WU81S>D?tcEUF}?MAKbxdj0~9yTn?Hw-MigYjO;3q36`29fI<-?V52!5BTfNAk3~tVdMyU1Yt$d%=3WX5DP?uFoL@ zE(}Gq%xQ)~BFM>y8DSMrYJh@@3<-1rTuAvI+6UU+5rvHN-QEBEH~gMI_@_VpBR>%S z=HL5AUz2;@ANfE2dcN;({tSQW-~OiW*_)5PuOvJbtwn2z)*w>IUPkgu);ot(Do;97 zAjCx13tAgFUv9Z|e9FCxH5coKkP=0qFj}#%^ zZzQF-d+X&F|J%N4eyb2eDv?qYQX^7^YDt+p@%5+ zna@T%_1J>NqN1M6iEhi~$z4{DL<=WqVg z=lQuW+(8QXz%vi?TYlZgxv}sNF0sAGjCNTqm)yU5#>q*(a4dEv2yNJGJGNG_*?R8W zxlhw4LWpczN7n}?m0@Q#Vt+nicQ#=@9Z{AVtrfvJwAQ4U;{*$>sYWBL%Q3$5*@UVd z5ktcHfDOFVdhs9p=oj06`k}|l^ymH`Upv{A0>0-H@Bh7<%ZvXeMrTSRC<{%YB!v_h zr8BcZB#bHuNNn`9w$CKi!cZEWeLDlVEv86SXq1+e^@zHz85vEq4ej=lwr%mj)AfD6 z89C1;dG3#U+9D#$q3AH;O~QEr+6au!I(aEF)JTN^D(3||^J~=Qn0i#v`3rh`nN9Cv z;E<3_{%RHU&ze@>=tXBkj-hUXaANbYE-lnBv1C~V4VidSo5@W+2zY1Jh~{e#6@ZZYM7Q~Vzd$kRWV~edYI|t2(3!m z{({>lpT_zu z5~2elLcwNxm*wUz&UFulHcAgn1Y&+YQ~@dxorh2$lw>;JW#`(pA)Txxh#o{hsS<0q zgIqAu+lJ^8q0c7$@#v7!OesedHm+&g-DXBm{S#V zTqEe)mPkiY)MRo&LKTXxKcTai`80|7I*Od908?5c|-VgUH zpSe|q#z>W?12LeLKnR6W5;wpBue=_u^E7>Cc32lEMx)OjKJ@r+mP)#TPk6N}^LzC#56mnY-VJL=NVH7(BPYxEUMs4x;K5-~WcvcRZ374^=u*|eN2 zx7@#2bNg(~>2gazFdk(oR$U0L9ZZ-_YU)x_k7p?1NilGI>DZZ9JbH7N$+!mJ^8Cy9 z`2HVwj!{+e$qzrx7hgN&_+rcM&Wv}z^BsKh;~(V4o*}g-oL_9|T%ax#l}Q{u_6&uN zjON!FFRrt_yoGC*SQ~l$wM&jqHZ)Dc>FFu=?j3V_a>DX*MbmY-5QYhd$P=?GI3UCO zd4L3_Arf7e_20Pp)YJcg)@u2qKlyWCvpvecs)zcQ{}FE=?|kYBUVr|x?PB-fx#Zom zigdFwin`DknJ@~Bl2oN)R7ysLVp11O>VnZ=Y%a8xW#lf*5#RJW}){^n4WLz4ib;Y5y?{;xjPvZAAjnR`i&nsIr-A>`?cS|Kl|y= zeNEQwyTAFPsCxXyX1)2ycH2CW1ejq;mAuOvb)|DoiION|a!7z2Hfd7hW5yr*AV@+E zlO0JyqD?_S5rjmUB2N#lqK8O>NnE0$Ks$jiBHpA2DMDA!G`RXa0OC^S2_!Fa7gK5u z4jv{6$K~n{om=Md@BmCG%!ujefU=wrLXY>36hhYZi|k#AArQ!g!WDn|$|RklC)oG^ zqL)*?1{OnMFs23~A+#i!b>@rdBWOKBNzRML6ErxkTEp{}M>WsQ&l5#i>+hxodW{F^-d&rWtcWWBjVYtIIkeD=2$Ma^t{z<9JE;RwN!V#p_- z80hUG5MoD?K3^ZjaNfjxex(qJewZ?fEOnDY=Eg*kiQOS|Bpl=Mg1yC~RK*M>6r*Yf ztqnp%LU8##3B=$rreHeTM;D59`v#3W&Ib3W385vV9@|)Ammo^YddzsdpwJa{HDflv zh7Uc><{W1m`hH7FfhV8%W}bNLoAR|Hb@;Gl)7+!&E^xjfg+z1`5!Aps$putVOff~3 zrGOsxXYb*IANZZzy!l~#+H&vW)A-n+OrCJHc1ho_=v|9*Jt--?jkrD%`;t*{$id;0 zBncO*J1n=yIBW4K5_}wrjcn46aVR!=2oBs3XmAx>jYy~{*gZAuHU)K|8C3=2(l9Ct z#&ym9{yvL?1C%L<-Xfw$MTbs4OJ_;cT4IEtkOGym;X0;7LebiQ3z?SfW5Rk*YlkU^ z&5^Ut2Lib1&fb6ZgFpRC|MdBnU;CQe=l;y^`%V0rA9$I6?J4u!v)cTnNu}PQ2$WhO z3HZK45D_8{@9*sAB1NQ<4-f%u@3`2u+&y1$?|j3>y2HoBbW%|kn%T5wF|V1HP?tIB zO9;4qvf-ycdyAj@;%&b8@-c1Ga($=3C(X}4cg7c9KIWMlQ>5E+da+?P9rK&M^XvHd zd#-V9W~tQ%m9jK+GTq_miFYxbj*y|H*(`}}OLA)vCFSIZyZ0Mz-8!LZS}vDM?w_1- zadE-<<%)INW*!MLCj_QkxJo53YLKiB_gXm&OiR4A3HfZ+`DD1{ni-M>`D$$C`nyv%EC}ciII{*$pJbj zF;Y`0gO-xY1)p)+b{pRPkbQ#*e`tkEnl~9`0n4r$>)D|TTP~0;oYwxQl}v;FcXy3 zKr4+yv}v9el!ahiYAU1h#&9qfDw*^DkrIC;8&V2wN{VVkSyfa8NIXVnUyKrfNazTK z6SyMcRC=J14{a_mu3&W!y7PXZO#_(7rahtUmk0?)msIs6`!a~xKcF=>W>!UvZ~lC) z(urbvFt$lysC+`6H>V&-PEhJOqtPzf)Rg4}ZEA!RXroakcj;YdY3wn9hPuuM-y{Pu zS`rbhs=O%!l&lEJ()7n{`}?f7ciC>w5IRdu`mV(_p4140R9U~BEY>xH1g4{P=QPbF z!R08^x>_K#VBOsT=~?U?vA_QqyNidY^c>qY^mavE?6ALhjK%y~HjEaA+3bL_Du+$h z7A-2ONr{&$kby*xvs6%LjIc9o1+QAfx*4_izv$J!9!~MtET|7)> zc8RXT`b$RT4tqO~vRGV4$vhRFO%521MmWDDga$23M$?*TRtV)mI-J|$!U`LgbeCN& zFqC9$cBzU9N+&{Z8L3?kcAr6)iu3ggG|dS@35+VJ$}vhPk_hM`!~A?~aJI>sj4LR6 z%KQD>i|nX{NZ%4~Xs5CW+bZE`)_Y7j`k}@C z!Jm2L+HUj8o#VIs9`~pI-QUQ)F38{Z-o4*2Ht{cxO8rz>7>pqMwXUMvO-`~4$>%uZ z80oFU`$*GTmQBams^!+nC3i2joUL1I2#iNXZYC<2Pc)M{%Yl`YNF{mo?wU_Me~-o9 z9*;hJlUHt^aqHfan+G!rQ}K(>-{#iwl7m@Ap(USt{gU2UKKj&{{i$F)k}UR)D2!x0 zo3ej+fL)(+diOSc=NauiLQzi{FAf>cuW|3zZC-xyHQJ_QwOVm}ddBg|8JCwU+NOWt zV-pCZ%nd*(GqdXoQJ%>1=F9?;ua}eQANst@b(j=R3viV_X7aU6|HJWuMsH`qQ|=)?;OrK!obH& zLecfPYdsoiV(6%*`&@tO>j+WMYcW&M0Q(w5n+4&ipixcW< z#Qn21uiZIAroa-$RC1vp_v-Z)JQ5KwMyh#3Z{i%ViFURsC8moM0Q4kQUr{MR7z4CO=CgQyHeSyPrZ)-|-<*&u}hYd3V=lFpscx-+a_lSD$R z3ZpBG$#NjA${fQet?uJ{PbGJ$ z%nn6SBDA3IS2W!j%k6FMoqd6m^Dlvk+_?TO7So4FDR0nJiRw=-V|S$ZWn3Te3lVpdQEE z*^a5t6Avkfu_K0-5SrX!BtrwG6gk>hPbi8CAp%O}uDKEdtre0)vI&`VhQd`fN=1y; zltoEZ*TmRi?OB#qNlA!XY`>)UXY~FI7nVp7DRo6rloY1M=#s)1>UxIO(A#rTj2Kzs zeM7(961NHE6_e3|>HL5~8;r?61LbM9_wijvB^T7iE@e?-3Q6!AdUwXQy~Ek^3!E*V z1LHWl{!Vsgk06PZWkFR}NEJw_C-^m;J;M#2nsK>LRV+xNp%16@@sz-p($r|9DCLx+ z(Z|`FK8-Lv)p&%D4PA3dav5&uV@~o)$q{^y_ZCOqsX7+$<=(-h+J)`MQ z>Fkn}0!Ec+U1H1_qsu&azrQ2?C>jX|xm2)Pu1B@>7hbc)ivRU7w zZyJg?rz-X^MSk6!Z|K}PoAxeem(Ou>@kIhH2Zv9ym_G&xj43Fp5lK3Hu)}$CM%!MJ zBGhJ&LM@20qYEcA?i3r>NLeG5Mx`-__1CdKc{f4!m}-O#9h=Q1zROLY-fuGnJ=lRy z_|Re;!G0kT@>tXOKNSXw4p2vLS|XiYEHjm_-D5s7QgD|_BYQ*<}Vn@<3cK8 zObC@z5k<-%QJFLbMd}?2fmRw)qPL!P+jIYH#hud?w~sG5xmeRW&tz0l89`krrX#~_ zq8X2C%xH&dJm>yJ&(D70HV@x8-~*4(*&Qo3tz)(6c;?|bvs$ugEHB+zav3qdDXu5;zmz>>xo!hV8 z!L}XUX3hHIjLq^4*RFA0m-$)isNytq-nf~d&`1k+e-#OTw zr~l!v{10#Gdi_c|-Yz`8ipS6Y+|SeBeL0o&_&L|NM={tBNs@>uW!+~WcSK^q#z^n7 z&aijk!Pk55vSDt(7YnKBttYq^VounoD?>FJvHQ?dj7Ns??jfVuoaxR1-R7KTwdKxP z$7f$TC3w$^uixj+*_zfRUb(ZP*<4Wh3!HTvKJrA?0(b3$ji)I}lFw0^Wj#U{8i=^w zb3v zf5@!=4B(v4W3?GkTHvngG&VG@wP{%fDqtBhQk>F-uX<19SV;G=p-q7Ne~HJ zMr4&cQC9`VS4s%37z$S~wkyJPj={`^?JW7ok(EOZc_v`Te6_9X1@rkeAPw5`L<$K) zHV21JK%djQT-@MOhqY@0T{fIbM`7x`p0UNd4x=Uz6#)k#GcZ!{gy@J^obR$8UYR^O zup45C`8DkvsaH&khZ)y<`EeynOg)J}6pp67z;@fr-q?A98%OWq;NWqP2^+QuB|(G- z9t!6hy6%$JUb4G;lcQ_zWVOD_y_4tIY>!DK7Ly}xTz?NYuYHiigC}|Tp$~EG`qMQ2 zn8qHn?T%^tGi+Fbj2tYUld(gn-j@^=sdya&cBoiBMORgoTmfw z3Y+Z_BBwXy1eNq4pGwL^UCx-#ucHLS)F5@fVTzOxM3NNN)) zs;*H&5mQTy9iYndMQzf^Z-rMirFh7;OraR6}v@ukd02t0@Nn63Sx2e107% zBra@+f6SYh7z3#XtRW?EX+sb#)@I|p3+(^`EK$Nd@IGiYLJ31mHkAm&D9>(g3X?+cmWS(x|NO+K+j|Gs zuf3n^NAIPNmu&1kn)ZZkdq(Rn3DGj2KEh*OX!{2)`MwxajyVp)eb9%53wffL{T>8elz)9R+xfr#;NSk+-9Pw8 z|LiSYuU~1$+l9wh`FP|08IRxC>#Mr{T(s?nMD&l9I?E!oOem4VFG&zXdSL3ZE)r6P z4v82jl*9~VTB$VNGi=ZWn~P(7zr;7EY%lMV+!`Yt>(hJmZG$o;Pd)Vx9((L@_I7r7 z(qVGxG(l#A! z>yT1plbz4$9xeo&bHw1$12C{Ode}gTOpf(FKi4F&J`z#}tqCDeMG2@JX_q2O>bXqm z8y2;=_fwnM=fCG0KbU^v=YH`mU9*pT>>K#RcmKvapZRg9004jhNkl8B%TOIz*6WDN>voa zjH+CqR536|A|W|ah@=R?dx#2C)LGtQ#>5bC*5Wn^T!}FwA`a_Yf^(20&i3?viw`}% zNhncJPi9%Fq9dF3n2XgLShvloOCrZwmgR&*#HXI7J>&lQb2Rphn@8_wHoC#Ky+mux zWVVaY2`@LexT1AuY`Qx**Mjhb&;kjeKubes&$)Z?Dej#90^9DGm^xG}5izHJ2?T{H zNXcctgVG>emLUm`(Pro>28b#9K15EQQc6&&n(=stqAmzwLrQiiSj)@`2!TRW%%(Sy zDiVA{3U+AqI!qxEI)Jnkrlv3zRgvR-i=spdfm9J8?Sq2n%A-CZshkp3~UdgwP^X$VC-Q;YNlk>ucm0IkeWnD%)hD?Bmf4ZhYIrOTXL<%hx}2~x}$_d-_G^$tZ@J5b%gBakJ&O(v|@33xz4;`UP$YiL;GfYt;40QIK%hhXi{VGo^ zhYZo8m}Ci9YU%Bgv(*c%nm0Jye;13%6SU2eBw;+;K@}R$1{>G(Vad9`g|*uZ+6-+D zr4R{0LFZQ7Ir|K+-TzrGH@E10L-dB^72XGe&xM1Jp5EF_ijD(gA!d1%yDB=m{9$c+ zP;}&&Td0%d@Umpj){M)Nx-u9osS86@lkjOeTRFV&NGk$Aauxyy917aIT_@(7K3=nF&$WqfamP5B}I|pZle=zx8+j;ahq? z`wRc4-^b?WH;G5X%fGFX@%xMv4~LNOHsD-O3<+x;y~}d3NJ1ASK1PHx_?U1Zu4Nmq8w-W`O9B=g^SZA>&=GqvrHyGIXUL?a>=%BNXer`#0QTZ3Jx)tDdQEl zBW0xt0*a#e7d!j=e^e`R@|hQ3e@oZvSK{$@;qew6Pe1hxKm4=5ba~_G=#AB;{iJh# zCk)NNkMiV035+VK)M7{wa!8;mM@%M%sKV3R`*^?2M)Ncn zItV$e%%?%lWl;jEV$P&K#OQ)@R1eauG(hzrrIrxaB)Y*s-6NC0=!&u&Q|bwY(InB) zHy3o96@J@+RTN^uV&{n2>>65_Y(^zvs-Y|IETtZCxchDn4&Omh6?ng*wRdpt0s)j| zO`!`SEh$-qbQooFkcG_T@DR6zxMFYbF%I`X03xtle~C@=DqVky4;yT}#D^u)WKW3{ z2_=hMm=y*@!pBWEZToEcHhP?C@*6*#CV><(*i#!r@O}ROdW2GCj!TsVm0n=XWGEmb zMwN_52S}6HcK5M%1EdEjWFdG`9)9NcVCW%1Rm_--uVGY4S=UIda;&<{>0~Z+^lps{ z4I%k#WDg0Y3JNpMa!6$ebZoa5wA(dy(-Av~NmFL?qhSJL1_@b2U%eJKGjk4h-^JnK z)0nEpx;2~b4sCZ%j1gmMv@Y=CQ^<Ox8z#mHC2L}UZsYP~oP>AzUKtSC%MtUG z!#R;ZTX1due04j?X^}7rg548Cz12t|34rwx>k?8`jApx(dpGD}#d_7US*`G0OX%~a zSER_;NJd7WMV>NhCDAGq?WI&Gt%yi`6m%XM8?YhbLSo%JHa6f=PJ=|q%X+%=CqMP` zpZ)P?zx})T?C0J(|Kgwdga0NQ>&^b<3%`9ns{X7L;%3NZ!)z3fS@s#j5M&WODY$Hk z@AIS!k?5^uxo$Y$G@LGT;nCVaRhN`TP-@A1QZk#AOc%RU<2eCMZzIljymoKJtM@n5 zMsYB!*_n?i3(3XhmSQyK+rI7{JoWG%FW)-Dhsd+kb39sEf|3(oJnO21w4*%$Bg-0SD`L2-d4;>p3zC#KzDosuqtA-K1(S~X;oaP%$WMdPJ zO|&-Bd9WeU`CM#l+sLvBY`UCk(tA6koCJ~(D5I$=&A1elDxzYK5i!fP$kc1o35r${ z1-Jo#m4@zK4EZBn!LzRPTJUDUA;<;FV5EyfffHiDrZt^i5rWIIDt`q^OGFRBgVrO) z<6VT5fMhnm&feY=6je#bV3&eAvQ<>qk5ORUQm=3b-AE46LgWiVM+;#a!fs%ASLwuG3)MCilSgXyNR<6 zG4&Kxi7{qK1WSa_5L1g**>?aQ?>B=KMpLLUML8onM+hyxbr1&+#sE*0N)VIFk-}1; zihNxYGGWw+@#Fxh1kN{j>xjwY*kZ$SAfAUe0gRCxqwI5$I+^UFbwL8A(*q9npQNg% z^l?cSR)k>jSi;rROX?vpE}y(wl@#R!r48O&n*M@r+Y@`BGt}aM>GX)j?oC|SaJhaR z>l*;Pvvl1Wku>`|PjYzp4Ac1mo^6g9HYNv6q`;`&qbhS!Tt06m*|VZFLP)f#DC#Lf z2)4}$7u#2mTC$iugzY zdb}I_8!k7pVjP-U4tx;!BycV>I&7c09VrZvrx}C9yj2`tmW-Xrd#4YIVWM*(vuvjyD;Iemu;B!$|PyXi{Prv6cJ@dYI`S1VRZ(UscfAsr*4`)`FPtVi8Qy1yKK#J>W zcrV0YbK^xCjFM;xMntrLLS-hD5a66;+gUD_4foDh+&y1&e7RxMTC|aj3YnXvm0~(6 zsEdTR9bbOsEbLn^6H&)UcP-!K=P}feTqq0aC&^7yXR}( z_2dmc{((m^M$>kV`N2cX=2H$HeHZ2QkkDTuLWhVQuDc-GHL{o?^n_PmzRRm`oO8Zv zI6k}N_T6LNxO>9!`I6PPrFDVUB|4wj+I(*g5p2xGXz$WjKvzLZaaPvVpZLaK_uKw) zY1jP3FaGjdwqC!IkGBhtx9GTg|CFQMy>#v7BVW8YJFPqGKHmALdOr}slfWf`M_^O# zs;_L|yz!j1f#Z$mWa~L^Bb`gw2rfbI6V_UUUz0+Qk_o##%Og(TQkNin!{+=x#rzQ_ z^IeL{aObSyaDTzg{VAV&@h-33%U#HKKCDDg_- z1^NAu1jEaB6|#W|imF!#CvYO-2cHJR=adGI$XEU+AcNuFq*=!}44Q1F_(~%lqJ0OfQs6^}qNFgRET57Q>$gN3azSWwT34a!yqVLPYam3Tu4ZU$2(dv3Phl#A zfZmA>ikdNM&}We-M;P*NjQL?kh6Y7$Z;yk8+iVm3LV9#2TJ!KZV4JSTaZ z3m+l^ht%1Wu9V5CEe{OUEg@{^mn~tPpx4M)u(R_hi@iq(tZ4gN#JC+Kbuv$35bVsJ zZ=0V53|wn!OJDq!6i;Luv&+yzobs&WQ&ICqv`8yO*b z+WsDaHS^h{jO%?mm;WwR$_ZaO8=Z`qAeADdyf&s7UN0H3eog2+u}{Q4p$Fh%SA`t}1n)Dr(D{b8Thp#u+&Ymu30|`^f0+Hl$FX6< za&wEWU!hfvQYKFt670<$=lZqxva@%C)}3*D@f^#|>ooQ(PXdL;`Yy*?%b2Hqu}3OJ z+g}i(qbz3x9BqF_-*2+(B}O*QeL8=^Z1OOZ(RDWMefqG@(n=y>khSC@#?bo~Vu4m; zcrf|c(6<)fCw!NS4(GDoKKLv1_y7j^l}FJJBoE7;o<>^CK2VW`W3sw^mq zBEuNRQ>KeO%yf@UDmh!VT&&h?+Kz2!*;=2C;!(2kaNb4ktsVE9z;P2f-X=~PSa*SC zYgt*36NWy3b6G5_rNqZbXD#04MnP58zck(5|0C-+Ubz1ce)JdKqTR+H;?Mu_chdD~ z{PZIJd$ke&7e(58Qz9t9`Jsx71RqdBB1C462$jut};`I)zedHSc-yChvcI z&gWjb$B%#ZHiw4?eD|;V0Q0e+mpg2`>}Q>gHH*D{%6ft+XGEb;+MvoYs+dxY4>&#V zdF7>BY_|=~X2a$A1t+IxESF1q+vl`MHAK1Or;d%#g@jMy&2%)N4~agBuBfZO_`YX9 z@>lQQc{TjlFMQ@LTCcZ{w+oNA@c90J`2XgA{;&UXc;xZNK5yIRVG+Z7r4S-ZLKJmr zsD{)hq4Ee9krmf5fpZb>v-iV?yfIBe(Y66+9g|57v8UOrG1Zvy;yQkPhxPIj7b~W- z5-FihYo2-Rh*=@{`OkcrUwYv#RbBDECy%IgB1K1CX{yOVj?eWimltccz2M$?!xvvZ zLHZWkT0%^eRf$M}sw@T{hkp>w+Pi@BftX_MAnF2L6#3#NBtnyc9eY{Yz_|irA^sj!$qWjUGerse`UwZis-to{iUyVjzOuqj}@cuC= zvQ}Tq?C#cDp_OJ-mn_C5wUH>9s0xjdl1eL#5)?*K780c*by-lC1xD*U#S*zl@IDce zB1jFYA|^vj8W+LEJVEg>TU?1?tfc5fzKF^+FhSmY{BIT@`R_51qcjskaWKddJO~3A zF&NK>Nl3~D^yCGKimF&p*E4n(kFme^6!mz<#r8EWHm}jzOOj8NdO=<8AP35Kh#v1P zHgq|(piT7ah}%R$n-I2QXYv%+58lgUa>%;7%h}~CY}+$@=m{YZqNQtFj2d%z@C@^v zBW&1kxp{+adq&r<2+`qOPiHrTU@6Q9>l<3PB8HUXIo$^DJ*F68$}(>d6G)k<-KLxn zqNVLGNO(+9VN{h9z#hm%KnjgAlA@~7vdEiUa-koCMWlobMXnZuWK`~;j2`4F5>u2^ z#RTx#B(8GBn4~8LM{t?kErmcNML8-c$0aHDq?irRLWEpKhJ;83m7Y`8Gv>2L*xh}a z@oYikZn5oN#>YzpiPFre2Nnd;!jPyHQ%m$6v5&ZQ#BUO*OVAcf%Evf7ct6wG5q-E% z)4W38p8@@_LCud>Q?tAK4rYrRAX-}cI=ww1gf(DE(GtRzlzK{2lTwchmxvURDv{Eb z7#u}4q9_X_IW12rjn*Y)InFVx{ya;v$`T{%{J2vCj~Y#i-2Q3ZJ7#QwqeGvd17~ zc~(`=*jYTn;odu#%=TC{cQ{?WOk*$b&QYitb-9DmWiB$J$A=ueU|mP(BX*r|>wq7O z>WlhOZXCX!oyEg!x>HVUj(K~5DC-lhjYK<9FLRTv766!Rc=(n_wc zO7=p*c!2xWWlpau%8JQ!#_sL`)17@}F~QkL>^u6drR!P*i9*Sob|vH>YgWUD$w4Ve zA!PObJPIBPtsE)b+TAI*3F*bn~v7k=X7-}bBd+~=Qv zOYU2L@_XOOrf2rpoIg4#_!A&n2busql&^vCbeRJQBaw{XgXuM*yqOKj1Rrz-P(uQs5Il&j zS2BO?Dz5zM7MhT;9F6|w(c@42XD@v5`OP2tOaBvp@B9DnZ^?SSeY{pT6ZkQic$;C&z3ZO#EKa|`?*{E>=*7bFNeppv&-JmqYyeK z(;8(ethEFD0uX}EWoc0$5=p~Jo{&lqq$DYcDs+bJr9_H(I?(kVq(+q^qEN&nvo2W5 z(#Z66`<1AcTl-7D>$^VQEFOL*pZV-(zv^rD{L8OnPTp8eu0MR=w#~Ok?ijyD-&ey)v81q{2z(IM>IgSuF|WnWLd!uUvmOc2!s|yD@bCH z0Q&sAjGAz`_cZ(ak29IS~^2L){s{_Y`J?5R%}!EM*Ic6g|;Kv@(bR{5Kx$qKzWNmPAKN zHm3}wJOR1#J!xr>Ng<`dFhP|8R8Qlh(RvojNQdM+1r1L$#kD`G{VOdHr-1^ zTC}QADp3|wiegMiE3{SvwYo*hJT)PJ@FhFdhuB+ufXeJKpFPHOew`#Y_;f+r+$Y+W znpAp#-QUk(Mm{AT65Lv2J6r&6~6dt7{QVE1|D5Bd9fIpWVfy?CjperH0m?v)tUz^1y*tk&+~N#kkn#;PA;zf$vVReob#1T<9^T#+WfF zCA6$5%1KU|%fJhA28A20M|&g@*!CAB5@ordD8?WpO@E1VJwg^ zqI6AFF0ig+-JR0*8$uGe;Ig-4@N5JRw&zWmm4EYd_HziFO?lcee9r(%r}UEDmEqd9 zVy+CMQc)K!{Bj+Yy@okuE3VI-qcGaVU9mC#Z!9cMuBV(*Zh#e}L5(4TWKD>yj3&g=J=+&NjI zlcScFSMQ#2=b}TVz|MS(6p__x&GRq3#ur{W=dnj0;b?!%%Xd~NA$a`8F4qqiOy@I{ zGI-bJ5Ss|6CriHk;(b=@E$j7$lan*<+`rHH`8nHdlYMnUpuPeKtdt!1M46!`1Oxn$ zY$h+wkMHj7{1M-Ex4(S*`0b+OEk52ZJiZ3UL)Z5?ec?-Iqut#XWOSb>RoXL3phUz7 z&@xx#T7fYVA#*a0l({1vLLTD_L^h z|Kz7$;3t0h7GJt`!T#P3-}cdWpi@Tzk z?h}fr3kBmsQObxK7#zXd9Jx988jLE?+F)a%cR5JDEQ%qrFd?O2R7y$%g$h(Un}3ZY z%X%c)6kHS(MMY_9Kn;<9^37>R1mh)jFOhMWe#kewfaxlBH|7n=1L+jQCIx}e!}kx< zkQ4>MX6aQF0Z&h3FIji@SvB_uvv@!%O<9ifCPe1=tQ5he9v3=n=du){OQHivq#mA2dhj)+HOiEv7&0fpxAbm3OlER4v@tc>RD)?$<3mRjfx4XK?r&(Y zaZMsZ6uG-C+#rD(XX?B6L<^N!Fd5$vp|nKme6vlW2W3gt z5xpRF0_AIz7*mhNRO1O{m1SCyORPP^#|ug`MZ|*Sv)^buI$&@2Jxu2NboLINy)zir zFNtIx3BqM#5YfTcC5LVgrjvbmXHO5p(DRY8a>=4RR z6jP*VW-9#vXl!}lVtX<{}8j+AP3@$~1R+F4+ z6~TF%-&09bRSR@g5Ta!=x<-sKzaM)@NnuP)5RP?s%*Ez)&Q`Co+TN$@xA^3UGJCN+ z78@Fz>+#9qVvp}V!GiAuq0QF|L_n4bWi&RlY`Rk}R&TJ{-lr_aNXiV$i;?kYkE6qP zGuye&x;^G{eGBW`4D|~w&UN_ckV>Ldjnq1`gNE}a#-Yd>q?fv=^2ARnT(op{`Cuv& zLO{w2rE5TfFgS1V$zqBL2u)|VGy$_lBPB-Vc=b$Ck4Tv%SCh&3pC84v)(J>~R(kA3`Ee*Ld{Kkt6H;^xtH zZa(rPpZLg=Oq55Pg89x2sSGYa6a~x64R5@0L9=ezZq}Tgo^t=*Jx-2KSg+Rf*5#?T z&?E#dCTxhqePN*7hdA6b@>ED`_55f&`U9u0-~a67aF^q=%eQ2`-ag(u-Yz`82FI=Y zXMFNIe+}>Zu7Bg!`HP=BORoRsLZne$=$vF_6r~YVN-!xj(^66E+&M25XaSW<6iQ^{ zxRM#vB^7OFiLN2K2B{4@2R9i_=h*EftJRh}7m>g7L!aT!>4wK{9`bD;ew?Rn&besd zsy!%le-X9NS?m%5aRh1|y zNYNoumW1U*C503cttHk4dYk14Qc23vFdi8S8Ij4MMZn0IV}=Q&V3ET#AjUXo=p{*L zVi1UgE=%)1O?uP^KJ#0@<^AX1^iAKu5B~U9S3CRMORw|HV>hC%r(f=y_TAR`XMD;x zM3f>44N@abK@yrIGL*@Bj}Ia1wnLW4X(@<Aw*3WNzoz#Mjy|X0#DI%Eb{X+&iIN4=$u){%jPot4 z=02C3+gz^huxjq(qa%cXPZsY3DN4#}#(2Dk)RJ{~jCBpY?Fq?aOpQ_nWiibOSJ5#V z?;xeZ#STw4$Qv`t2HA3){V8^Z^L^%tr~(9p=yNKUnGBmyiw}L?c&35&tRSg~66Fwa zo707~t{7GORMi}%H4%^NdI%D23J`&q+z{26@UA10B|i{JAxFN}lMzx&gos4%Nj?x- zhbVf4^n|#@hisINeL{(n(PRgy6E0kU>?o_r0Pf75hnUv*ctMC85)DcAq|_rtj@9+c zgkPyV2^t+_$xQ69?wH=)rt9uv{V}L~P9todwuKyhTTk{dmBGgQBpPDqK{$-6(6XQ? zrU;P`qN5%y(8hr5Ny;IJm?C>C@%Cx6GbjSQ;Jy&pA1D^4>9L4 zb9h|9HjW^BkUj%5?Hcd1KOuTZGT~B3+b`+t8XtRn@PrhGi6XiCJZypjiIha%kjH@A zfL-Q>0U{Uko!zqB+~Z<>i{SjZHJE*rAm}4 zDa#oGf%VO>HdT(MjusyRT2++A0#l3uNo!YF*JY`cED%EBg9j9anIokkB!>+>F(aNy zh%5_^d8%Q3jvbHW$wi1+CUpe>bS?Dlm9Hb4#+?Q01g;l2pB_v(V$5}AZ6%APVZPQB zK@JS0EJ02}GpV6x3Q!8;MrA5b+7R5a~jW&y_{_VmY4v0cZO!{>W#(`hLX!>U)1P z-}Q-)AcB2tR-`{$YVkXSkfju22%`Y`KnA}^$RzR3c@`L>9k3YeC9-lKcj&i)<`a@6f zt|zZ^Fqd>qOA?CNd`3ChW3>MSh>BNVzRl_Rmi4;f^z4Fr_s=*!yI{3xXsyG?Ou4tg zXUTWWwEGy-0B{~$U4tH3DRnxY&;QsT`SaiR18ukECw}IwX=c8C{I8F<3y-hS@%)!x zVz*G~`jhYa(w$qk%-Op8`b{5n;{w}0(ECg|4KWc?debN@axAJK4YCY|sK+$K&nifU zy=zD8Ub{)KTehoH`o2Zidq_Rvr#|%suI=yeJ)e9xj~05RFxL*s!R2rtxv!F@Biul z?*5nW{C+7W0quGc#sJ)%E;ZCi~eUr-`^LZ)y{D?w#6le%C&GE8etSt#nPhsrQO zDTpM3kTiW{-3PWVu?^5TXnkPQJNl4FLJn+;0V?S;j@&4XsY+x~Vy=p{!eEr47;boa zh|JYeQKG1xpsT zaSR2E_ZfodyMW(FYJJFbd<|6;v~~qTQdV;y(e@j9*J5p-e_lU$Ipmy&Z+w`Yoky^- zVQbG>HOKUJixlPx<(;$r{gwn+*RgFcY5Emizs30;>w2v3=K`Z8yqswFhqxsJuWo)XY?C<>@%}OYeu78s$xOsFKGHx+V&jVw1iGV(izy6 z93l1~LiR7{QI_uzF=oVk`Y_sPT6=Fv&e7q$3EyNDEN|0KT zVuLi99b$@co_Mg$QxQ?3Ohs8tF=m=QVyecN5mF0c+Tg;P5NuAR8hm7<(RHeF9~+mn z_L#mq$2ARpD@e%@V-7j+p(o-oW`xlrVhp*-AuA^1n1)->IcrrXkcm+xmu;vbG+>}^gj_IMp9@Jb z5G4f(=N!Ftgcyl2fK-?DZ1E?4^wVGZ$M1ORQC@oe?pO7`^ndu>zlXo{*S?>h{-O82 zZ=}-yO_P4ZcD>cB*0Sk6%f@lJ?b-B!<#t8mT2@WN<+jBk@xjwseCA;uI@;&;yXU-q?~Fw)_?8bp%txQz;h{Z=4ogbys7p;ft}`%jc@Lu# z^>{=`piIGZXCEOeUis3CoSj@?yO!1RlFRdRn)M~N*^pw6B3Hvy%7r-Gzfz7<4v8)# z`jG$ZB1A-6PbPov(<^_xAp^_3!?Rr)&@2LFJzIQnd#pj64x{!D!i#s7 z_@wyo(>Dm+mS23~gcojK^2WV$taBVp4Ms&uqj0vzS(lSY5@?yb`oV`GS6$F{j<&OO zou_Sktn;`4txdFEv9*z{ja;r8THE2gBY20iEx}o&${G1Yi_$VDP6q$D7U?5D^Ftqe z{y+bV|K01afA$aandg7ztF~6pz5E6r{p7dtcYol=j-P(~(K~I|f2$2~;-VPxz!Nq| zoLBS?dY6boqNK?taw(7^qGUwNK&=HM1CuI85mu!}%Lp=0i_#E18$-m|K(Jxxyld*Z zWKz|XMc(KMnT`6uz^BOZs3vKOPGCBLh!6xr3Qab~4*vfKiCI??tQc}f_KL}I^?f0J z#lMGaYLC3RAxc3IhK*C`p)gbGdQM1=)-FLRobTz}hQ?mfwp+aO`DZyuPO%t2%43gw zl%lTkL}YV^t-Z+I;+Q|I&xMMQJ>CagvbobZbdaw$V`)h7y4ooCkd4B*0L$Is7zZCr z03QM&Wz%Km6$}Mg5`$J75E&}vqowzGQ)m5}=zF3~;3Tuj!|co+r|&Nav8Sr$ASHcw zi4P4*8-&g*50t~l1|_OYsg4ej1Z6Sj`n6|KMi9~pr3#cRfP|D1yU=9s3s^#G2yBsBQ`LI<;T#*@lQD0I^7kf@;bu-`d6FFpnLalVgc8Uy z7d{~+Li&mhZ;Edg*S^n_SRzUrl8`7>=0ZXgByudZ(ltV;{C8E51hoAM=PW+vbH#<8 z&Tr}b7VB(Ir}7)xevOY29}{7?R)io3K@O=@3TFeZOV};++c)J^-iiF@;qUqN$`>IM zC5uM0zbV)a1=FHnQX3|9L0uF~#$)z&cR0Fso$J@Gv$uapHJK4jnK>Ia&~-hV?Utr( zY1@`<*R$z5R;^_-+;=YMzcM>`_`iMnp?&{Pe*CK&|Nc9F7AFh>*%*V z{kFrk9lq=EeM{eWbiJkN1MRvc^`2xc+jYm;Ma#X@CAUwP+&W!x?_$fg_Yd|4r4~$U zouyPpF&P>5cE*${&~8>VP0u5bKg`kN&#*Xn7%2j;zkZ8<^kZMdhseM7%}=wVdv?Yp z9>Lx7hHW1?+ADeN`iRkJ#NH$CXTEgZ`wc^2QkLPUoRckt!1{>u z0j0!H$%9aW$*5*;cg{mM4te;-5l7b!8BfP77GoZH@=>(xNp{QG$$hq)HAxtn)g>tz zlJ9A@Tc)#uH!cMK_@`cEQb>-biC_5QEk5(&F^k2NZ~o8|6n?``fBqKt)`2G;y}@EO z;qJW?rlsIppM8QGJCd&55|YB}1E`U-fQ!pMGs;bHovr$DNQ5Z#`Br&At&pQ72s~i1K#yYwG z^e_C6U)8nxZNKN=qbNrD_|yONkGAW}|Fejx%+%?G5|YXoj8W(z1;q~=Tj%4@wa!y2 zDH24-sH~_9iIE9sElLi)e4#R50E178Bqc>zFrCgBjmA{9LKBJ3vfZxPZrAkPmbPiI z*5X{AD20L1;bWlJk!|H^O~9FKLYHPBM{DxfU4aM!r4vdEbOFkM%1D7sn${qV$j>1? zuy!C^e%}fNs>m1mt1F672_>`1F{SXJb4i3~1tMuCqaDW61zvP)+q29Sh%t+}xhj+f zWAj%e1t&4c`Ui8M5V;6ST$%re#k(3BekFWSW&*L$1NA-T6Hh9TN)406^njV644LaN zygnkvVJ#!E5jllTmuRK4sZklE^auq;mZac`sYf7@IsiU<9CQZ9C6|Ae(Gw1@y^GG? zqwVetNmdY=h|zT}xQ!jw?dmlVNDZbe@xCQ_pPzOTBqz{f&Zv5nq&9Tj{X88|6;U*V z&<-Z|EdPo@Wh1sLkW3j>2UOD?qFNE+7E;PS8XPHkNGf|^>=I-^>nQ?BpoQ4ykO(!# z6f;Du@Nr8B7lZ*qlNcbCG7w#U-GlVVs1Zt|ih|(VoQ|hdPT`5^!FwsjfRZJM5}!Jd zBFjF4BiMjl<$@UuQe_WOx{8ZV2`MXLN;uzT86x?d$pv`ceEUAjBZC)AX9sNUInLQ! zm?rQ|db4OCzgMpcl9aMPFAci#n3pDhze|Ix3xwuDv`)Uhzxm#M5L!2kAk*O6Qc@s_ zgf8>?3(4T2Ne}*$RKu`*h-8)`Pcwzc*DYfzCgTGNJ;wW<)?VO4|K>(5dZG*jpPyh9 z17uBtP%=+GhZH@e$^jt*FfaNzL~*MDnhV|~qD%Nb;kIuY#bfviIaPWy9@jX*xKqqJ z!zgJgtIq31S`SU{vDRX{md$2Ev)<5k9oBmMK&V3k+h$M43V#+{Bqq1z+`H-1gogG2~MTP~&Fg)h$$DB>af9K}I5C8ihPyfyj ze|6=*f9!jIH^-ZxKlVuZot2D#PNwuOA+kciIrkv{JOz(P3GaJifWEWYqatzxBMMk= zJ5HBt?w_x?d$#6u)zVl`T~|~_P-(&LV#H)58CRO=Vu$&xqLh}t?J(7tdOTr1t0;6r znK36P7yK{(;xqi@7fyKg=|i4 zF%r|zILcBg{}nQ;tJ7#WwJ41Fd;5p`-w(Fj_Iok7PB$)#hkj%=}yY%tkd=Z+j(3cS*}`^%MC6hx&XZgm&Cpn z@*8WT&o@lI`0bzkSoo2j{JF2%T7CZ0pW?A{6^$uAM{q|3_d!Jzr4fuwVphTKSTV0) zUMi-AVo_gFq9L7-V^Q6QHC-OO-py0m@!d^$O#L&aBRpsRuHwKF8}mn1T$t^BCUL zR>vUn=HSZYIY>l&Kcs>TvaT>_XTwzvzYF+~WrO+W=8kX-xx?(}S+~b*`%9d6_>`&b zS5t|Qa_9Fd=_W9I9%L(Fn8akwxOlLE3GphPGYwLguaK4bt4)p|eueQq4w5tPa*#tp zX1NO4XGDxS{&{fkr#|nm1tP;2lg-Iu!V{y*AHfn+_7LQAI70>V7%FE7<@n-Uh{)37 z+&Xuqqa$>YLe7~j9;F`5(WW3JiM>HVRS*)T1MA-1v58yN{AYgAeB*Ey^fTYH{HVu=z1!0iUNZrGeCu=rd(q zr5ph)2FYZ&B#9QOhExL)0fm-1X$g^iEK(K-6;Y;STs_95ehMiHVz3l?LZRmfsnDvT zDsNC$ha`H0(u|607`ec;me8gwqY6H!Ri%`P;LZ+myUfWL%4QI9So`e7h;fj0u0a6WhNg9C4KB9m>0^JSoI`ym7{iYp0LmR7y_-p*h5_AB1A z!+RUVSI8lUDP(-}eSam<`pOAPx{_U9#cC(;(X;KAtlBd+?PZP!PJ!O`IPY*VJdn?Z zffW>nYfV7rb1zC_9Q+u47@}`;Ot=pc9~91oY!dGiu20woZ1bj{!?{6%HoPSYFPLu) zdz*q?S29tCNmVkb3&v&UCY5Eycs6CWd&qe2I%2$F?KSr=EthLc*H|{|hV!#aR_irg z)6w^qG!(8r1g!NBq|Hi7N~I``VO-UeCL7f2LQ%*_p#+5zRHez%U?Bz)dxokeB&hOd z%K5<`eCdUkUf+52oqYL)ub#K#PyeBRi^h5L?8DV>-yf?#H!96Ls;a>V^~@JBu-+$qsE7SS^>Fovrw}FW%*3*>infBL2VZ{dt(K`CZqEzrNeOJXlMm+v-zeD12#&iIe%$Z}nMU)Q0|grwjJ>_SoOs=k#=rZyHnzxB-S(TBx0(vQSxlao(I3SgJf@6t*aTak_K) z>mPpX6CZ!gr@ev49{>1%yJGM8Mg^jY2FfTM%GU;n&U-`Agh`k!V0 zy>I)JoUccpI^c!(-PxI?HYesd+xE^A7mA>$x zgCw!otSEw}ZVhFtF@x+XW-+uU2H(fCWJu>tM;xGCu~P%QD-(M4tO9;u)aToU(?#PD zl@wtE@JrHZ6cI=|iSHsqOzNUpiFuHxoEKu}jYNdxheGAN+3@OuavX+YEuE8(1H`1| za}4rv7hl)86497PEO}F0*y%h1e>ibPyj#T9LCS(~}1&Q1%)^ zCPyGDv@WQt9g1>9)=P{tSgT3&6mBLo@7R!hFVMr3QK_bKmoT;|9hNlYhTdbIY$ za3UTvJHc3sHhI{HS#s{kd4WPvmfKX-u9)J}8CiFrBSsmV9Rp&#H&$YHbMAy~q71GW z%N9s^X(p^D+lbN{Yc{dPl&tdby;blZ#w$n{_Af4x2$i=CsS4zAAf#7o;MyBjYzoNfW(326Qde7vr6v0 zAkwA~^M!mYMMcb7n)hclHF0S0LmFVGLCe<`wM5?zvZ>JD{DxrV9=;br z8aovgRi~M)ESn3*q<7TH;fz7H%NC_A))o}yh-$jQWMhYFbBErJS*+n`zTouil-1c8 z%f*7l=_zg7&~_bNjC3in?mTVCq@>ANX0^j(j z%Z`&}M~CI}Kl35p^!f+5a>t!)Obcu{fbNjhV#(>er6@~A8{4GNpi&DdVC!v)=^Y#& zt$FGb2dtKBPEO|R?;o(YcfkJ9G4rz}%eJHKBW*}@F%dKPBm>DX3$fp)$4 zK61Wx4ttYyoqR#XwnkVH66q%?71n5`(=nHKci7#%%=V=nW}BN#Cu6oYEM?^(XUR=( z8kPi;pAk&2R?SdKmX~6dFa7ASRtY_I~n%2|#K+}2@ zip@#Q?$(Sub~f1FoHDLT#^aK*umTzboU?--NM06oArg;1CPET2c+Qc&?`XPqg|h#9 zC^!DvgZCaB{GqS=FxPANEc6=|$lvouL|DM5VQ4 z63)PEHW7$c&eTOgOqtFnG!dgBnnAXNOx=wYuE$~}5r>7*SRGIrY*FBf0%ZzD^@Msn zp&Zq?VXM@2t!S5*E7mKC8SMJC$b6yzm!#2)QsAdVuOpp{bXBAuWuhH&Dv_d24U#Hj z2U(Ux+oP4pwX_zQh%MCMD;Q)QT;w5$d}JV?pZ|Al5xK_j6X$=yt5_h4*|j|aqbD&H#wsDMUD-b#r?%*Qz~G~|>dvY8c16(WVPiSFCsyltDQGooQX5bU`4Xv_?^i-zPSt?8!+$b~$T8Y=`Se z3mmQQ=#M*cH(@$`5Um}D2Ol43^_I>rhAn^|!X0F@CI3dEUAIIPL1|*l_%=f^aC@Tq z&$kfjb^(3v!3e!Hq3K!IWgN~IgGBLM{&#-u=W}z_ps&jD_O;LXb!*PAU-`Axw+m7b_9AzVA(E9~EB$c&hM3689J{Lib02>H$Nt4D-|$lY{jYxHQ&t~*_c#7=6l!N? z^Ix)tKdH5uIA^dxj-D8Yh*%Q2?`2D;dh&dwIOU!hgv&O5icbZL{&tt4n6%C)w8*L?&v;a$J`HXy={2?OLw^Q>ISpf1mCx;mmSrpA|^+68&pMPw)+Csr`LtOQBH89 zOUzFi9{cdeI6giRdDYDW_V)KVJU-%hF3DR78RW!4cAD~#nl{L*;;=zi$bWOzJu}(Z z_;Vk5>ei3E?&UA!Jr6(jDLYrsAOCg7^Nq)E^6{Ckdl}=64SVzBAOEVZZN6KRw@POz zoxvKYo%je+hRRv0Lf~>SW%?K?onv!0VLX{|X=j(&)+MIXDOFukSCO6FDVOeiF^5k* zLcgA~b!ihHG)g;~#XieL!&#Gg`k=u#E&ul4{}K;>>;|9qiu<|y+71Wvh7W$^Nj9s* zX%i?k+;?@xhn|{KSjAU-;fvVauF;cw>6#_@1!t#o+ExtbZ67#0>sfV)vt>`yM`9LO zV9uG@sN%}@jBA&-xUxNCb2gFubfrKU+>oV?k|}Wr{zA55T|rTl7aH4PiW%CNf8?a) zFMrXU%MbtXCpLKN-~0!kl5_P(zTzwR7yta9^F^QWs?S-U9Q|PS-AlEB(i*D5P#0F3 z5Ur?7NuY`;(e@EDY&80qNipJ_CSvG(CM1TbQx?5(NaUDsCQ~{^=?dAFWQB2#dNih} zDymV9D;&cRc!*N;B;VrOHGaLM?^^o4Cj>83GB1(8F-fdv$cdm5Z4qgzNN+PiC&FN+ z(C)Tg+!$##hL9;KfX;vDp zCN6a4LmXm0Z*T<1FaRm#Q~1yP9^C$S8Hz4pI_ROaz zAYZquz&b_tp!47f(F`kniL4x;xfHG%LI*S{cW9l+eUyKz%BTn`TMPzMCTywEM!pHw zo^LcV*-N9LofnONhSB&ACX;()gP`M!)3y7gxR!0JF=%Bl*2*^`DM*eG&d9Vf zO+Rm(vIA{NxkaH+&XH9_n;PxLpe!*97&0ZfCnS=!AGRe5rOROh5Qx1eG>QH!lcJ>< zPf^OTUao0Z4H)r;fCXg<^n+1cp;RSX#@-LweQ6|8gkQ z$eqIU8sqh|E^$5-uIWs4nOG#ElY1w-eBlF8M#;5h@TE^q zTiW%4&?ovXu<9EYt2K+JrS(xJ;JqY#rNJAN$vA76)Ro*1F=C9t8H+K}yg4H$qi`Cd z6e&tvcNbvYd3;D1XBTcX`c_7F`~&>b|LEI4H4@)%`MN*JAH4gNx80n)c(>-QwKHFy zQncQCGP0==W`@T)hcQ;3>)s0>REys&`b5`x7H17drwgth&bf8G;OK0PPnohXl+I9E z!))Z(-mKV|S^*S~FH@P0*(g(%6^qj|MwcI?Jv-!R?UzTbwBv~< z4_Pi+R?8)aM~B>e<|YUGhs+mebUw%?j(m~6$j}JJ!Dn=S*~XM*>u>c*Rgb>@%6%{X zXRFiW_-h~f=%?gdJ%4<1$MaIF-{j-zn+M#nb2;ta^Wej$M~5cH@Wz~zJ2#s9km*CB z4~b>dvuZr+CeXBzu1~BQ&wSZ(y6P|BPiRBNkg~&C!?@Oz<4uf?jHeq+cOS%BMY}qo zstW45q_zS4f~RjC)3iOG^RmlaohI(Pc9}b_UE`^n$J}#un|07U^7IL9pLxxT@8D(k zZD4g|JT94SY*209BakIsk)vor&&`b~W|5<$0TLJ!eIJIYSj1?CO!dsTcGRWCXi&xi znb%_nE9BH1-+pUnV1!QP^_BBqD`EwJ#9#Y7|}XO1ZIj< z25JYTNmwE#Cq{*&iYQUfkhLM9=%SckwbeMQD4e_~OQV_Aj;%?})}&@yji}2SQ&eCr z*;vtJC&<~Nj1fM{pbxg%;FO{WhPpM3Iz!!CY)}{G)^rXf98$9KIsXGL3ZsY+2KU9l5MR_Y}= zE5+e+@Izc_6mLd~iX8Ms!dC1Cvb#Sw01q;pfeM{`CWRy)*WOM9%R>~F(n!%cfT=Rb z_97{qsZltLGmJ+&7+c``HPL&a$Xjt5q+cnZbBT6#hz=G5dWs!-kY>fWCUy~GMrDJwBaD&off$7o zqpcMZcvbJld~V#78=N;U)tq}c6uwpTokYL(tz7HA%|hKCWKnsgDUL1G(owD* zMQ15{OBD)A?N9VAurDzRR@2I?^?u+5S8Oq*Jd&?+VM!lu(EmdczRtC2==uXJ< z`9iLxhE%KDNn7zkRyd?;J&P26Tb3m#*y>|`(>=V8h znOX1|-;06#JY`O@Orct8Ca0E-rDJpLm;^_mEY8}Y`PUTIQo2H*k;Q~+vdQeyRqD;F zsPQE_J?5-|qvJJ4hi5F7YgVfjP1DkL9jD7B%VtH__he)sR6EV6a#U6`DK+ETF)AG! z6URnfvN0~%oRnd~j9ItxL)*b6E(Dtd-Mg5(d_dWD|N$dVc ze&+3;l6v5KzWGlOlUFBk{KiYQ`P59X-7|CxPopQ2R6EjREHC165k1D2df z_3R(cMLsZ$b+yr)oNBDuXErM+3rFxR;qWn<#Sy0m$HW*JO-ERI#wEPu1^4j8^;5Df z`M{G$=zPShUUU}^-M`BR9({_>{q&b`(%wVz9WT3QMr_Y;+Y_o%Q`L#AJ!ba`wVu3 zo(9p*`_PkvLl-3ph1Psdt@A&MPW>F>nQ-C;5~1*JAj3QIY_9<|YAT}tB3kjbyLB_!xVB*!SRlpzV*r1zM? zyJM_Ik!7tuG@m*vbj%PUE=9UvX&XyXRupB8)()i{xiHkOX1tkbS1XQ>4{6pbLboP{ zo=o>LlXMNjuq*fZc(3ES7O9rFl-ql9@YidY7 z5^0kfq<_-Z=bVOSQ+^*~9;9D@5*oHr4E!8PhDsfzMJB0)H;KN?_%acS;oh`J3YGL} zvJus{uWe;+Z%a^VknV|0QGxhqa=xvA95NO}hIKpEE+^rc_V+Ib3rE-qSIVR&Vjfcr znyv*RE(b=1VO);TN;Vo+Yf=>MhBJDA*(!_`N!6;4#1t@A%*Qc9GKLaKxnkJLuGSrA zP0ykgKpp6=98JE@?cDWuZ++~e-An%C!|)@|T|Dv?|I=6VAD#*N%O9-1WV3dE&6)f% z1*DiH#ZQ5?3Uo#n8fB!Jwnh<^kp#(Z*m_4eS*+PRS#tAq#qqM`Y$d6I&KZb-x-8fj z88$XIn2sz?t*NStab>8|0or%gaP7x?8NkKLS8o51J1 z=6-(m-5=wZKKKc=QT(nq-bXX8Sf2G1&NAx2*bgz9OeDZTkI>~7wav`7b~rhj^W-NE zn9r9q>otc*huqrV=Wzd!)6+AWwnZF*lJc39+$(R^z>dcxpW0ZVs`l& zGp{JGKgvV7<{iKK2ow{ZKD@#FbiqAWx9Qdko<3M|^OIZ-Q(NhPr#Ow?VVbcPrd^JN310r-|suHe#^9Xu{v zXqU6nC~Yw+i+)}ke3qXRN{s6vlClA%URr7UeQ(%NS8jFgpZ?>we(mZ<9yk2hKmEJU z>0JHTFTRgI@R@!35AT2CU;VzpM1~;mp=&+1% z?iyWtNFw6QHd!|U0<{KZA>5>>j1Lw6?(53S|`DFNt|E6y=7f)`RTGLRQqpHC(X`+2GTXrai#NrT9El zhhMkERsoi_KO@9rtgRT=cTp5;PTM(wPPTQ?l5)cPHEnFj7$T9r$pqn>iZR+l-w1$A zq#bgw zr2ZO=Dnoi6=aR@g#LR(;8JiSEXQ;fU@R}kRT+~=4ffuGw7%dQ3tF$y*Mq!+#tZT}8 zf-7o@*(PQ*C6^R>;=z_SAgUc-DOYr6{cPx*YwZjXUo78(OKw&%F0T zynXw*H65Sv2md(V^tyugAEwdoxT?R>DE^nGjd%50hK`}{@O>}e`=PKlO19(K6ojlP zicC=%*6W7(S;O&J$HB>p{o@5kr)w6C2MyzTU;`=fA{II_E&`L^+$R4BgZ`U)FDP|jIH>@^*w&}-A{7Y?gn4_`ES5j!;k;M!@To- zPmrVM3*Pu*9=LKpDlFK(dJP{ldwa*&a>~xVuVC})3+b1)IJxx{k34dn{ex4Q<%0R~ zG5dSBIM_R2esW6F1-wtvSePLkCCMe25?CejvXs>YnLK5c$K%ltU%T(#@Alp=e)ajL zx*DczXO?M)V^$LwwvoXrn; z{X^Gy+b=)HPyEW09G)zQDbe}JhaSI4SsGsO;FSAzuQ8i?O!Fiu1?tHLO08%Q_i2}B z%(kyGt9NPU*ICUEsGVn0OA(}v7n5J*Xq4h~?eSqnA2nUJTx%nnvoYgwi7N~_%9P%i z1f?S;jaE)1OanPqrpy-S@{q@_uc7RB)n)&}{iXk&&)nt4k9`Wx)xZ6v5Anrsd^viu z^Uul@l5bYu75jcy7nXI~LDE!(A%$M1a@wGPE{PP&SdFtKDQc>s!Wo6nA~A6TB#x|T z+MX^(lriG#2?^g08TvW$)X6E%Im)8InTE-vWjr0zxE5Dd7%O^ds|%`8Nj2U;YemyF ztmnt{>xD#%_6;d?kc3n&`l=)mqA`%=aXy%3vrx-(BIZOS6EyTT;k7g;!6cHA6rO10 zTwF|4&wqVh1Z9QxL$GNA6Er4(4H|piWFSLZGib)ow-t(ExUM9IxEPF*YDkDXZ$!?U zj2Oiqq?Nk8Nf~JKGQ}Oti6}9Zb`jTQ{E5a-BViT^RTdwI8DfY_AZBzB!Zd>)CZE$5 z$wYdzhONn67-MPLJu*?G0=b}c6;)l+`7@4}kI*$Igg)RHQ4}?0T~oM;7{lWlSB_A| z6Z{J6#u)8EMRH0QGr{RidVfUhJUK5>p~mP9Otnp-Cnb+@5oI*v@_vk|Fsj1ZG0HkZ zJf@2`hLRQEr1S>AR8~k4d=^wnNO4AY@81Ejb&|ij+Lo z6kr1(wFK4@J**=sIfx5eH*wjsg~qeIWFS5Y-^nio}q`pHe7H z8l%h^*=r(E0vA#qXyC{-&bdTsXe@e>QS}Xkp~;LnU&ss9nWkD;D(`U0U<)%`hgtxM zTBDS~hfMMj>#VSPw3daz5Q#1{aYLmcWx2;vhQ1#N+47l@A`%jE&YMW-!aSP?-vR@_ zAjc$)NRtKXrxL!9VS%@aU@}n+cl6+&OI8|*|E%E{HYPbMR2a6HNn!GEZ}w0GP1$Q) zQs}I)(o&$MI+g-;h%?nn(ifEyf1WE#s?nIyWQH5fpr{Gjagr5b(bM+<-+9`mVZC0_ zcP&IuXj{6z#~DK&8nuuB9qEEZ%ojyLJFp$J(wHC{nt~*e*25M=p(tx75Z;stsiQ6n zQq**z$441ty~vF$iTEJD4k(c zT4vLl$+$x2o*X?^S;nI&<4K8AiLN#HU~%JZMw1QR@vdih+pj*s{rBC?3+}svM?Urp zk6oXmvZqgq`|rDpd+xiJNv-(s<9odC(P#MZQ>VP}{xvUqsN>ED9>8kPa@jK7eUR;I zFTxci>%)(-TrBzM$M!iqTGFf=_79J_apM*T2S=QooUv{?X)rRRA=WJ>xmeFL5n`59 ztCTM?9H{H!XU7|}w>}J2d2{)1!Q)jA-U&**u3s(wF*&`` zY8Vw3XY>V8+Gve23ag}u&8Q&}#^4IeXgp%)(j|6xuWs4l2#hchKwTeNdSW03}Fj%edEvJI-7$Qrb^ zBpHGGwU0ls_VF!Sb@8G7Ma!T6>;L3ApR2F@GhfGRUj6Cn$G`7eziPEO`x~UNs})q% zh{4<%oRJA~Bx7trJ*prJGe9dTP!eDbR5lBrAr1uw3ZEohNulTmI&15Nj}nuNEA?Pr zb=ESjOX_h=Syi~ApmdJ1EUD`;t|)PJg;HA5u6ltEw#zx~;)J$Y)2>&9;PKweYI|tv z)Zo-kY2a;;$!Yi;&WoTyDwT4QfvA#+Yts3`6n@?ioTupM1Ue2$TIXh0t+dN1%TF*Hk| z*SknR3xutRHed^|4qT~lPE2z}c`lF4xDxCDVYJ3Foo-{bp=(ctxudtZw)<+lI;Qov zSf1WQn~c*HThj-aOt--VVpf?nB0w8aoPTt*GlOD3$5M5&o=2=ZeYn zPU`VxatbJu$*j;;i&icrY_W-RBdpy-+lrXx_%J7g1;&<4%U5Calx6pRVp?LD3JWrK zl9H7gv>HLyq#g*CW__J59uAZ7MAize63RI7Wu=I=j+7H=C;ximJkDE1RU?#VGH4Aw(#onTnnqJShZ12t#ot0?w4ekUA$3<&y3r*KJ6meLsh) zNhn$x0wfaQ+*586K@Sbd`9vT}vtgpfCUwzx=%}#q_O?+eFhiuB(Ze09^zcJn#77rp zNmoE1-DO2n*D%3_U08+ne9BR+<~cbEW8j7b<-O6c!xmuW&r({&P;9Fw=z z_#aO$U;DO9-v61O|K;cW?)dI+`bw@$CY&_QOUKTA`?xe;Vze!WfO1j_A{Wb9jMzaM z>JS1sNxGIKYbpA!XR&TLnxAoSe8!D~6AtDJ)?L6A7H5PDRu_id&6>-XXG|tzit#38 zVHw*sq;pX9j|L*f&i7jj1{%encQe55L<)N26z#Ct> z!PeMta(u!w%Mk}BYbqUi*+VbnuI(hC!E(mv(*4v`i7^?a9qV<+Bab}E)6X1odbZ$j z|A?obxxw`tw>UXDp=~>PRt$qCpJV`#pVeTffdiWcDPYc^t^M_l&Do#GIluR@8;Ad` zO10;YPx5$vR5s3!RZ#7J!CEa!*J=Pg+k+<*5~-t(bn zSa+T`z4nEC+AHqibh&2l`VIc+kNz^%Xw1F$T;pVZ#*e@A3B2$5U2lGf%XLK#9VrFI z8#|2b2CI`B`1Tk_WICH-T!r@s7#FaJQ8q=(svWeO+R=7_gVQBGMtUFcA+ozYp>RTJ zE(*y+h-uL14&&tv^uDD_hCYV2l`Gn2Zcv9kS>HF(;I=QedP+)*A0S&Q5!Zb&DM;Uu!L+aYHq# zsJk()D6qy+))PjqV(Zdn$ewP!q+OlScP)#PW4dNBtomDm4}+Ocjx>;aW0VR-jOatE zO-h)7eUVcV$W^{b2o>;Ot{n5gOz{IxFCuXPA_H?eleNLegpCF_*R*}0+Ya<9U%c=z zGNaCGutmmI^0lS&H)IN&F89$%D9|RBxakHkOU=pZabi5f zXbsBaTscsx6;-v1RV6Vl$gv^Oux_qPqu58Ro-!HVk8zPc?MdR5?}oVFh%IMWR}(lQ z#$!VNC@HT|Mx(Q!>wTZ;*+XZCRuzTa#p+A4?I04ul4fjx8em$ zT1?T#RAdD{w#1alv1M~KLhCWR`BfM_LRrg6`y}mIBl5a|Wzn}t;!Y`_-nrbu}K%WkKer+p@R+_H!L=tsXF`163MzzR#yhJUN z6F!9@wM%4Gp%)Wz2m*%HDGHrBNulifj^1~obI%!z+^Z?c)-pr^W1gp8#TbTxfy4>t zm~V4VWEUqJ8HTOlvo?nqg?59t>LRU6rUW80lZ}rxM&gxCK~YtVM`K2lDb;w4t;S?s zfL8dJ2%RT}mh2;`St z7THIL*$^|7Re{wATo*^}9K=A%k#!S^NzrxC^&Oq>hqz}&hOQV*{_*7UmA5VqZtUOt z>Nf(d6NoryDI^wZd3b z=V@Bc{B*^^{EWThIfti94o;V}ePC3VRHYcx3unnu0Hj7moc3%^9rbudp=)Z>;oAkv zb)fGeZR3eK^WfLuU+nQ2XxODI9aYZ4d^5p?VS<*C~Cq8k^{@w}Qdd=C%oW1>h_V)If&rfC3A=FIq z+&n7@-Kc?Kb1s#Ii-ap{&Euol>V6)`osxsvUI*@Y}bU-BjWKX%lVv?BA@=! zYkb8*{^ZVQdtc| z#@-E*8;$N0y^koRST#Mzi}gU%PUPg#TB7kZ0p~0_%aqEw+r?-uYkbNqdOt+6S^~0? zw#rfz`gJoE{^5N^{_8&TfBwL?eRcC^zx}y2AHVSakMX8gz93zB!&m>%-n;*I->pvn zx^J2n`IzXsjKbiIrql|P6QOOXoWnSUC(%S`dP9hkRjaiWzdnex>D)Zfd5=aht{jEY z7Z5O|MJ57PW}q<;V^3yH;T+S+2&`k(b#$u&KWZ3{$JnaE_dP|~Q6Ria_X|1CePq9_WXfmaqPH|O@a|K-xuZj=Su!bOfnUoSK zL{!YEA%iR@Mcd0}!^DK~fyH`F({)4*${KPEgxHfpL?)3LlMU5Ep@X8?4){qTYsAd$ z6nS8KBr{CtB7-MMp@S@AR8$o82%lE0j&4%g8MBQmOlLb-r#W4GoW=Z*vK--xEy{8{ z+}me_us{>(`eVFO6vY(dYC>w|ytD?VFJp?#x_=TCJhn6_493L52pk(AHL6W z{b)UvM%a#!HqnrCKx>EAHEq0sjz?%c6B>CIxma$6`O~I7I(Li0>@c0Yg3{eZjvnt9 zU^Gf+j2qE+Cp67XLRu5j8t)fm)sj+A>Pl=~5n>~fDkVTWl#yuNaGUs@lOx3>*T0pG zkFhmAv=pi$1^V6dC37*xO9sHgsJpGRT-{+lE!s5`2pqpc*+vREk7;6rRE= zVhZ95G=>=EdXDFCOa?nglIUb-jg;Nz+r?qqXwKt#)p<0uB4q_yHa^Z7ilSgt*SM-e znS!#Opo$U7Rd{V_k|y_=;5+)hr|&zuwjl+P6Rn$;uI=cWMmDcHV>LLVhfS|EIVnYA z4p^m`jz%Oz&Z?rPu;4UIhl#(|fxeGJz=xqi)1^osBL+#0oUeMi5NLg*?LFGMrl>|g zFuHulcP2P|#|NK;4?g*v-WT8T7ru(7@2l5cslQ^oHh-yb`aTf8NRk(xB!*c<-+NZ; zjxKm%4v|R1#>-ekyY87U)*Q_j>>r=8e|*NtqM_{qRq3dlVq}FtJ{s3(Rbf@)WWL7t zfl;+b$((XFp{`2|nyxd1R5F`wvN+zOY1V`gIa?k=e9n)19FG0VhWb z#*>n1^#qF*R3f=Tllu;<4+uMYwwixx?yp4#?jFsXY*rL zt0he&d+A&z8Bk%zVoO5d~BBArbsib)CF;sAxm8knv|Hbq&*Bppmi2w zESnn}?CxIX>eVak?Cda`&6v$bT-`0HN&ykNa!j8}KJ@U1`0gM0A)dT(!k_%o*YW8u zzl+79VcBR74-ff~U;GHmb;|=+COE6OKF@sg=|k?gRP*wCHh9C!uW|Ra38T@7oIJL0 zL`Iw~8dhhAtU60}+jQ-UzUw(T+T(P-r1P29E0(LCRqJW{K-)*G)=cY?-K`1NcDC5L zH0ILgnCZBtbR{aA0Zykdg(YN3FChiWx)NX%Qq@{((Z)y-3TJ)F|J$iofAh7M!_6Om zqTp|Q&-Xp2^Yw?m;M2J4j?4OeANtsvj`sJyvu~QuFj_HkhDl|xCR165(-wQq#n4(L z9$5ie;9)3=!V(!0Zq8v}8u~CKP$dChC?%jrJv1CCMzYcbG|pP;Lh|tKuq{YgVoS@a zU_2R7jYeo&;K~|fOI%UmT!||Sv^FShBuOQ;_n!6QjOaV^ur=uW zjugT$jT&Sg335tkq)`fSNEeEtYu6~*+8N=NSRtLSEX^>XP7Q>iC@I8IoqI1Uko~HG;r)$2W?;6@>#d5V8 zcqkJ8d`@`tHqz{P(q_h3Nw;!^$gW%kt{5a@224U&1|-}77j({Y<Du{`cB=#BWRf#cf@M-kopYnkiy~HFdp@w%cNwQRy zO-nKGE37dT&QX>H&K44zYz$ec0jwu-En^hUDilG0d3{d^3EwtEFTal=pq0pU+P)>n zUY-w0wgE9FVi=$*8Tt@NgUZTib75>ZL)ZmcUp#9;;66rMdDj#L+B!^82yNUI7*|l# zBaE%V7KFh+6=RT;rZ5b|e4y((nzoVcOy84Kx<) zg)IQh*|Mec0c$i>)%W#;lX6x#exMypL9dyKYdrD(d9Y`i6p zn4BV^ZzYy~Xz)Tzw7qyM=8F~khbQdM&p0?;a=L2ie55K2rBl>{r=uziJ6A3-nT#l` zB6KTa^bjJGals27yvn7WDbaV}#%$j43Yyb>bWwA>$o#vv{WAaQy^o;CT;AE@Ghcl_ zU;LSOvoW!ZCS&SLFU4BLeD6tIHDy#8_HNu@JQ=aEwJqTk)drY?cD?55r;fRC^Mu7> z$(c?kWG@8h|3E~fk}^i0MYbGEU$@zyW+(l7bm zM<4!B__=p}YPc%TAD{Z;`Nre7`0?h~JcKRE$;tlpw{&gu7s#p5N;1lg77l|L8gGNo zoRLg)XGObSlogjRZF6a7hwYsmHn%TPk0wkfCD-oUU^KGy?TY9_b9OrC7k>2-{@IVdix50_Y}GuucZMsqg^O9E`@7?@gz3+cl3Q=35D6C~tS=m;^jMa)+T`(zy z-Jw;2WTbIOiZ1pTt*Bi=${HV&cqVeD?V_ZdIayl9b1GXFIZIU(Qs`v)_>>|>Yw-$| zB^X0pmP|%9MOmR;g|Y=jS>cLOWP4WROU{*8YcNJZ?6F2+tR*W$3_acYjNJE-1HS2K znk8M=(01ZO2`LTRhm1mFjUuUYl6pktj0Q?8MfP|yp{PX;b5ZE45>-w_KymKfNSWRT zIxmcq-Uo6B158Z`EqiFxQ}jb>P$uPwRf=&b_mCz_Vp>j=0}n-k)K$ zrLL!drzl5g6$t4}V1?G8GeE?ZBTQ2C@fcJi4NW!!9X?Z!rx+(v8Ep+pr-5=V1V^JZ z){QWBG?1_jWJAa&LvdG1%)&^Wwb)2wgOEl)`~qt%#1p!bbTISrt!@NBX|X^S=$IcS>I zDgC;|uMJ8$FqV`vU1-GP;6dqvKK10RMHAo47WkZN;k!XvwUXweZ-)e{A+$+*MSWx$ zwPFIdM*IRs8_J?&G@VdgcoifqD}*Qn^;V=JdLUdXG%0JMQqn{!2r-I;Nhz$hz+gPhan8CC;2=Q0?f_BwVSh&%}h?{EU8pVSs{#PXhWj&lA0)Af21s& zc(?i>UbK|ygBO2c4EPjjI|)!xMxTsEleb=e@a2EshtU1lPyNhudN2I7um4Jp7fW^D zmCLWXYpZ^XQ|U``$~Gqme(>pNXx9x{2?Q_3L{9Q73&G2FF^be~v20khfrHZp&+N}R zI9YPKZiy*T7Z&Rbl@l7d%89bj+;L^f*7haFvrS49I61gM=QUO(?!J4Q%R3u5D>BB* zcihWta})19t5wU<$%<$87j!|fvo+?v-IBUW1fQACHrToAKH62w^^boHV;xuTxsxuA z38AB~iP3bM(bhfmedZ$%e~6nm4_TZoIXXJw)~#FY@9lGPe8OV6r0K)JFN*_i ze0l1^P@V@tT}El$k4N?2x%b{X|7zQH%MU#A#BXe&_Wbc*bv)mA{5CjV`GUJp#?ErL z`i|JOe=4U`pcJJQ;%m-%(A3(C40xeU%Op*qsLO(_?F}wpzRd3KE|+(A8I8wmY|Xgi zuFL4W7OBK~jc=E%mS;F;xpc>UlvPdFG@KmnapRe%`S2$e9Gt9q>ggMNkqR$nKPIyD5?qC)%1PG@zDwE#-oa@fv|eS z{A9(^ykoxX=m$nb9}?aN#--!(=9oKnHn?_q#?I!1XNJRu%PRL{U}}IjI$e02wK(QmE9!^wrt| z<4OuA4R4AFb0kNIEtY{WI2cSrjP#w5?E55~2S*gS#>h6rRT^s* zMPVpxMd@nldO}^!h-pO#3s4s-`F{;676aX(*K= z=@v!6mJ_s*^t-}cLg`4zb5d?35?75erbNvBW2~)*Onf_l{?3H@Y^E4JB9n+|O|$+u zF}E01L99@v!PqH^>EH=6minaA;uOTT4K=T>_}P?-IR6Y zY)KhH>}0#ubo9$i3Py@I4b^B%uA9&iqbBA=7h1vqhKz%kA`Dy(-$A2}kr| zCO5rieC((?gVBTO);g3H&(vfxVYao+WHJL|ST!OM^1T?LqmQ?TLSlPG16AqJ89Fab za%s#(6B4r;uDe7=BEyw-_@wYrla<3c84Or!hkLg`8I3J8g_BLC(H3LmIuqEaMk|pS zT4Xq-LGCBt6QcMDavsv`V!kl+Dy30FV3xR+~M4d~(P09*^G!$FD#3G%ven_pGX` zw{~rqlEPOOPHC+*IYlv-JB>54k_-c{Kx>6cGVxwCJ^kSsorgX^NSaGKStL!(n#-3f zE?>Qht0pWCd+NF(g^rW!kKvrg=!8v*txG$++G?7n;HQrED0JrN=F`~ClGolhBK1?= z_Re4D=H5PE`$ylzXjI}R58>)9R`ct0i+zeLM#r+Q(ZvjM#u8y%(s!OklNe{HlXyLH z0-Ryd_Jkt=>2-ai>oU9Bip`A@bdrg+Geb&?%vw%}-j6^0@=X9#47V=t?0j=wmLG2xr++cVcr_#22eeR| zlTrkwIcuX7d0L~17@Z}SQYqTx3I05FMdDyd(4P~SlO!xjEUda%L#re&V4sqBA98-> z!<+yBfB;EEK~%T^eZ@gD>$IjW3otA6@hMeNP?yCpVRb{};ZWvWJ|TZzxB_hrd0-pV zl|$={$`Pj(MoAH5t(F)~mnkclYD2=2D8MLM-Q$yy^q3G)ArKaxz6(NL4uR!rLGV4^ zcXVw>hyksVczv|G0AlH!FH*ixStJ-K6G9%Qv``lkg&Td4Xw#@s*%6|q@oSn4C&r+y zrKoC(qQKcPlqE`)m|Q?OgS@7)j>39E2>9SJbCEi=mm;amgYiC-0z?Y|i6X5g8eKxr z8I?0RKvrHh5w>8_924oO$`Pg*OCcB6tX9XuH_7t$_0gl1rYvWevLq0&%3-S#bc-#w zu(lR2MeInXL1!sG10LrJa_-T(#^@=UVlZ&8>HR)BTAZ0-%!HU$kUUzK_}GFf$tZf? zpz@f?UL~d$TW*tdkN0!RVp~`rL@IZgk*a9@0qgDwx;U5bP0}b+5n~|u9<;*QT}H(v zV)FE{A!&tAYeEhvRT6U~gl6z(XtW-Yax2sOb}rN6j1aVkK!b2j?v6vl(F=*&cS0oZ z){pE%-2SHxx;M z9vXe4Ep3nnL?nm-tL0e)8H2)F*&Zt;_p%=*z+D)g56NPpCij};3i@8;ZP6*RtI*n! zm6h#~HWJjZebZF@t1E#ex)Nlc9(0h_>nidT6XgX#;~)EayQYY4R|Qk1}WI zL>ANco)|niMndSNsT%&w_dQ+PNm>z#J`AaOLt1qYMMUKhDcdd6onhSAJ8X}v9^6g>mhY3Yg!-0z^@=iMb{@1Iu}LxE2GKmE#LB; z|MSoO&2Rf#yzMzO9pC;9f08>lN)A_H=gz76>e}Y7)0kay!gm4+$r-v7q*-nolu9TK zF-MA`mJks%eh67vEY=*KE!i8AtM=y$7V8dcG=-BCi@GpuZB7`MFe)>%sbjjm$#_&z zxg_4BJ`j9lJa&v~%fpX+g5##(&2M}KyO$=E1$3)Zjt}>_a@QVLc3zC1?h;nF=u$(u zF~v>ppj1oFP7b+w{ftXjuc5T1C@uH}{jvw;uv*cyaO1`v*KeG#UapzXPuSnzV}F02 z`N;`g)5xZ92sr?xK^)S{3Zp2rqK_FnFgzr>TqWy@f4Mo^cmRU`PS9haor{V2YpCd<&5$X*9}H26r~%YcYUHGP&h+jGdg?L zt20hc%-}81Y+c$WDnrPcs$Npp25lXIN+wdnq-tYkDGEbnI(p?XBg?0~<|X{fhpzKe z@A@z=y=#L{f5|Q?_OyNEiR(vv{NtbC1$R%`+?i0#F0p;(4i-mG^UTK{Cd5cpD#p_( zU9&`GL*Gf>`{m0sHoA^=n=mRfUnWorT{@c9b1+{DP%A`9BTA8RT~L+HPiQ|Mx%X^Y*R} zKE@yU1HbpIo^1T>kG%JnA8D4SZ%uvwDoDx=k*~J2)TP5|q=;5#$mAD*A333Qfy#-( zKolprY!`F_tqDPsQgT@O{EsN1HR+7zUDv7}e2aZOPflG(Y*_|CeW%vzEddtj@#^ z)=K;#*2osfID;};HXYW9ENa-!7;C6XhqGqbXcky!!Acre;S5S!K+=+|b7+M;V^bu6 z+=F>&;8G&=1BpFJ(-C778P@;+X@x=Jxz)f*G0sudH8DdQBGE;vA~P-v3M0cCN!rWg2+qkydEF#VSFQLt zylj!ZPB>RYQ&c}{#@pZW8DI6ykNv~{rYdC9R@%tKHB_pjG=@?e=1oK4G~3%#Zk=j={sViw^ab~F_1Yya zZ8)|j;Ny%Ct})+#nzN&Kvw3M#Qu-(wz$X@GD?&0v-*Ne#JE=z_l#=ayQUyLL zu3w*Xtyl@Zm){qhgf2 z_91{|NDs;vdDrK;?m3HzP-W4OX2e)tmkwhbF$jdMp3K;~`T))<#+z5zzIqQuX;__} z$_q3IqhMoV*xcOabQO5PJy-F4&wCy_;A|bZ_v!}sTq^0h4j1RpA5vLQq1Mz}_b|PD zABBln!v!Z|y>93usMujmrfYiIHsGw4DZk1Xqh;IC2fASj6kBDJ@3pZZO0H^7*n$6Yh<#(!P>5qg1YlGeV~hxbsy+DPs)Kld6rF2+j~%E zfa0k^?<~|-Z9uC;Wet^47-aHqiP)Sd2eW%=6(c*O#0-Y)s<23GW1#mPF$A0jLyt-x zogyr84V2kTDot5uy@3f6DZo7~+-EM4uojvX6w$OKhmIXrrjg zg1Rc0)D@Ge#HfTbnZk-!M4_=>p*tloMJrp#qSgvS8!ELRtcY%_^`m35?hQ(S*EAPjKM&n7rBoaiIfdb z%$^We5;2IkW$3r7M7 zj2(Sg(fR|L{s5mEjGB;@!-q8tg>N5LteSmR>wQ{3r;97veu+;lSy`YU#6Z99NWDRu z3EETwHw|mL-V@}zM+}M>)sRkwY&g7ZIQm`^tGXuBHwy1FDJaHE!^UYrl?{azuUuj6 zg^5oE7t3l#kyndMFNx%FG=SktKQRuOdAt@%5*V{Hz_OD zePY!o+CI|_QXzkCz#b$gg|$?JY&KjS}j2(!Ez z`GUoAN!vEePa96o)|{=EoGu$q7A;L5Ia_xu+n!Y?=}|t)*L)s+;bUgiMr00=hfScB zAA?WNSVLJjMrFaMEZCaXY)&f~juj%O)hbb1MP)3dRU#$!fj)SVTn!u#XUy?*GWq_^ zjjg{_oId^3{P=_){@4HNlYZv^)&Kuj<4kt{!{;?$xLN7HGje7h3dR9%x{>dpfj~3iGJmc_m#kvoYR#ga0xGW5lN;4Z!in4T6WkK*gH?AK8nXR2E@A~y;_{bB-{GQKwCHLRGLv5E#>&#>%1Fc-_ z;x_JQb$UpGpY-&Uc1C(R^zG}+7xJ`ncsMrcC}`@`yzaZ zobElw`s|R8KlUUy_s=+9E8h0YAK|g54|w4{yIh@Gp165JRT|#(sw=$lm3OkWQITR~ zvUL?Zxs!f*P+x^e~=70K}&e!DX~HHX*#*m}MGhTivgOF)1$K-OqRLsYKl zT%%YtGW6&25ufP2a4HHT{(v;dopKV2^aTt{i>ycuvZKNd({CkdSs`Vz(jq-0NqnX< zV(82A(yvRyq_Sx1$XSz=#5l&71?YyNDlOyMQC9{_z-aIx%QnI|DezMg+1t<@8ZBS1 zG{90|g@H!f`-G2~(piG`WDDpEEAgsUr*87RlDz-f(AnZ@ZbtHoT3v@EG&F-H0j zXpHA{qo=PW1xs^054O%;t&Nw&0Xfgj5c81XiPb z7)EFSY?J83A?Hk0X{G~I%xJ0tkSsyV}F;pCjBQc1%heh!Jv zRTt@1eLIlI8^v^K7&nGO8SEghvcpE8EJ{Y>3A5=2rmX2=rd_wJ7Ax9zMewrem2^7L zMqj}E!T@?>kOf-f=yRs&0-`iZLo}^Khn~wp(;%zU+At~$thxO;g`qjgieb~CD6Nz4 z@qp;mT2nYTOowF?ZnU~E=1NU(2J*GK9a(xFQ|lu|Av~9O!50$Wl-0vPCuc&8crUTE ziGe+mq4)AEGAa$eGew9pxmQZbKxq)WYBdn#qedH{+XJK+hXgP2>kXhCQVPS@A{*Or zVHuYerDP+v?f@lzxU(j*?4zWK^3=?UC_+s<7tIRfWA)S|b~P7=>pvNUYA!xi*jqA~jW#zP4&5G-J7FI6OMzV7}z~ z{t*YK3+77+PZ(8>(kh%$RE1%-Ii++Oe2aDkbpiL^v&C#WqN*JAXhiTGdpD0bK3sC; zj!S4~`M-YZgWNpn`1(KbdwA_D?jO2iZ3}MPT5#j$ob__W;o%W`xAxiF+vD)?h_l6tc3_n# zWC)YuK%x&xLKpJw=EKJU5(<=M@$;9jUis6(`$r#n@|oX|y6gGlQ+GTswfb#%T)X!^ ze&XN1{iAPs^^3k?dA$F3iT=$_8^Vy*Vu0R@hFoifGLW*N??CsFh3~PAXS(jVwC-7Z zMM#>>El&y)PEKaHa?IKBV;Bw7X^pdn^6Gk(JL)fl#!q=(F2inxLp;QthKg zs^dm->r;Z?F^CLLXh8-{fp!%|F($bQ^H>m8fwt{gFBWu7OYoh1{R7xh!Z11%66%ms z5?Pe8m;@<#`99=4Fg1kpGF+xow5LIam@+veVNEE67LGI~y$=|tF@t1koqOTlkO%I~ z;J6+(c6C_}CUc2$&PEerz~~;dfCP=w0}wEiVq~=n!$$ZV;;H31loeKsB((QoNaxBp zD}xkk97&}CuOPA_kHlP8E-|VK3Nvs(hDmp)GTCL63KvHFsD*`Nv_WM>Ado=@2tkbS z>prt=dz_ZWNNJ4^iHM@{5g#FCk(u^h4aQ|USJgkOZ1IQ^6q|CjuCe}o_ZH~;dJ zekXs|*Z)zvZmpkKn^)bjVZXUh?n_gQmDZZ%1InlYtSUnp??r`RwI+rhrIQS0v?gay zvtF}Y_Z%KCxpjETt-(Zox^BohQ)I?|lnz!ep{ z_ub3sd``bQWBc-q(RfU|K4tUHR}t1ntXHR$qfPd2%{kaVqg}TgAJ4h9cfkJsfw02P zmeQD}Gz6dw5Z3bzhq|rS7Q@SH9@e4sYH1!RY<#>cUW1Lum!%WR0RM%tb0hO!5-6T1&L1QjF?~ zt*vc#b}n({%4II?Y%`rqna;-Cao1(WV~t8J(ThwYhMu-*>DrzrpL~-4`^SHccYpXg zdnapN`hvUo(l@`DgM&l<&Ch>?SG?qYUiYdOv442VFTeLu?zuAKkABH#u|3Vi_K3FY zXx1%@RpjWjFq^v2N z!5G<~WF&1TW!Vy-6+R5x2Pf0boHV^h(qTv#U{YF))pTBptV%0tt0=8}jeU{=0TdyE zAyH^BW;mx&sN)ME&6pOau|_mND@%fi=;wpDQrSgG(M5COiqoF zqFxQtc*Q_DkAbQ%)P+0TrS+aJ22Neia^^|nENNGE_~9gO*Hwz50AsI$O^6naQvvKU>B&F7@GLKh`nSQB!R4TqP)?!2M!t!z5_R{pGS`Olh; zoS<47HW!vE8mvOtDpnws&N*y3qN*oUWewV}>O9NUn$>DW+jYcVqLstB;X0o?H-x8@ zkVx2w6wktu$>J|^THv2K5yH8lR-}`nYfr=+QAXlx(*VxPF;Td|m^JuCv=)DyQlPUm z07}W$Q6s#d^XO5lwZv#ULx}03uv1EWTCzzsh%_jml#*gOL{#C(O0imZ;-5;HwiCj2 zOoL71t+SxA zkl=G3;d>Hc0ZyZTSAKl#}HgD-hGKl!u2_(?y{zv=6~ z8m*`woOEAq$=_NS{gSGzRU+e@8@}2>HsvKaBZMsQ*20mqnDCX-g9oPLbg|@UzT)P= zoLk4IoGcrbZI3aU%1Lur4rz#&w#IDjT%vZ;yiX<-mo8ssJl&wIYMk;I8qQA7_{gI- zdDjPTaneLyaNjlVy*lGpKJXMb4(5F2?|KoR_c?FmuDf@+yje0oTM17__ne(|9Napi z>m&ER{B`6QB_v>Z#N|8hWpeoe+Qlt$)+o2ZQ;&a~{k=J7iv`C=BCoo&x6l0egvHsC zuJ;#F_i^BKNY^3FMu^a-B$3R+mIDPvQNH)`9asPCuRrqmfBJVn`9pl!7ytLap>@~u z$EW6a-sADx^!WGx`d{$oH@=Qvde6f*?z#KUkH+9%6H~hC2d#e|tc&OL>nxjoG?~)D zxX?pEm2nDG3TQp}M6@9U=$a0N7jvHv^vwd_tubzdQkq*g5BTw4c$g<{o^sz^yL`p( zdjnTDG$D8%d-{Z{n+2P;XJ;a5Qt$roQ_Sac3TwD>?E%Wo`#3#0;p2}#!+Rdt=K~)< zqFJrjtPQo(Y;IH-B7`*>nbI$o{SZSb@&YqZu6r+Dh~D?&<48SDLseOvH4@pVwV3V^ zj)OL4i04eQEm1*=(HMktAbtsDblxe9{@m@6-6~Z0@Rxq!=bZic&;If!eeV9l5C7l1 z^{sE^)vtX`xBK8j|H+>ny-O)IA#q>Msfo?wX{1z^axMgs zGo>8xu^(RUptF#ML0?EdlvWqfgjq(KI9E`YRwPhK(MKtWjMhRFw_^0vh?#WVb|O#G zh@1k+)pu5-jlw6%;O~NLXx3fiwCP!NktQgbWN4zW0LY?>cS>Xf)2hHJS;3Dg$E+%F zO1=vDzGN|o_K~LZa{h<-PHpsHj+KVnDA*d8Y}OWoN97>XTIBDy12uXb5`j#YU==di zIFu>qvZlqb@R@m+XmJEpvP_z#*YwJgl*PCbt*lIDVn$=|~a zML|f4kOCoggdp3un4$Aad|2aqPw4$nP+wTB#RwieK6p~r^s#5rp3wV_zHjN4jZBWS zNSH#DEk=k#!_kX}qw5Cqc$X!)svVk<1bCP%EZfV1O4*?yH5h9t%7RHfVsm4I&DjNcp2x&Mns6-exNQsOOY2YBl0SYLRDUqu7EQbJxW$Rfr z9v?ETk1U#=W#>6vcPv{^ACnlMQ@-#tjY>nIpmv7sX~E{GWLy@^#s#xc$+Rk|3P}Vj ztRAAb6+Q|8)!G7;MGoeir5sYM1Q;riQ)LWVTby$^YcUuKt(g>-S)sXO>bO)XW-hZ? zYbG^pjte$w%d9S_okFW5DP0PR!uqnV-)8ICH@)z+Z}`6qdG!nLewfFec{R^^~QdE-gi28ILOJLgb!SLumpkd)n4Bstntk6{Yp4xIpEO)w1P@ zCk}XKZ^6e8I<|LrdEKjC#FN*L`1mu2^gi&=i(ka6Uh^8ZW`;s{xMD;xxgv7MWWiL7 zrW;Jgmd(p|6T1WYkZGERetiU3OgZD`C!S&N)&Z;Kg0uNChkG|UIoM})dP1{W5&9mh zhvYWo`K*!WwItoi_vpOo81NmGMe%`4moIUc-{5<@u@kU zZ#;e*A5T4Vldt;9FXy{{=!ZV>_J8|tKCDA{JuzN6rz#tz$uSFOL2I-K{^?`7SV8w* zOpa|Aq*8(93=%<@iWQ@v0B$&JfWFO+@2U#wRgef+i$MatU$R zkQlH^49=rMG{1#&vI!U>d97BGoMqLpJ;@g^u$*MW&<}ra8^ph%l*lTw)&s~(wkPLN zhDzx{f+1(fY5BLELBL*!6hfp~60_O+DCZ|cf{%nC?2*m~+K}i2top=h7imH!WR1_# zob(CWDCf2HiI6oZBc$Bd7bBq=S{LR|=yOrU;>s&tI2Vvv|f(K1rjQH4IOdz!y5mUy;HHy*D1S~jjjD_vt>0?KXErtzZO46`oi;pdxKO)2q zpFA#^- z=caM_(KXPu(scBVG$avljbd}|n6BMGA|IfAMxdFt98pg;siqrH)~rL~Xu0O-0y54Txh|BF1|lco&!suh2jaqh{{Wcs%@cP@SFPyFKhKJqJ1Jk3A&-tYgUpWWa7jbF{HUiVsi z=UBaYt2BRWRG9z48a2)XGsI{^_JQm@1Wyj)naK%I!!tD{+CH%Mk#(CmIcvFfbjJ1l zIoJ12xp{QP;n|WdCPtN&RK6iVV{=xqJu4ZNkU}JegfnpG{r9uEy}_i`C?)=><+A5p z?|X`Oec%SmHt>d5Kgg%Q>@H?{&D}c{+E_ku^MuztbT6-Y*}YV14XLM?UBwj@DfWyv zc5$N_lda1XCXm94q8?Mtb}7f3^nFj?M{YcG$kD+G&AMTJbj<#(J@)tZIXpaKK3}j} zx3pf8&?G)Lp^~~Cp?ltNFj$?C;aOQ04{u%G`MU4_mp}CL-}JSAhM)Plcl>6QT+bh$ zqT_jw$8YoF|NLh^%*P*jEWhjhAAQH`UV8sGwr%*^Df)+8Huw|=vU#RY5$$EIAM+4h z=b*HrH*I4ATcC?m>XD^i7cg?f-to-G9_Q+{9X2m*P){x~+FD}vZ=%O@KL7K_d{H&y zzTFAyqbH@1&w3wpJGLs```8g@ zZRCxwei3hc$z^7)V`DZYhsbiV#D{dTD!+7T3q>GeC|fA1hGyN-bb;i+hsfcgWz|OJ z%Z{Vjk}F$dE^khVTb}7?gw>k9TT1d(;XpyxwrFipbwyB_s088_fy!7^%*5zz^6~SX zwl99gw*S6G7yc3Cli&Mi|7U*U2fpi*I)DHAM}7hTRgYG`^v);#)n~u%pX{cWh1Qfkl^3(F*BDD^-(9{^z?CfblB zdRGagEX0Io$mUnKB_mdgWJ((V$x}BBhG+%G4B$TDU=fqZe*Pc!{ySc?>@3fOua$Oq zcfx!f0oL~mVm~4y<Q?8fu5jwaH}1H?_s81rX?bS8vBwrPZfgC0r@N}osZ-~ickR8`^E~%`U%f=f zUn3)3*tssAvqgFt_5UHz0!v|Ii?}5Tf5+$Q_!V&O=B~28mF;qp1 zG6`v-(E@^VR8{P{8yRCsqhs6C&vUZeAZ4UdXG-IJER;kl@y+K< zu5PHMBJKAX_HvRz%A!z|nWC;zdWmLPET|2Xbs)Ej-dr(j8m4P4O%i<^R)8X)&C2r; z`42&Y(g97BUjSvDC>L`n{H6m0976jqZvKLd7M2nOOF4PdB9IN#X?<%w!P`52EM2}1e1wlxh zQ#fCNNDvsjH&|QZoW;3{;ACei25;lE7H@EP7WD)ndNgfJee*s}D=gp;I2#Yzt}8xT zOJsXEK~o2s0&GNTM2S*>{!%fTCFEWsO;;$&$f+<%N|Fy5j5p{H#&{`NRuzjy$!xM@ zI$hG%6-tIK{Gl_ex4wfi(iOkaw?@ZPLO6UpamMbh_c3LzgTnb>F&&ZHDQJD5aW?WS zIwlVO87Y>OF#v-ODq2+0l&n^5L@AJ+el(WS9fa!czN)e6S(#Ed4k$5MNf;df) zNd#_o34yYcGd9*zI2S`XgkaG)j5k!aiHIZ_86ei$6IiDY8tJ;iOGwHl$|xJ_aCq`G zVVvfOpb$#ql|tGU5j-M@*wpAJG_9m=EejjEZ*rrh@V^jW7@lxhtaf6M?HI^oHkvfUg>)&XDPdUcS)*C_yUt@NwJLSRadU; za`oyi2m8m&=JOb#9HNU-$=GqRo)|FE`IoGZg>g(kkJ;#HmOMOKU;nFbd+fryzT!o9 z^T7um{zE@!U0vDo*lg1 zCRpLUMc_Nh+%uW>) z$lNmtHjpSms%1w6@AB1yKq=K7TEj_!V!iJ)-+|iNF6Oz5`Xur*==>*IN{o&Ni%wiU4OC4-Q8lsK?tP@Rx6vSOWF&(}xs{DZMCWnVQdVs&N`s6N!VtT`1c{cB zEbAbt@PjC#)?JauFw8nMpceW=6k1U~O}gt({Y>ZEd5{ zl)7%HijsP{q%0QHRf%<;Wo0SK7H>OtP6*iGsja1H+gP;55fwsok+3eC(@LUcKuI4%F}i!ks*ez( z)Z?@|GjE-`p4gQ{{v5+zv&MDTfhBV|JnEG^Pl&1Joan9&;R;A-v5f7 zp8VlKD*wAgg-szk;H!!}9o0?DjSM77yygjU>=bG7HP{LnqCaIaE!f?g^7QU8S2{+= ztSC`R($5kS1(}fydkJd;!`e8dmnnL^jJ2&(Y;O(O*cdV1zJcZBkm>#*M@I`TUzzg2 zV+VZru?eMczdZ@lmMe9`y*1i$>fU;jnreD-6`**gHTBzBBVr#+6+qNqQuZD% z+2eov#owo{8*V+b#^=2FHXgZfh4(ygnWAw#@75dniZ|WC`BN$5tsBr<^3*3k!qqDi z>c%o&@3D1y2NyK^2U8Y>XE`~bv9YLH6qdtDMb&y-h#mX7^%PZ$5-`kCHb*%-;{jXa zoXw39gI*7#qS@2gs5Lc4b?k#UEij50?WlB=D+q;2Gfa|zk1@TCwVz&8_QxwT`uTNH zPQK+Qe(tmKTE6*nUdhcbf30}vx8L@>YB~F|vMm2x(=@k9(6LJ^P)f$gWFHNq{XB`r za2b03+r@*b*gZ`~qngl-zt@mwO^Hh#hwUm1fJtOL3PA1MP;>3DDv@$i;t6(>I7nbaXAAx$*F zMLCov%G*?;aZ*RXph@ZX2NrC`h8mxX_%WA^G4?LKCQn<-7o4ViYfu)*fTqDOzV_=~*DktDLJlqkpWX zq=+Qx#x8K7A$Si?(7Gm`XFW9461+jHAyqqzVF*$nbQX)4w(h`z4Q=@ug@_X;a;v6@ z)7!QTv{k1K_mD3Yqj^Hl>PR5hvG`WnkR&OC!I0i?Og{$C%x4Q`lL@owl*MAf zvM4*iU2I-B7YlIj16m4f@H9ch!p}!OlIS3O*7LP7j zDM9GsXXDqs@?*uKMu^xr5HiX@>7sLEku5tZX!KqA?zoW{P1ePmYo%}q+RmtM>j)6d z6G5UBRo%qB&~YAGA3=kWZ{j)$Xl!n1B?yStq{(A>p8eDQc>MDR@BY~3oiBVVzw+zv z_%FI2|Kd;n4K5s(<5Ri+%7GEzW+Zoq5WQ~B+7qdwZ5zCesa&Gl|9E0#ZU~q}10L@M zRnu}jnR9vfh%5WYT;7{-G_NRIi_(%jiH(s|OA;d(k9v&8Lq>TZ%M@wWBkK>zR7;W= z`s-(L&610cJj}u2k|(b%x&O&2{lS1Qeap+)7!BAxF8H^<{rgO(Gd};7H}kxkwwWz0 zx7~7@m%Qvn40M)Xe5B+h@;{$>DB0G%X{udk24^Y8!Y_y5}W z|Lq?R4?O(%XYa6deLbt|`o!Zow4S=M&*F&(IRE??J$mKxm8TYE^UAt)>#c*T4HT`T ztUE&^f;=${@`T(d2B{`bOcy=cnT11?<7fj(qER}dZ6)40di`E!##BgQ@xDds3?&@p z@kJ7&=&j#~5Q6QE0n+sN)Kdox`WfrvF%Ler%hLyQZa%lepML90Ilp5_Gz_wU5iM1@ zWHGOhN|NOX#j>JYELhvxL5WywI5f^n>Z*`|NXq=zWWYO-Tb`wmy6l2YpJF_gw59Zt=4(zeAh|s zZsM)$CR^P^!um)aZoS15V)BmYlD(wr4(0Jc5JD_AN*5??pm8xtstwUoAyrJ<(lT-c zKp?s#AK#^u*lv=xiau;QFjO>2cgzhRAJj_3Lr~-5--|4hXCy4vMafCs6_-lNnEK-4 z=arqmB$|~ybrZcJo*2FVyzNZM1q@#6PY}<4PAxqFEh7I_Z%xoY@VhWV2u(rmx z7L#nniIsEsVB+D)t)eVFpiv^lIY-@2x~Y|plZ$qV5C*NsaXQh~wCw`x8>C1GK_f&; z-Ig@Ai3dyD;(V0k$&e#NLenTT{m$$SqGX{Qx~45 z3^YxM|1YAS!@D@;7|jwk78!jKk|Z)%k|ZU~dgOxH2Z?@8qT#EWr7dzJ!>mZ&bt zj(GoyDN!A}r)YfK-yu$feYe+E_ZZ)OEfzr#QR-D%8$(QpO^m9I_ge%w?+LQ~s$6VY!X7^~p@uH$}o-{S2CKeroEXu3; zxn_H3%-Z@osex2O-p?7YZD3@atR?<*5eEl*?CXO)EZmc+iY&0W;hxZ+x1ns+k{MX76CZ#~-@FQx`7ryB~Uji~Dmg=d7)d8Rde-WP$UrdHznEbCmNVt~~W5J_y#<@;IhVw+YTNog8vJtywNgoNZz< zOYNwdn9kDrC|mG8kR_V+LC)s5&(8XQwb77`@tEPDk5Llu8eHp0^E7tU1ziNKA~e|_yK%lNECXHSXLrB{VrGA* zvkfAeS3~e5C$W~{L_)Oc1b2iETyQ6u>qe?*aF;-ExDdOOiI6xE;fJPmSx^hj4VD0C?n7&pc8>f1iG7em?WTejNsK;qLfFesGAl( zO5>!Kq{DTT76{?cN+W~vCS!PiKYp;Rv# z(VP7kyQ}(L5v1|10d(Yo1c~bi<^+lJvH0-5#k&xtk3d^5ag9GIeq9iq^h|)t@m|JB zMs2C*MTFpHN&N$@19All1j%ZPmN1?c^`+Q zs9I)|8S~kM`E1H^wxFu3Zc5Ni?t<@%vUtdDTiX@xp}Y2Ugn>$r>P+8NT}NpYTpZ&*ny z>C*6&gv3Z779#{7A&y;QpzIEcS)#l6-WbT?Lck~$eO*#TZiWjdlMzn@eX6dYg_zJ+ zweh|X{USczmz2bZD8rMon_BqLH4UC&d^BzJmiVqY;eBj)NGWZaB~NN&ep>6~m$vSH z`9rg(9}U0xzW;Jb&j0#--+&MFgBO3gmxymOM&7FeQpweHRCMw~LDMYb1QJDJ5^NiR zqC$F{vq%veWXqyvGFx)(Xu_4fIoFOC?9U1oRrH7?CMJOOQbRA(jCzVJGh~@zZD`2z zl%{bg;n~?9ljkYLvLdJy*Hm1(bVTC>h0l1~?>&Tbj#u5jPHh99x-#STbL)KZ=Y0+z zefUX!{og-GZX|Df@p(S~jnC(n^E;e6yGegAtmn3z@K{aM%Z}u^MB>SCmyfs#r(&F zx6hLVl3bAJ_z7DvqIC8~Ftww+-YSE&{ywyx+Wp1W@zvUkm}dt8ub8E<{fJ)Bw7y!WF|asQ{U z5xnQ@MvphX>IK|++ZJ1=Pt(jUu{qwP*H0*lisStQ4v!Wr${A^DNQ`7-eZa=%CW}Qu z+tk#pV?LP@f@fJfWNJDOOH13@IKFQhLhDe@Vr)&aHi(CEA+gR;Ei7eO;H)L<<)m2} z3BFB5@Sd#KM<#}*ZAp@pEbB=nxYvvNCyHkNhF`C?f8rO!pZLh1{w%$w?|R<{;e8(r z-}OD;`S5M`-1F&w`6EB{bGEi`TFz%*;=FyGwaxj~2H`qMkqDwQdD0QTz}#MyX=GAbIlS6l-nVoYMy zcIjN+F$*q6unKV^O$i}T5(}irQnMsNVmjYSkb;0h5hpUOjnKA4sYvh+K9ZnA=;EU# zWeY}oa-(QlPib3P7rIoSSd`=mIO5k(xENEKY6U^Z18p6xp209Dk(wkQVbT<>6Y_LHT`yP^lDe*G+Cb7O@-n4tH1lCg zIdC+c#Kd`lcA#Crd5KBl)Ie#E(qK#{zmgK~0?LDQ;Beh*YJr;E^y%l@w7#IME>ag) zP-%+E`kh1ug!c#>&MioE4<&m9AxN_w%4SYgFR^V&Xj^115I$ZLLKs$|xDhm{P$H$Z z0qf@|nIP$5-3;%bsh4=W>ZErDa3OJC;zA%`@J`{Z#5+wfKcFof)`@5)@0c8}D>(3w z){^nU&~FT>hy|rK24fP8PS7S{Fc>l3*rqpHBbbz;@hlc4^ZAU~$&>4*0z%SF-r5tX)QYjw zc3cCaB!+lzvIJ_pg8cvm`#^)%?g%v+ck}1ftYH^Fq5Q-AWs#;z9!2P`n{CiV2IN3S)|pH>158; zt5cRm!_`U6Lzfo}`aRC=Y>=gf<5|nolZLwWl&xTGJi;yaxcl6I2X;n0^7J7eeB^+8 z?`?R|D{iO1KEju~B#A<+l;O^c@jj4fPe2o7!lE`@x_X7ltYkKwvcGr0m8-j4+udV2 znNk$xiNw%$g$BezT?o-7uyw|ARt+m~A&@1>w3k=!&EC09Y~Er0EyCFciDlCy7let zO?FP7Vry%Q;dsn&FkowI$k`iDllS`sZ&7A|kb?P@kD)0zKAcb#b?p48b)J0skbm$m z{~bH)J>GKPja<2Uz|a2n!yL?82K^joTW&bL!GHUCcXIQo9^-)~?~kdf0w?>-7A+5a z?Bo3A`!6!cG`F1|kfoBra7daZ5IkkoFqtm7c2F=|woxk3NqUN^MF_zlOV}9pIJF+9 zA7{4K7!Uj8xru2{O&z0%JrO`CC1q9N8XF}u-9uhT8553##wbJWERAguLVPj^^OIH5 z|Hafc$N$5R{M2Xt_5BlXd=*mZY_@;t-sQY_y>srrEsE-S-m^~dQYq2to+FVs0`+1{ zuIo~AV(e=yM&e{7bTW+9%|XO2X(y-hZZ!#sAWEU4uOh~-c5)!%v`B;UL4TzS7Tuvaf)3+bW1}QWb+F8~ zjnj}si!R+Kf(N5#B~C4a@WA_>1kRrqRmS{{DfR5cwTgEgSX&`9YwjcyS z+myK0Vx5TITNFVkyjM6Ua8BU8!Ml{Uu~f?$ZR4?ZCnc=o8v#@+7?p-mWyq}}F`C3A z7;VVX3~ds0l9KlZ^v4^R{+JfUvS_J`ie)ipI-OD#OBO{%S=F>Q9;96-TMWT>%n^T* z+|;^A?mm&f1y9??cxcz{Whqv^3p(!xU1VrDxdx?(y64rwOsFn!;bf|}Y9c)IchZiL zal%6Au3w{LLKdOp|8%~kIHBkyir$^*?OPuWy%52Hp-X*=5;EDzDZ|Ocl+FVri4eQg z=$P0PBIHnX&UV+)%%zOiZ|IEf-be3T2%dJ8(Je&BE{d;h)A2qU>pJ!YAY~9j9h)@! zkd*qT25al@{i=WPQx|{r`@WTb_gnw@!5*Kve(^`XgC{1{`1XzT%X^9Ww$#WM1R@wQ zni2x8^)ZFbTb#4lwxVfTl+w|d9|FS1#)$QSvT8Y+EZ9Guv3Fdse>~%GK4)GwxBz*c zbW!JuJk#_ItPPVmZOSxj;~vBH9eSBUhb6&V(mbcBN)8T|98D^AClw!l^oVP-nh*lF zogVRmJJ0j!u&dB##cqqrdujU+~6Xd*8c15FYsSlb`ix=JoYQV_h#ip5yD~uXq`o zH{T|n{>TSEZ#kR%xNY0#N2rtOF8Zj!R5B)p^wOB#qh&w~VnJdIMl06FL$w{p3FX z@aNvmt>;d0-}7&#w1Q7Rb%knv#23EiJTJIoi*i|lfWg{2i^B1)4?fPn`S(w-F--WJ z=byq5yYNCuhJy@k6tj87)%_*&!qT+y09vl{!#l%sZZvCyj8hvUPH(QUwKirn(2V*S z{eG{LAFPP87S}jz8!$Qn5z~K^Rv42Yl_Jmkw8B$2H8_Vr6jji_S_J*0A1&8EdZQ`) ze<}6+|89N3=e`D|wK}|ZrQbSU+$@VPsH*an-n*B2XGbgj?20Y0I>bamdz5MEn2{S} zL3Q*E5PTQWDdGcmW!@m_|GkgXjfkc6CkOtpGNaol!>U?OZ6oLkVq!_x$RslgNi1em zC;t#c*@h3@flNkEhmx|J)WzstM3i5}K&)OfgcCnsFynIpBr ziFp6;A`OLBDIQ>GY_@~>m=SQyh~~7nzm{p z`=trk%HusK7tQ0trDo_gy~K1>a6_KuBuPS+WtcQaXMOVi2$K&ey<$4AIXa#&nM|0? z7nH>^@_gEsrXyhc=pN`MNl_A~jXsem%1)Q=x+#Ycf}(6#metAi;yRF}4V|er5TsUh zV{O_g8c}dkJgg|qTExPf5Kane(;2>_WF<NH>rbRf@?Mh{fg5hQZEuW=_F(?^Glx1yw#Bel5 zO4&gop)MP)UfJaX5AE~hUdapYy^GOsz=hoYH*xDM=ww}qXMwyhsXh?s!O;Hpa9#tG3RV)+@h|O>;S%?ljNcG&Gm18_2<3j9Zx*; zM0oe_T~DuiF0bq6^5^)va``G}`kL2%-8VitdF+#q4O8*rT!&MGR5Q#JsS)&3$uLtS zMxJ;)A|FB0di6|u1$p+IU}>nMu_Mmdy8V%Mj;0eSE6wnC+wgy89=aBgS7 z`qp+dpR0@y-v0?wzM;CzwvASm)76!eBT=1W%e}K#W^es^c&yN!7vZ%BG>HYwEh8wKhfw zdx3AFfzkVbZ5o=|MHhpJBxfaKchEV9M?91HKQAc@iwnvG@9y<>`Nl0Kg1UU{((AsV z`qEe2&PPA+UuHi4%=PF~PxHtV7sAumj_Mb_^u-r`^ur(j?ce)_pM6Ku)W0KC@<}07 z(pZ~VkFh@Jw(Hu4&Ys=XI&Q#AUdjb0$nIKGt&U%>s7?BMT z$pD=Xse@sZ5Tm(+#o88YBOx5$6&BV- z|ATW;awNiu&msaqV?3xAxHQjxuGi1MPb&S}S)MPgbEfqHhe#VAWZO+4YS$Ui9n>Kf z?A7Yc1xg2{4HPy|+8E`$Xadv5GpRkZ+EX}KHX;7H4b0p4=hHfHR61sLplG0MBUH9* ze79$zw$TjVy47SP0tj0NRpTNjL#X&#+c*tv+CXbVmwY9=zvrl}qi)^FJ*Kjjs*P_& z2PmvS%)J1%@2^GI*kR7`ua(=Xy zwARu(m=qQJ$8)aiA8_Sx#?_-a2eTzb(_*wFPZe50FV*x?MZcfW&lJ5(lckz0RV1k* z>93I*81xddEXVXVk=YJq6ZqICALstZjybcv##>)?54WG&V%X1k{OLWOzBa>FOI~ox zn48ZINlhS24QFn=h4COIkuAMkaq8x~@YYf;BKKpkaf-FAGo*txhFdqYTr9bG@qqoK zn%Sb@@bHMM*RFBt@-A0*_c%B{rmE^L*)VjGtn{_A>(16vp4QA z80JU`Wl>NU3z9_88*UNWC2ety<>Y{}YG^DR9nSfwfAb-J=fjVqMBwy#&XvQ0W$PLA zbLy(*meV7?^bNPMHPF20V^8yr`>(LEKH|&XbT8{^KpV|?`!stOA7gj-kg{kHO3~|Q zpn5plFqZ|$9V~^hEQXe<@9>(=7#=56N`nj@Z4Bu+Ln@1s zuJeJc480zyd4>fOIy$?GKPzPfDp?t^bA-qdLgIaivlTK3jLvYj0<8gs_YK|4PQc@u zy8DC*9~?nwTw@W^U~Pl9G7`I6A0dD);GD#h;GDoV7TZSXoVN;VJ#F3KYKv?GX)PJm znqgzegzhFBamo>dLYb5_AJ8AJ(I1XTvtHx`w3c~MGo8*^EEiPEg0`+`o4QL^3Y`Q| zAcXHG4dTSJ(OQ3!tQ6D5R^AQYQPkt#4-efnDQh-FVBAJ><#C>8^qq{f5{i} zTfgy}*;7wFovn@6?p!WrpF3YH?@&rT-#K?r+u9+W240BO<+=K=h*J{z%p+WOGOg9G zl>npY66EM2rQ`cn*Zj%UK|GU2r$oFSj8-Qf3!(eBR*}{5W4!m+?hQdV0VPh>;N<-) zX36R~vr6Dv$*7c)Bu3X3`3NE0KNycbYIO3p&F!trzw$fpuYdWIyL{EXXFjX<$M5`> zZ^ZhbUv@5i!zeZ1s)B!ufGoy9`UuUU-^R)HeVJWE#5&8vGu>wR}kaAGLeN%G+K&i1$d^`HFP@A~=w z@Bb0r{4z35I}{*t>nn$G#a{huUM zQ|>)K;+-G8!lIRY@f+^tj;+9WeaOby=YgHGf9VM>@6HKk#N^9j>+-yw#l;3^KJ9Ws;YK;2qRz^e9$X>`sxl5HT&?4cDIv%@EJ!TK^Ga@B|vqP z5$jhx2hkM|G2GzfP!pbs{9Gjlt%?xBXC$7i-hc9y#1-|zbPsdU6-}~rfw}d5nizAG zWZ7drgb&tb_38Ds(QJEXYxnTl-W$v1@&{aNw~UUmLL~*cQS=f;ny60dB``+Egsx6{ z6+8MdH7L=FVXmXRMd;3};ydJlpl)M=k1+-<1^q#f@!C3h-XqV_D0d1Dg(bL(a=D~h zENSY3wl1-@#(7I?BiORWvrH`Wfu-tsoQTsBsRXJkw6p=!@4O(1LK%fp7Abv%or(B% zQh0PfZ=peF3aJH&*??g1p}^aM-~!+x-#$3TN zeK?7HmBhj~Mq00mlvP}G+{XQW_VYKMKmT1%Jo@O>N!k8iuFWra>zm2)TwQ$f@f)vP zzI4>I1nQ?mcWnetnguJV zrRq`-Rp-yLolHs+B~qemICQfhR99557AeBfvKr+@FiJHM8jZ^{&3tAxCvtyG&WHgo*w&*N_;E?ozC@6tNKY5f-S0B6vsLHY_U3@vPv={xMhg=Uh3OGg%gtb&FDQ zf+|GEGD)I8Cr>py8!2mR1F~M9exA_l4>0+d+;}q6lBJqrG2_yuJsx=Uh!1@FfV@Az zdAN3P%xmvCO|LiNo%cV=y|yCZNbj zVpmMM&U?2i!dEMF4443zB>CugbL+eQ^}B!dcfS5leH|b8$b)|{ulM!!M{Zp&JpP!i zFZqhE=2w5=U-9C5p7+Y-@xhO&U|*9M6=O#g9WNmglZ{rf3o2ztjg1Dy%t$h0NV1H< zXvoga4r`n14A(XojAGpG=Ei{Dz(DXMiA0Kc!)}TNPd)Y|HYk+o^VA~`M+sDtlCN#> z;KNVzOaJcu+FVL0qjRt;s* zFk7^2oV$(rd`?waj`y!In-y`&;XK+X>e@10#^~K;6FXJ`D5@4I1cN+feb{4T7^fcN z(U9?=N59{T@s?TrRK0BvR>xVw^;8)LP^B->8_H?2|=go-S+(dVUh6ml5%TsO0(#UDhI?~Xkh|(;h zB)!C-r9)sbQeuqmKywnK4bHb@SxUb@B+YXA!#-J_l9)XDPeh<;D~jcUvY68pbLw)A zYigP%PCr)U_BvRK#IfjG%FN+Zh{F~SN-L7FLg{Flms+BAK`1cb?o zV-vdQS4ZQbED1sA_41!Rcf$>TRS0qAqYr-SKYD$>;Z-l==38#z!3Q76FI~Qp49Dwd z@b1;~>FlnuZcb@q?j-n|owXa@g(Suv$7w}OTI)PYtBGKD(X5Q_l5Sce0;~4H>d&R% z1TN^qD)u+}#Qx6{7PXo{`jz)%b=9s4k(G}_AeAb$HkX2TA4{|BVww*g_K5dr@2>3J za(mfSrTz8ae$OB1eeYlVz;|#|hTeMWzGQ5|x8_E^+~buC9udISmbPgLL12T63V_Ce zm!xTeOyZO=L~UW@Z5T~mRUA)dT-~2=b$`mG{UZ*h1;_J(woC0xbo}`xMlH@QBG|L2KI+Uz3X$#%EjyoXByztdPiS>Gool zCBJ{_^y%;T_@^I#|2O=lui>Bmv!DF~eci9GKU(X0;qk|CebJwIBX9f7ckzao-u;qt zy8rj__DxC(p@|#=-ymTlwh^XBf3!)lESOJ^5j2c8&r$n?Vm_r@Oqd<- zb9Hw{8#KjYLECz~cT|mMQ8}#jCx?y3I_f&|ABMe*jZvTVL7(;EkWs%+KR2k3?}36K z0=~6mc}|cqQdWdWiysaRc^5g`x~N|_iHOp(G{qsNMPq+uQj34|NW1>gm+B|o*Z=S@ zKik`b&wbsi5kjWR+2s6UIyt?V&HH8Dpz3fBS21|Brs3-}p`6$O~WgV)?`0_kCZnTrPj2 zZQIjEb<(QTAVXvr^ixBe48{s8OH#BpXceNty3;X-?r<%FCDjrmbxfJk0cRCT8A51S z8}><(jH+!J3FP96Nrlf6c2L^OF>#@R7X-mn$k#7;9atd5PR-Swb{YsIw ziX=pWbE*}oi7XCb5_FQ0^#|m6k0kAp_xng~C|k#}wk(zfv)P>GVn$soajlKgr4T(C zwsSvwA7PEw)4}&5OQx#ZE^0dZS5{zU-;EVQ=Xq*u^f9bL2wET0nOqPy%k!Tm+@#wap7{+n#RPwiklv zDIo@0kPCrc$-+Vx!myIiu4GuNAcZ)sq?1#HNG@M}MCg+I#AonA=n~~(!5*Ykjq|?n z-cOPwSteN6Aq5& zJbiVK-NPx@j^`|^8U&;!MhGWLkfoY_Kck;Y^1+DF#wM9^wACCXETdtDZ(E8*$*I$8 zoWJ!pa?{Y(6<4lK_|@Nig!essz`3=IJJ0kvDjbDZ+;!I-+<3!j&hO-$-AIrs<^BgB z<^3POz!$ygrM&2lHG&$TrD8PHBw0={Yoz@RnrZ<;k){dbo!hv2=?ag0`VnTcg4uM= z-rhcY*Y>!2^%}eT2h8V7TKh~YTAY}!_D+;st&-YSVA<#y>w-Z{@;jTSPk;9(A9>>b z&wayd`Q3N_-XGdNxW4{qtm}oxAJf%@Kq3UMdC{HEJKEd(k>KnXY9*vmF=kXtb&|!M zrLk+;w3gb&Lze0?$2|g*WNdD%v9rCw+0&;vb$W;O^)&|lobAm4H{W&xX{xE~mc{WE zu3mVOwrS~)Ht;f|UK}#s*gy|&;`rbS7oNVtFa5jUV>X>~ZVV4UdBCTho^bxunEURz zh3TRs5hdr=4R_qU!Sn8YAv#}Ye!PdU3slm>x|U)-XMb-(RTdO3!#QZ1is`&&I$cn; zlG$QOQPweu!*e2)Xlyi@_A`riCI`GFt!(Pw*${0HkR zzw*E3_UFGqe)v7_GJBUV>*ai+jwVxCRF?BMoj-51-n4CVCU`rpt9rwGp8}z2ZL%z@ zzV%_H0*Oi}5qqs#>ncr5Nq4r@T9-+h9!n`3lbEX^h--WM`xk9p+q{?g@o4Bz?QGlE zy!Mst>t6FJUle8d%0K^?|FgfZm%iX`?zrRm@&mvBu0J)O7e7(g&5g42eB{P3$PN8W zlPeu3e_D{{2CWs|T7nB08Dip*b=VLHLUd&HG_pl}gBG%rp$W3okfa)^JXzAm=str% zgbil>oV-6EOA?F#?;48bgxO@uaxtS?ENPpH#x}H#jg)WKc2F)yBOT?yQw=?~AEUNn z1aOqrNG*`cN5G^O;0uCnQCgsT3EE_6oyAC0=?K0c`1Toog;sHLCnUkuvGDNGd>w4S z+sM=iUdHZc;X!rAb2@Wr>v3)9sO%Eo@la$Cm{O821^H5v21P0rsZk`_M0rY@kY_nr z-beQa=(LA2DcYn2olsg&RXK`9#bmxi>#W za6&AR<8>XulMtIPE);sI3`tBdI@BYPq!vFS@KlF!n%L3wL_Zwfc#rvLI-nf?OugkRf zdZWZm&RZc|Y;QEJk430$5H1Fg*tP))Nt%!tiN+zk1uKx+&^pJWtT~=9*`LhWJDjn5 zIA`~0!tuO}@=6swTw2AXv0kQ0jU-DITU+bo!x4ERP`*U>HW;mM(I1QnKr55{-3^-Mtc}0&Ayl!g))-p?`RPCT7r6GDf3yma=jxKctvBp^ z>3qKYiK?n^7M-qa6*mj<_lc6^iH_HUgw(_WCDC0Pl~903tN1xnqq~A5prq;^&{3+^ z;?PNokr~=(RFaVQ2jsmt0qON~`n{A?c|_Y#7fWW-W9q7)X-djvL0wnawkG&Uk8heb z79he==TP+pbzkBVea?Nj^a8jx?_%h#G{AfIFe^q3v}0F;pRv zt{uV0#4T?_^lLaG7*NFtL2v=rcIjI#{yxNU7kE%jEHrwBBxxk6kKPKURVR5fm^3BL zbNYh;gWizAa72GJB+GIvA|@}nKvh;0%O%BPPEjnV$|?dwTT9VcmQ_pJMYdLri=GYd zyC~P_+X+Olrk2r@3Bg>$|lS6e}4AH^MC8p4?cKp zYM<>L4zGLdD*;kqoe;!)HXXH1n<50ZZFOCheILOKxVAA`DW|peqBC2WB(2jdE0vU( zG%d3Ja4Mxd3?ca6{r7*gy4Kro`g-=>`QP!s{lL!-$C>z|Q6l~aEyXLeQi+nCLA)s9 zJ-X}63n{3J*o10Z8{?0GEJ;>?B7BIcj7Rji->Ir7Q~4#IU)&&id9_w5SoGAs=or>cOb5Nb@0NJM2F3Nj~_% zC4S?B7rAn{Br^biL4dxRG|zd(i*Dm9KL0grY;7ajeI9@8G5*2Nybq%!%f*r}dgF_D z<;z~ccxcEnK~aKA5=P@bYg=b9Nssw#&i>w<>9k_8STdbX+3zMD`}+q>rxTWCO5yFkS2=Y1=a@=ExWXzK-)UfMA6F(!pDbnmPj_% zM~v6k8LyvW`}8T+*2naE8U10-sZ(oYy@b81PvdRP*6ABbdh0lAvDG2I+$RJ7b&_`oh^Q}DXSM5Elv&# zN<@A_qE@M1iZszNG=M0f&`QQBn01lE(fT+!Nn?zZQ7WcS5w-DJL7r%&3>a+?LXu|* zBq`{e{&*9aWT-SJPc_1~*s477dz8y5wl1+%MbktGp|uURZL!wH#I@k5l#f%8o}|qJ zA<+>OD12ln$QCKPKcfNXQAQ(FK*+8uDMBnXLj1fS5Hd0v2;yY&;VlFwP9&Ktm0hr$ za3lnvogk?sX(8z^C7F{XQlfO1sH7s~)FcT>*2822`h!u_#P>(&ULTocI23Ii4{(dc zg86bmHDAz{C3RKCF183rJc$q(3D&k0bxUny{Br9YN=h2*sVvl9P&S@Ly-IECv=>e>=eZqn~*6+1}oNtkz%uu5V#hS@oJ5lUJ|z z^fzZlet{$mR}(CO#I}y6X{hQNp=I>v5opUAoTaU6T4zyOlBH?%f3%LWb?i@O>>V%I zpDx%vnsYc^FqxOs*2bosiHwrWX!^Nk)KBR3GupaD2*uhsbjz}inXEU+_`PmMmbt}v`N|DoAS|*JV6x)pHJRPsE!`T>U&w9T${E!SV4i`+NIb zx^k8MYX?jwQ_7-@g+yl-S6wmcLKn^6`Fwnr_SU#~ZFRv4ZIYOu-PqjxYsc-MkOO6lrscXlwa$LD`%(AG-dpSBAa&U0W-ob=r<0Wkp$*l+}W^ zX{gGIwryhim~({SvBFd9K$8jTk;10nl_w~V5VDIZt|Qf4DWsMlqXte8^LIrDQ9E%e zhj;)9&g0t-v?gTyz2iiNA-q6(Fm;@6B()%EC8>z)10`dMo>8g`aL^3$9{u5vd@!az z7?Wf@NHRi_(s)5r2Np%id_Lpo_?YElfo*E$i-NMOV^!4oQ-Y8vCF5&DB-krL2di@( zyiZ1%R0qIpKZ7OH*m#I?A^N~<2Slq|cVasC0hHE@@v#4sr_Y@Fp=(#JEH3O#p6xCD z$7X%ocYha!73%djit{<`H{_bHPK~-*N`bR2&N5jr2mzu(JARA4dPN0>fq>pM)RbE?*G?dm>@WldES z9335TZFi5$SFW;q?U2cI%Djj)a_{0)D0EXo{~6QU5QuWi?s{{cE=pY0CQbhLyx;rV z*RJmEFWhx`)%A59Ab5QN|I6R~J4~vU8@BpIk|yspN+kwfs0gVfV5Gok5o0(*jEhaR zL$__Vr`Uy zEjZr0LN*v9+6hJpq*U0}V&#Z@IA%1=Nc&^bEG6rYVr1cbjv&y_6O4)pJ0bWE3ej~% z>z2k^y!U9W&_-R3*dFSo#E0@KM z!P>h-@M0Am>VrJt9<)ASyDneTbwI-ofL%69n5$ zI1mUYFts3?NqWbU!I7pnQ>2wbSrrR$84UzNN~B6aB_wH|{&1bO&C{&!oMX6sigdIA z>3~`oiYhRjESMfmm`@H^O!k=`9I>2C@a2-=YH}S&ML-enS*A-Xn^!IK+EaVQ(nbJZ z?FF@m$_G3WsbY$sF|iY_Rg{;C6PZ=~ex2)bazm7x7?T`~$D_ZuzOnuzRn?S_J-z#E zZ|6TY>$iUVTX4>wdH$OJ^8@Ywr_6-^Mg}$_Zw0)wG0?yXf{Vcn5J-%S#dOo6h~)9A z@zh>o(jHQ3n%1#@aKxqE11?-U;KH>dF6|w&H(9W#noe@5&=S%_Gwdbw(=MW3OPsaf zJ-tjLLPK!vNsP4)(XXhrps)#Vd-o@K{K92k@xq(A<)+h&$3uE~f(Q+h$(+TaWn*K= zt#{wc_W8RxbK8s9I{yN6IzT2lI$tN~F*@HMxEdXbD2eVA3_4pQ$=5LXh~=W@@}&c& zvx3EJ!T#PpmoH!8($iPCa^)J!`JAS%@Xn#T=;o9ALsw*k&xn_99i7xr5ZQ`q-tXuC zaD6=ZTh6-UYx9D?^LPHvv%OWWuRj9odf{<>ovbSdGoE+z4RzKZe1PDKrfFWV!*s6LvP& zdBHvRkPX%_d7rhl9{paz(cV7OX+c$4T-%W48S~=<7PEOIwHt-T;%&uzUI371I)1Iz z$Fvs_4glaD;x zTltU0`j>zAo4N147mr)#zI3SU4`q_SpoO?0crV+!rC64|V9#=kUI}R#ynSP9j1V7T{cr z(Qd7O#svErRP6}63lWIeCHA<$NnEb&4C)~W(k%Jt&Z+J1`h|D=#_#^p&-`ro-tWEM z)%}BBKmL8+OyPBM+LUidh5t*57GJ2P7)Yrxi9skLz_e`<-Xlbm1Ga69&9&C!VumnU zDi(`r!eDI9d)@fVM?&SlH zCKbzN!D2q+U~ivGmv*_jdyT{6DT~FDrnR`$L}MkPA{HHu?GmkgIPpOF&)_;qC7&Lw zt^J+5U-06ezI5?IbN_=6Kik{o`uZcbt`{EH*Z=MMk}v#x`lGS=^!>m01$9yU{ounL zTE(=EJTVxfBE?mJT^*!|Mp^HonL0}pS*lSoA)ZeqSRTuZD^YX!Nqu-wprpy5lS*U+M}`=^E%;EpLl>@e#ZxS^wKeJc-bA)WySkG zagiiVNfJY_4Y%&(yyl)QUiFH**gSh%Owdw2j-Gyy!-ENn`7)Aq5gZ;IvnZOVQI`ph zgt~5+98Z`nDyrI2TNfucp^M&aV~lE|B*VFkK45Eo$i}Ego@g`yoJC57QIYF` z2qb}MDo!;b1o9-MH9gQ0l}eP#0gXvb*Rd4@ERFNmiZc9iBjrE4G!GBHe0y1c@pu1= zXM4*$C)Zs!-w2R{`-bIg`d90^y&(`fSfC;^l2paR2gHMjP#UQJ75RK+aDQa6Ig~UjSBA`{2c^R!x#?bHg$n%upXn;=pm^33vGfbLA z&WH#EXDQ}Ws$$M^K4ZR^)6``&&$dmMhGlU!O8jC+KRgqY*|jxSfXL8M>lFggK_I&G zTMSL;zK_*@)lx+Uh5(@yNtTjkDZSo+;b0u2gNH+OZ;Z~@K&P~=r>$F>rlPKj*sU&> z%;ypQ*ES_hRZ-P7WnJN1^khYDK>QjT8(BRBACu#}?~=M8tWtM;ED8ydfQyhk@l2uE zbVZ5v(ZjIPwM%DTp#dS(yPle#o+Qb7*N0~>+SWLH377GG=OVgBb!tCQD+jYB?)>7X4ktg`ncqi}i004jhNklW% z&YWeqafb5f38qWI`l%bZ`pAdbzc!2ajWi=m6q}oUOqzl)h;FKG(mp0h5kivmx6sB= z76n(YOgPw|vMdTF$HyG(@3FVL$KL)v^VyWPj%*&;{al(hN=IX%9j}L``@i$CV7D$# z6^ZwzM>p2j{_3kf_swsg938c9d;4!b+gs-P`Xjim7arHw|KIi6m%fnq-v4o4aNCWq zw{`iWBG~%`vG^Ed8lxp5_^yj7QA(dkPFx5G5ztb^8xw-zV8HtN8XKFNY;T`tXJ?bq zcu1aQq-h{aJ^5gakP2rVll{vSlcNZ8aux(E7E4;&^3;`M-u|A4c;NAC+Q?T&Ws6}yCm)}}WMitjrEXfvCQuax zi=(SdkM{`(8Y`%+p{?f3ia>2)avY6z%VibGtX*DzV?A{p^D+ln!l<7y9;B@G6Sl^E zwgx#{<37DSrK)Qnpc73m$KX3HUJUq)kwv)E*JO9p)JlosmIk&#*zxfmFoI0)E^*itRlShXKKiD+Q3qpu= zXC)!&G~S7dUH&{Zq-hfWa(9qTlPKLo2oj@^A|~Ig4A22dYdv*qyVNY%!4)L}Vw`J; zrt~CH!veV-eWKt zk!1t2-UyTDpc7gzW7KZzsq2EOs%h$ia#>InOPZ#js+PE_#x^z1JDhjj)B(JV2cT&5 zj#J6_^@Nx_CWz)@_YBEW2!U;F_j3hj9j&$h0Z_EHo~DyjS>Nf|Js@Q<91VYFZ9Mq9 zjSWv{%JaaZm!9pd`kY-q^0&T|r)NRE{G5F8TBg3bm++N|QfGybG6auQDo*3PrK#&K z*dR*l2p-qAI2+JLQdcG31=`wEcM!?OMoHbWYB`)PI5?WKcRb@@wq&-fXu4)v+9eRC zM$^v}y+n{@hSA!H(O^K9fR>KgWQqqiPMv2oOc7G!yg-DOd1;x=3V!unpI}+FoZjfu zGoHg)#qpx$FaPP6^Lekmi_MK5qj8_02k8D;LI^zZ@%NK&KaVuk2$^uSdzrn7%B+h|2y9-*})$$Ds$(ptrIS~H!MOlNZ@$H!c`y36Iu*Vx}bVm_bKwiO{(?r~Za zx(R5AJ~Z1UxXI2u-gJD9F1ZS+)&0GG?>jDCJNox8x%X~9_Q~s$j_d3HeqAp-uCM>c z6++C{3;DaCI9Uu5K zpT2m+#(2P2z4gVM+f4cG4?fQ0PapB7`)(zXo(sDZKJd^k(`C)(TF#v}jQHZuy@$JQ zzcK2)eW0vbmdlcZ;|0@M!Sr}SlE~OGPI}bEg2JW*l`}corz~rxi;DT8qNv&~(JPXV zt&Q+NEhU+e^fSZOxX8~ZDz=chlh*F5z48+;2r47AqB9f&Ey)46M)zQu!by>13 z=FBEXl*KY~3)&hHT;vr*;0D3FC`)o4?=4bBA4TNqMLAgzQFbAf!37ngtdoR1ACjag zS(ei456JR7@;I^{LMM2k@Wk)2XDqA3gNB1))=s-RphsfvQKswnH0vTCumg|4v? zyhn5s7Ux3rbvTdGQRapa*v@BTT_+7$5yy!!pxsM@_nx}3xYgNqmO z_Xj_4^R2i3vk=1cUGMwQv%Ni^v+Lje$Y0?%9&gOc&Lwva6Y*#BM1Hx}@|J*?);a`= z6;)jbu(qNpYn&H&-vXY*7?f1en$@SS$+`Wku06>`&(GPZsPQ%{iPdIbKwh zO-lf@1g+wAohO<>t{LV|n#QtswBVtO6RsW= zoZlItvmrO0S>yD2!qsWuoxk@9mPN^5{?jk#t*?JRTRR&JMq`3Vk+cL`FOPK4v2X&`4s$z;RYn6LYj^ceoEF~W8+Lp5CMt9IauG=qH%&tdvm0e ze9lXr$LX~`630uQe=Cn(n({O6_&6Jb6hdHDTF#w2MV@6mborRs+aKU9ubgqisWEGP z#iD5Wq;}bUwy#KDh`G-piey*-x`sIJi z;}1O?%BKDJaBb}`dMW?M<$V6NA%va}p2|j@v*;3L+a~fZ(nO*}K(72y*2Q?*5NI1> z+-(SyjYVsPk+FCwtCmEq_DCR45}cQ4xhiZF&bI(~B-oa6xkNgcFXrgfFsS=zlVXxI zPC<+zO)?TAkcnYvHtF|A)a4RuTdcEG*{ z29xwidqYf`A#{RHbCfY%wm&$juu`yS1io}w*HBkA-dTcgaczyQOX}qU+g3DnOHoC+ zRogm(jSLhoVw`dOS;xWvAAgpCfK<`f(Y6-n;*=;B7^3@m&)T;oH*Rc zbR)#{G8fhCS(ZKA>-WCr<~wfxw@3T?{v+2J9e*I}hyTa#;L!>4S{v?p%RT+COojV1 zGPs+-%itZ(+9;E97Vp8ewnMSTpa(&mf>`S)>KddbOAIubGF(T9!18Dj zip!}#?~v>3KY6|W1-H=~jnD1x?tag5S^Nd?eWPUr>`56}0MOB%={owpO8|-|G8Y1g z)}(35aL{LKYm@UgoMvZdi>=KK`hyX9FQeBNY;13koUi=C*RnPqe_?(Soq_;gnedD6dVjVK79!pUS{ujn(fg6HHR^G8bHL_Wk8v+y+{+mZM|f!n4uW&&Bj}_Ma+_L3o-*;*F*eg#Rx^MbhKk;mD zpXbba<7;1mkYfDQ<4^qg`C{?c>e`-(VyS4LPPHO2(d^w%4N3%z?hfBRkmxw2z=c?t zC}>?Ys5=*vvl1;x1ZWM&Kq3tw2|;6nMHz|pg4SBHL}OIsgcuX0T}EkiVo)YU=nRu5 zqAnn#9=lt|I(!6o!kg;E0Bwy}$eB*`;mCs}fJg{=!j2q>Lky`x;t@vbHB<)r-~ zN!~}LeF75eqY1TX9ZlQNwk>l$wxn!2H^E6TE_Y3o>|d55=6 zmmUQQN!>Ph@37vTOf+Jl5D!R3$p|*Hak}HZ$0NI72rx#W5Lg@iSv# zuCq4o(;KaIMZTkHJbQabxYn_?J>uY?K=sbjUt2@_2|DS~I1wk0PILLnF_X!H`Fz22 zI^o*2J@&3$V}I{}!=nkaMM>)%z6+NSAx?H8ER^`X_Ho*W2;IaldS!g9^@}v`{qs|2 zZ}`E<@zLeSp1%5QZ=dVyzmj!5PWSryPhOw%&=F7mf4+B~-gw)4riVuh@7&8E^t6h} zJiW{?$TWGX809IO!;DccVUQX|c}8Xwsg6t#EdxddQWa2L*U|@pkP1YM@vO^&syHOe z5(cAnl0+f3W_{-dZag#MwXb|Bx7@J7#@Jv~1RCDBC3)RzUdmlJpJDIdkc*f0sH+&a zdDC`}x~lm2qq{7MnzLI&8lUm;NB0=^dwl*IUe4=ZaSz+$glk7Lp1wL^XQR(JvnXll z4L8xs5Q1YcS|{)2WLZX*8MJWG^eba`Oh}1R3hO+}+Onu(MCY`$9G8|!>8OLGcF@+2 zB$b#n7G*L7M6d{NF@z3U=c1%U1i&E`Ai=qo;4O&;BRyILX;j!sRd{_u^VXYs$HUrj zjaU7dqrdp3dwJ)(-}h{9qyOsHQ%_$ePm{X8zVRUy!bb(+7KJ-6MTk7#5D!AuI?CEo z)-l3Uh7h~4BKkw3p*bK?awCLYrMB7J|!CR^v9Uq7@4m_Iwq(dZBSITqir3wb-1R*x0cYhv{lJ+Ij306D2fGj zIj3IEXv-ySS>UP~SC;suB)A%HYh-XpAb5f8d>ui=L@*&_oC=Cqpi9xkb$by#Gd|D; zfs-0XWQ7DeNtTPA4cqD08`ni-x1QD$4dgaNFsd;JgTdg(&)jgsU;oG_9(nvFuXzpK|bm`!3B%BeG(80DEyCp>m(%I|#mB9C7_ z;?7&o@s?NJ$t_!&tzpWA-D9pEEV=8Z4Q{<*#N(F^IbH_d^y+(f#q)RQXM((!V~mc4 zckt*Wdi~NoLrG29NY31TZ)`9Khss9ux1L8?dM;mhnqpZ}EEg;$$IOlnINaZ3a(G0! znA6rZAvk0g51uGVYGgMB4AF0ez=f`OW0h_tuptnnJW8_s2kyM*?jO4F)Z+(FUOBkV z=(xWAlh^f$$MyC9dfoTD^W1#b-RAL6J@_@YuKy<9-I^yF({*xVq7C#?LzXC;9CCVt8S-k97bBuhL1mbKvOPw{Yy?W=%tK?0~V#F_5%cwXHqOn=JN$r zSyDAEi)G8AbS#QGChH(*9W2X+-~*{q^isp9m$I`lU}t^AT7S%Vl#}NYBT|f(qzRp| zIGS^nj+d&|IuQU;8c)QzQe;Df*{@?-^2)U}Qp?BqT|K()t7n7$6QB>Hb0q1aaO=B(2wLqkaBLu<6$SfOTQI{Hx?o#z) zM6Qf+o}!EO6fsTA`A&~6Vyczz;HI{4@{s6e6n$NSuNQ(lq_(aBcnjH#awb zqix&j_doW@XM5W{r`At=?>Dfho!l8DH;uC7D{>>g#z=9Gl)^x4;H-iRT-!uBRS4L+ zrnMG)oU8-1F7D3Ob_r$O^Tk_BYb`}paXc$HoGjQooUwO2kL2;9rnW9h9hHn`Y9GiF z#VAk662-8uSsV9R)SeGNe3fNwx%2!suXz4hQc-~(keWcAI-FCqBImb1@C5IF@FIgu z@rD=PzzrJ-Sqi(y4e$8KRgBSm?OUG5OP+rQFH;QWx-_XIN051v6wTP%{V+bWbfJ@$Au1@yaYId1m@>}c((Y9rx^P4Klc7R zT(c`J?}UGAtsUR|o>WN{l(Sl`mefI=6S2V!40vD$8?ZgL$+j6|3^u`dj4>FG&3MLO zGsec4V0QxzG@Ucut(MeME2qj;C%xy*JFf8kvG%F<_g&xk8V6jh_IY%@SE))>N7C7A z@4cS&-1qOk#F_03=N$Lnyh=N$cG%ww?&St=kg6vb{9#0?z#1ANwJG zgkY<=)!KE%;v*$czr@CS7#ezMAzf5jPntq_hh42hbY53&CdicDLkd5w}W ztyYK(HC9Nh3=uA%)UH7`JL*dNYz`$TjiWZo2XZ<~ZGA9dZ7YNfh(Zark#XTSVnS%& z=r`-9Rh1-hfNU~Ea2-TlQwxa<18fzr6#hO_%73I(-j^aJewN@}rBc~O%3>QcI$-^} zuIrk>&wsGmlZMKwrq&*9YPBbA*{Y^4E3&*KD=LJrh=!mQHVvF=rlQ@*lYuI&DRqrf z{(sb1-P{Y_A7k;plr)M+!hl*Ew1HA*w8+uhDj zJo?<(XFmO}{|&Et&FilAlKZl$zxy|Th#&dsuce**-lQ4z<{Mj(zb|DD7uLuwA zXgpEI`t)ysUmvEbaLy4%5#aIUap17V5`_NG8sj(0c~vo*F4^3ju(dm7XS`rE&zNN; zMeQdXaTKDIKlxX}Pd0kp7X87{%c=rR+G^27aidQ7<42$8u@|;j>&M)7{Smq=hlxXr z?}P@pV#=L29VIuCC!RgWqt9=V+_vZV#@%jPXZb-M^^G!xs5k>*6 z?g}bU4Jb}AoyN^+11g}dN&+P)>y(QZF0#8jWj18P$%1R%Csx7tg6vrrz9`5&tKXU60r{2H0xnmES zj)VHose_5fLH%dfSG?u*eD!zy74hpo``2G?7NeiCT7RXrLi*{3Kc#dIqA)^(((ACkMP^?{WLFZF1Y)KLpZB>^6ZG(Sdu6pD>c_0?t^n| zPjjw2(r0_TWHc{%)7?k7?V1jQUdmvl4M_GT1wqom60)3ZQWO!SXSAO4xEy_{as z_0~5&K+@?3o6kRc&ty9LvBh%vR%>0NlpqTH`j*KFXqlJe~{LX7bS zb)S`CDa#rmEV{0#w82`wi1W30xz@g>+-h(>59Laz8SYB{i4&e;6)J_ZK4DxbpU`d_ zOL<6CuEDn$T_JM)kL~b26iR07lcKaV3K6>mo9qfJdGY?noOrWun(!Lq2(*27W4KsEjbJltRx88gspM83h&p+`Z zhgP~CtF0p5`IQgw##i1*yWOMH63A+Y<+4DkkhP69TCEgpj#L2z9V*)Ws8 z?X!=PEf)k)ij-{@vy%C2!F;x0Z*Pyy?QJey-ez-ihrRKPMV3=mx*<$^;6klEjnP;i zmMI%4uM_y-&3&Sqzb~8$qdyuRI`pG|>+k%?!~g6T|GE3{$3A_vm(xMLMC+jOIH>>P zdd0OH^oN6MrsL^P7+rs>#U+9K50Zq1_kgsM5Z6He1Le8>Rc)w@#W{gN;GCq@N;rOG zonuEfIC6M{_4O5c{XR*O((A<>Iev^J3Bcsw40X9gR}1Pg!#c@qcbmP@+-GJ;$&2Sk z{O*T7&nF){&$Y)7@uvH3p8QH!d~Sk5M7%O$0jEVGLFY{__0kynNW zL`ZEsy3^t1H@#NGpcS(*XmMzz%UZ8ZznjoXBU)ih90nw5j1|I@T%1D&F&2pv5|l&+ zK||@0&1tnqxym4bW`h zz`9^9aH9AVAxOcqN(* zQlJ`-h`PYb-YK zQo%3?d~+)VWvNk85d|UE`bNk%6@Ze|#?#zzj-sqQUb`Vi`G&$E8XA@q0`Mkq=Nil@ z!b?E*|8n4rpr|#qHi#yRT-zp31nV2PCP%2=HgfGCheQAFHI38kgq@}_Pn1%rNzm9-U=fIKhBD#6FT zu*u`kU*>gp9p#lTyM+%wbee~sI>RkDoM4&doWHom-Pf&f_pPV+z(Z#^zd7c0FTa5| zzvgz13_}jBwm5R?S`-xmXm8v~;Fj2GL0wD;;|LWb2$c}DkD%fKIv-J$731+9d7iV# zU^+JxdC79IV6?l(*48$=J3EXW}Vh@1DOrU0&_wbWktxIxv?X)PHeZo-9eDWTsIcw^CI`o4c%aEn~eD z!y&0^%d)5`YJ+XGqeZoB?QX<+_OEsiP&pvEqPg!l{HzZo9~k)H&7Zu?X1`3s{ms` z*A^9oSR`8eL??;I8oOHiEC&3BQsTf^6cDHY@EcYsg$yLCVIUu9C*j+U_M;nGNwD{( z2VOJ&^Y46P{cr#9<5zoGz2s_lw9BQ7k$?f&eR^b{31GGO#Qyi_$U4Y8I7cQE0N#QW?l= zLtblUS;-C)rMJ7u_$V^Yrv$=l9v^;ET?F4 zH1e{f))fJo07DWuLZMM;0;@?SBoqWr6A4G6BuOBMm8KOyEFq8oLU6IdI~N={F1|p;C=p3<9Z|sgk_%Yhj>>14$Z+D;Wf$NtdsUyRyMD&FCqLqNfML z;g4?|TKk!^FFbqhfBos7=U0FAUtjGd^^&h&{fR%vYhHCvdo2<7oal%j>IM8r;LJO0 zZPu+7QX5BEX{u5;$Y$@)FuJD9bMNagcmQUEXs<#S;pm^3Fo&) zoV&csh3zq0jh7>+y1h258yjqFtg*T>pxug53VNM{)s;5A zZksStz9BY-^_3o%Mj1PM8An!9&TmiXbvu0RYi~t5%Ts5z2&0&n-+DcrR>}((c2KV3 z#84oNLn=k+GSXH`tA7}&_b|njBF}M75k(0yOhANy15@H06h+B=x@4INmP<>X70jkH zcD8r8ba|5t7cX;pbBD=nK~a`XGw!Y&bX3NX*S48>c$$@LG=@sx?>qMWgKDqc>;6KV zBtMZ?)z-z`$<6}|`I9Y6*xc1XJTbz@&z9W=kHB^Em42VKS z+-wFSrO?_@R2qvw35i7blmA%JPXZJcbO{Z$NjQU)4k;D2fxPfzVj(P2Np!8rvn5d= zNINTpNt?xN%4C-D{y%<-o$-=4+dXKB zZ+~y~bDw__v$=D%m)A?GKKOwTa>vVF=6?3q{_U9$zWZH&ptYG6mASswW~jBHGKNz7 zW*{$ov#G7cID^$*%Pw&U>j)&ofutRUv_eH7G=YRp6wr!1sGt>x1QLYu!F^InD($mn zDs8Du?Ws6{pxX*bLrW`kbkmT28qkXsoiw7?j%i1dUMr*>D>_j?q&z{$)Rs612!*5p z`BU49vesy0DJo4~YAOv?ZK=&DHYrlw&ZsV z4&Ic6Y9*3Rh7uwr5w^xv1y~=Dr>hKI7maU7Rn`!buCF0#9|#bh+1C~};xan^X?ix7UADrk@;Uit~b z-y_;j0YNlPKO~WQesyK|Be&gk=Rd{R#gi|bz1mCbpuSA%pz%1U|7vya(l)Pp?d#0T z-}a6t&V1o>50|yQsW#@AamXw9N&~vt1Qgo%PgRpuhDBkSEH$&-uq-UIykfG<$+F6m zXtc-5`dko+t_^jikt(3GcHE5A%SuTS73?-I89j_>RO|ePbl9Hv{FslwGR^1K1H@NjZDU( zYft0Zm#$f3P@$Jtf%V~qO_qd^$}{e@frdundlIj;cit(C6^hz;y}J`aSVUXF%cDSj zt5|X@yX9z?Ire-sZW3EGyL!W+s|;rjW-n=$4`EC zJ{^BZKwH<SJB&OYJA!JO2qI3!wrc_ojD=fJd7)yiewJ0pzSfHFn zI!il_32<0l;DjM)G7}UK3P&J?r(p##wKbHcrf#w|@~WmbzA0GdmCqlsg3?H`%23wc zg%K)ApaiN3qc*}3DM2SubXqa(Rzy3Ah(bvmLL5qPUc%*^MJm5x63$-9CMYUPS^JGu zS^KqatV0T^<23zjr`!7*$FIBoAE%w(h4220f0<8x=5tqj>AYm?d%pWGbN9XXs{6WY zN9T6=&W)D++i57iR|#IDq-;Bc2m*Cwx>eT|)_8f3Kp>p}tueK4%$@T@w*9ikc$BMi z)LK*4CDY}C&C!%gn-eZx-s9rVn9HLXyVE7JtfIEAktjC7n_)n=mC)-X^!pvsIHZ$G zR{9a4sG*({2avWR+PyW>b_eN}gc6J~OlAe2e|C#Me)J5Nc4r)0Z6j2`lV?UG(y-PE zc;fUnkDlJ+rt7ZZ&G+6wJF;{;DcgHfo;o{XZ&q;P*eZA5bt~(Kj}V8Ba<)xX8EWlt zI!Cw?p<=?Q4Kl(ana^`BTo^OjE0`?`X0sXF+ape&y~K-WFL3taCYQGNn9g%bZ7C|_ z*L~A$o2KEe_bG1sBsb$IwQCx7N98aw2tS*&+TZgXKlH=DpXH_ggZIAwYA>&Y`ZB5m zkH&p9R)H{QZ%f6{8(3c>4Y^tyenu}{6dH?CI~x_ zGN98=SzjHpzP`%QqlZ~pS)twOlBOx$UYCs{8?;h?Qd|^sma_?aJKJPs$T)*}R??MA zOg=`15tSBv?DOY%=J|8H;Z-l=^H06N6HmX$-Yg@EW8QqvN$$V(5KGbL4?p}6Hyl=c z!&kqWIFf|Q(}iY>C1+kd&u71Ii8DJ3=F5ss8gcS)j}sdmT1h|@2Gm+J%LGCOAVU`O zDcNF4QIzDBBdZ))UV06FqdPBa0~V4fpp%3QJ1J}ZHtWMKtHUmXUP`aiLj?hzj_P&z zQS8mi!g!su3cc|h1VRO#Nu>iGr4pzp@kzDXdQ&bAAw{K~dsImE??#LI4`0|R zo_P5kC(Jv)?e77XuJ$r}$<$Z9<+VKh%nO`2dAv2=y!6%Ca`6LIS-jd=XKB*ZIc>0aj)RiSrG)d+mO8QI-h5wpDS%e74DvQHUyYf;~nugs3&ViE38{(}+2}t6ANHvp7<7qf?W^X>fVPao%n zO)IZy6}Ju|_ce80f0L5xWr4Jjz)_V3ZM4rJQWC9e0_A0(&Kj&SL@K}K)Jz&Q?$+LnooU8+kuzHsWMz$UG`Mzu=#qrW=SamKXB|b-2Wo~P zk*o=%B}&`0T3t*rMb|ZhftOazXBkNnF}GbFdFmohpV?$*S`aG1E3RMV+H2PN$fM_( z%rgd^7~>?j-*Sp~yx|UxA6X#{H6rZu;G@s-k%!I@Mj@}h?GSH$;6{!fKS2}5#xrkH;(*ORBnV?y&}* zV4EyJho2%fvMTSl+CL2RjUa$=qK;dw-)?uiKi-PMXGe=A7cXyL?NxSAUuJdCcpTK9 zSiS0|V+;=;U)#NO_WSCx_-=t)Nt)(@Mq?|aKwB^U5yG2mgTPxzr4)o=h*CaBpq0i9 z1}h8(Lk7bEgW(GOL7z^yOPa={X+XQJ2$VrZZNk=CqX{-xCn=XZ=zPL#yvwH^e3FaX zOP+q=JgeQ92kyItg^swqm$NZ!GmJ{=`6h9z%lkg{48m#N{K^|aN~|t9vpMC+`YPk` znBV@;vs~QG=yX~P`W>pWVx^_H>!u@|+=vOI6p^f;^9jXrjy9I*WX@>3@bVJ`%VkYg zXfKV?hRS$}h}IS<1!)w}Nn?hc7HfkJYr_s}{Vv09i*7q23t4(X{h_ipzc$OuCq8*5AK$PR zxgY!KpTFA6>?KogdBbb?z(+pG{deDdVlkP0Tei%;L+k1~0ZNL7NY+gA(nd<SnE(qVXkBhc<-0nPh`Ze@w#a;>P9-^H(w5ia6zE9+nx4rcRTI> z>(tG+Kb>dG`u!jJ*wtPZFUk7RzyAGPo(pm7x*N7bd0V2yH+R$cZS6?jfYX6B8bRan zt1EO}A)Lh-i>aC%6`?Q)s=CHlOI21FYlz~AB#J!7*%=&ueN<&dR#YtVg1uSB?%s@@ z>5|cO$#_|iSK1TX!oWA-&KuU3rT2s!Uv074jff*jl%ynSinE4JZ$PWvquXth1QnCb zGn7R|x0g_B$#kyS8ZUVA;sQg!-M3%Q=I)e-AAg=fE8;6&a~Hd_B_I6ElN>+1!kg~9 znH!F#99m5|di*GX(9GtJyjBEBgp!g-YI<$K`o=1)P6w?WF5aNCeiN=U5qdhh^ces3ci|y?l=Cc_^kyF*$9~1~b{nkyRA(~t*yHA6&u9*^g z19=0OL`XS_)8rRVUVq*He&Nj7?bBQ1tG&z)>dUka8jpke6Rihc@iJ5pMC0wtf4a=` zzio}aE^bbrln_Yp`2#{wTb~?ko!?~mu(cS3Yy1p?G>++Y+6;$%*4I~A-&kj5b(MCf zN0=l;kz~+SY+Q4SAdJw(gkmuwhNnoYY_{Oe+pgnd4?fFRz3J85 zea8tx<*2HXFFf%qlkF|CyyC&9F0(z&ky0>OR%i>MQrvj7!?8ikC!gLyhB0q@&23zB zXh>06PG25#`phNnxNenGhhl`aEEbtR$qgme1&kM<9 z6@-DGe8`X}PLN7n31qVtkX{z%U{YxF$JV*u*`Di z7VV_>4+-aGOZdh#-rfiniz zq{-HG<)^Juc`t($NN1_a0x2a?6gHuit_fz%nanbFry1jU&fY9zG|QMS3zkLg53hvx zafC`-@pE)~U9^lidufMWZ0RM+%dWzxc@BnjyKRnatZ;NSLg)$EqVTfAy23_7&hHui z_~9ohG+y|REf4G8B5k;v{QH!M2N2!o(S+KjiU~jx&zF0Du zOxfKTvAMOy)|RJM&87>oqHN@hO`v8&Y_rZce%8_4L#}DaJU-es*Hi#12+u@G{FBE{ zocK3d>++$;p1j&i?V!Gl>!9&Cs6X+#|E`-!+buO6jbEQFWUSYKafZQY+9MNvez6SB53q&HZ_ z$`GAxBZVLaUpp&Ctpk{YfGM&%w{>e(~?D5fARmj*?iU7zf6)S zpxcUB>9knswm7^tV0GAK*zM5kbqM2#CYhapZ~4LrsLIiV1MJ6e(`EAxtBzJ z$9H}wfpEcNpZVA;#*_J<&$8@mh2xMneLBLxg9qX$Xz~U^ob?`vNXZ6_W=SGJ&`c_< z_0l=5eX}8j_f&z;DsZ;;m|fYRZ!H8WL9wHQh?R8 zq5!EtH&m)PiqSRXmG@L2ErALNgOJ+P-hYAh*kB=0!iV~42W4f@#x_|SASLJqir^<0 zrkMsdIfbst_;7w2qMOOaJ|)ii$=xK1qK~Jo_Aeg3=9*8w@WdAufB)b94&VG0Z@=1? z{v}oK_@Dn07Z*-^=bd6@*h%jaw)_TLWp9$m8`3z9mGZePpm12iFhmMLnJuZSnxa80 zI}4`Pn5x3m8iDf@ZRyvk3KSTNHZ@NAaL1z5Ocw>a;|05uCA-r(lf{47aQH(iDQTb? z&`!KOHHsANZkxf%I&rtp_Grr4a~BzpCv-b)gn-dx$ucil?Zv$E_9NVR%SnQ$#dJF5 zlMg@7`Ryf@Q*4i>Tz_=PH@)#@B4x=-!yi9!iBCL!i8zqFuE$(XX>!Z7;i z`ughM*;pHXWWLDE!%sbbwU^vMeVNyRx%{C1#OvbLh%@IeyT9~(KX~T08&7^jI@=Z4 zTLpv;cg53?)sC#F$ZAbh*B<*Syktn=e8vD?8*ELDH5Fx1c@$`+uLL4#3wdSHT970O zt#*o%4s4AsXV`K=s3cE3f0>Uz{2aUE1)VhDwo~hLItfwIrF0RGpWelYfV=OwfvrS2)xzXh*QSJ7sTfD2kHH+Y_X-TpSfV^1?2wsl(}#&%Ze4*x^-fy>693 zzs2g>5GgF$B`BAZ1{x&{0__vt14&(JveN%t`MEh#iYAl5kIt)o1aDPQ)HPXIvCK>I zvbyqe@nLhG#1h33DhzSP`vg=I#3ivPX+U{@fVG|`BL!f+Of678+rfC?(!L>Dg;K6Z z(%zef@}2ECeAWFYy2B&=XmQUgU$yr=-}bdmZd}Xbj~s-r{Xf;`A9;j_ANjoPc83?k zcK;81o%Ay}TZ6qu+q&Z$?xq2+jh|G6o<>vEhFaHv*JxuwNl)sDm0*x2bV5li9o;Zs ztrgJ;1)+4b1COpw6okT(NKGr0gczKzF}gzFuvSuOO>L^?l+;pH6{YsThSDhVQd3kF z#u)6NO%2wfgu*$f_eJJXpbh@yS@@}(5T3Lqgrw5`nNVv-p)GmslP`tv z(oCOkVks&wud1z}&`ramf@u&ZztbD^e`@3Kk)OR#CQq#fW%c!RT@|Nr4KizLdKb$D>Lurz{Hi?prBuWDz11vr`)j36_%O+qF z%Bn)UhFE3|!dhC9^q<%V_C+C$zsUjyi>0V4X3LVD(Tww(dz{M8IdCxX5Rpy392jeO~#p8#sGuL?A4^RI)W*^4X^@@$~sIy?&4T?!JjT zZ#+sjQ9N^Yo2~H@U6ovSWQ{nAnNOFLl_N<4hHL9YQAD+%-o&YtJ|xpQn@zQkm2M83$V$_i%;f$(z1NQtHqH-PuZ?E7V; zSJmzV>AYN0$t+Fcf4z2S;|HI7;p`)4E^fNN_&wi$wU^#O{dZ6Y6OV)XZ(8@bRvjQHFaUgTq+evDV%bv}$0#$DK7oNkJe5ttcc4yeCIV zM-)UDD=6y{Aq7zokpuxrsHk-1lf`k~Gm<9Y{Du%~2_tzWxXm_jP1We?eS;fekQ8-| zltF_yt%;$<5Eqg({iqCre|hBC(FY&>%p{rxcb|&2+wCJk8jh&e<8y87~XwS;?~0 z-j}3&kS5j=DoGOg_kt8Y9CD@a321RBh@uedBo>&@OD^s%I5#SADx@DdZaUIqoEsi| zc8i;?>GSqi9OcsPg7-h{bDrMzic_o%yF7Mg%;%pz-z4jRagsZ3y_UDX{%%gJ#ngFD zUJL&C3m4cP&*;X2uYb$E+38U** z`WF&}DgdO%h~gif0_l^O1Fvrnr6f=eXTe%Q5=uJl7VUP6;Yyc7hc{ST+h8zUrPc1z zZnfzRy7X4sIGao)=Q)0Kod@o_9jy(g&u=l_9dTrRh=>v<^CgE?I=tnc!yH=; zi9&@jC8dclF2I@+T^sUBQkErqdlQz6C8NCsvw7KQ)Byxx6nX@4X(&o>LN^VTveFjQ z;7j%;Sp9ZNzmu}s@321Xv$oo&+fGT-kT~?FbV8Px~j0& zW1ZvBQ=W|zl(qF*di=yO6yD_S6Q>PIDXeuA+ELXO=T9JzLQvIyis}6t!h1t%f0}NZ z21H1qWe`3ZgwY>#2E*U(B+-*!_YL2u|M-)i;NSk<2d?&|e96?m`X@il`R$47$M*L1 zPW%>$_&R0HE0m<`oyWj_qPWonD!^!suAAULZTvKCPAJ`htj zGC>u9Qq*NhmgUTs1+zuQ-fYQuQ7~EL%(8;KY9v)sBD@{2aSlN{@tHH8ASkJ8!`f<_ zb~{F*=?zv<$q-{;b2Q`qpL&cWj(Fq!H#6QFlTBwlcWJ`Tyyo5;SGoP#H7<-w-uJoF ztad`)e(xz{5HVfkeEx+U&Th}S_QYY{^6J}p!>jIOZDmLp2H0wbih9^^NS#l8@L<13 zyVJ#$y9_r@kWDT#-P@$Ua+J<+gQ8gCP&iU{cP4CYPgrJ47V`zuy$Pe;9k#c3*xuP? zI-N0J<}CBd%c}h4z&P7P;P}Hk|Gscd;=9kX*>602K8GL(!w2J5>u-JIcYN3TKlGm8 zGoSy$fuG}`{@d2U4&b1!TD|s;>u^%sN?rVzt;%;gNQ4wf=}$-fDVg6m(S#ov>j;D- z4!!PNNQn@ZFz_hSC{(PjuClhi!Ej}j!Ei`_&?iX};xr)$AxQ*jno#5!2uYas355?R zEGxqzxBS9C`4#fzj6eI0uVpZ3W9yP6bi`@G-c<9?e*Gi7`$LcNb+5mb`))r@UX)lT zc>4U5N1r;!eRp2REjOK_9V=2X!PN_*P?2uE`h)&_+_43S%p@_Sqw0pa`V{{6@6Wc0XN)qVy-@Vd%jJRpn_# zLi!95e^Tp>$dxt}Rps%x%?Yly7OfjYvXrAZjvkJq@K^i8-Y0H+)mzRj$2<17e)C;d z`?9@c>L-8r`_Y#0Xg@dl(_ErLIb>(OBcK4LPZ< z4FZQTmG>Dqh=gsft@0aU?a5XSi?s%=EsJH%vbM|?Iiu-<$vk706-<`}i=v{aYVRwO zf>3#_kPr}s0ZItcSkZ0A2xk$(Q_!%MUMEI4M;Hp$R(m)nDJ#Rpy_^p`bdFvt;uSXx zS!NaIb_*_#b2_brej*5?n8T|*F6}M2v^`<96H;5pcu~@Bx4Gwz>v`iVZ{@_QU~RQS zx8Ea4Ta33y1hGfE7nLB4L&CtJl2u|=6UPw_NnVuH+LE^WRF!5iFPP73vb>#HHD{J6=|#WJMB*A zC(GJA`Q+KlS9=K_)PJXS;PE)9t5)CiE#JiRPd&N2am~pO@9oX9Wl`N#RJvW1HO@F6 zE~O2#tYVSXRFy$%M_y_cg(fR&7DYjx*UXnClX=d3Q8Jz_8BG@cWj0%omo=sKX1b~d z=K`WAKuSxi-KNt{QKF*Cr_A>*lTWvJ{Fw_p`TQlKB;oL}>p5}VEtJ^^UFR5GvRGt% z{^?5`8bnBE`1E6E35DgFm4qi=+~UlIO%8P>dA4MGZ_a2u1tC~Fasv`gKHFxQSx=M- z0v3w}mpAwL=;tqx<(idF#GtKMU0g|R4E$+g zWqdGWURGp9?a3#3K~Yv%XA%Clp_D`hfzMKD!U@5LWjTx=;R}g%9;Ip;)G9)t2nb|| zlwL{`r~uV$Y9$WgEP+6U!mb49m$gFq&Lm0RvYG~`l2F8Ndc$i+@4awO{^S448~Dh_ z4{{X#f9w3^ZJvGc48QVk{@LEo|D#`iAVg0)vf5B;hec9bfkvX8B-4&%X?(bt^D@7@u5m*6>4ov=Tp@jX zqonfaYY9=HNCH8m1lCw;4YhVi=|9d2YU9YY@l8doSyqN+Wtf-VfNbyqW{XO*EH!!K z?I;?&v_+IEO1>ae@ax01wVxfV4FBG3uXx2HX{R^&H^259?#U;fzS@`UOTNDEd;dJ2 z`Rv2|{7?R`!;Nn6repp1&!kcCH`__@m!mMaPfEFpl*%~|C@9Lx=M5EQBegS>MdkfB zWkppK)MZUwSLE4}vZ#D|ywex~Rb6=xl9G+2&nMf@vy!dxoXy=Smv<*@j;3tx&DotT znPmkw)U1ROT#p`h#=(V>yPoW+pkB3F;BdBnZ4DH!nrqm)1T&{&pvpym*YYGcUlJ%kAu4Eb<44J`u#yr zl-bvsD*qd{uJ4ZnMHC34Kw%qSf)Mgb-hiMnTszlDHvl0J*5`pFt$=>7L$}*weRYjP z8yl>yuh8rF>2x}zX~f#vfYsGDxEiBNw63X5j5hGt6VLKbe(5(EjmLcdx4)S;zxFn! zqaB|K0-V3J$N%*kpQb8uZn^dl@BPea4i9412Q40aW)q3yw&Q)y?kpKEDsPq`#E`?+9H*{pmWw5e+>vJ) z!f8gMDS6>HiPk}7_Fp<0t();WP*(cN1f-M3^gAgly$-8`4#QrDwUq&bUWYi22?DRX zmQrDjB?x?&qiMAI)>>2$0t%dHf(Qk&L7xUu=tB~rX_O>1r>@FWLIaH@_AY1mSq=;>pEyd6zc&n-|OF zD~xr=g>!MBX!3C&lp%o>UQdtD){wIKLJo`p(I9sNVVhG?ua%DiNhl>&YoutVB8V&U zKJCCsNe~F4P+h@zTI(n)jd31^;2N2ZHhw+ohO}cXXyda)wDmY#=NetQHC{R>BtkG( zO1u~*?N61peRqF2eEzNP{N^*GtxfySf9aR6_T_j<)Sv&$-@`OF^7S`#2kYJBMu+>V zP87V^I(MtUts)TC+ADb|RvVmkXsxNM5?yPwuF<+i1qx?soG~aR5YA#M@6Q3_pAk|Z zoB=`vlB%w;!cmqrv-y(AB4adLusdEb&r240$+Fp8nI0}e_ zrP~UcY!{DtjA3I`_8`ui~|`)55}$b{}#rO%)aHhs+jWHS}eE?crY4WVZT1OCtw0rBc z`Wsliq#IS-a^rFCxak^hxps{tk_ee%k)*AJBu;5}hkW6=%N$x8a?|z4xUie?-1!kg zg}nT>>nJMAv*-6Xw6V@zcih5gR`K{VXDPF}Pv|uz%9U6L^D^W!UpU9kwBUwoR=M$- zJ}ZNiUdy9~gLIWnZ%C&d<4i$O)r3)k6qYoSM9RY~!Q&-Gvx!jNB}`dsmPO4nFTI>$ zkujZTOy^6oyr9xGx_P2o~y!WsMLzW1;0z4`0D>8Z0XKKnZ=N`FsD^{jFBTo6XBN}GOd ztkMplEtJ|(YVV_1l$xwEl(oe;sIJkv~;HRDCmWYGADr2o;7RmT5%<^O(J)GUkI`;)Bm|IS)d)W$hvsiNqq zD2#q%xH9~wgVoi)eb-Ibzv~x&<@cV?Mi<6^?;rd__sGKsw6yNl^37;0{`%* z{zm({)#(0Loa}u|w-x=>cBKA#I}W}wP~v1DX-SC?qDc_f-VE+1%^IU?s-kEn897y% z<7|zy9)zJQLs`~d>KaO<@cA6IHWam|3)a?4#Ohj~ByIU=Z#<;Eelsh_@1ir@AP8exD;~=$rNGG$ z8Md&sW_NRkt@9UIPNyvAGsb%(wk}`h{P~NVzi^SA?H%UxIaO6*j7A%Su}wY)zOm58 zcnr0+O}M82@e0jQc>kkRGH)gEuOB{g_y-?<;oK*S#g2W~?|<67S$E6S?p(0Y=P`ht3lme6zC?QZ%;;bW$6-nql2i8Im1PG~! z;)HH*z+iRAaAnBq+8Qf^A??PLzP3JOeZ5VRM3lvX#cYD93V*`q9Ku$#hes&$oZ05n z7$=#R3Ge>HPw|~rypiGBIet=Lsbd?Qy*%Z;AAgA1 zGNa#)I6O>v;LaoT+A%L&nDR%TJ5LnFJaFp<*Q`aHJh@I;)D(G1+-~_S1zl5`h@!MC zi-P&0qFPQtIA-&bva*zo`Mj+6Wiz0)xsu{6@u~4?9MWmU^gA6^1|3%WUDj7Rtn@o{ zTQNx-5l10PDxCFx4rdHPfD9FO{{`nH2!#l}0p3e+BG1Vb(xXywgh7BnqMIaY={%K2 zsQ`_oGR7KbE@R!(St&k(5bxj3>eJ`<@=>Uy`MZDrA6@O``6XL#dhM$aQpL;3-if`@ z&haRUzOpLHSC>_F$T@e^THOY3`W8Z5Ni1*TDRFLJx?!*uA1*1wMr!Acy~cRSlvMi$ zaEFi*W6YIY7#~z-uNdCozL!cnb^Us@dZ^#JRvYn_G>~t0Qr;{DM}r`g9+&N@ zWeD%Lz&YP=)V22xH0jm?sB2BOTu_!J#(IE-2jV#b=@8DR&?6;I3T$o3vx3QN!m`$6 zm1eqJGS3QT%YrQTeuknkj7tGg=nqQv^E`yuN8>8eND?YoUuo0pwJ>!>R#>_#>!cB2 zYql?zVOUh#3iR{L#6lLZewa~T~Ze9JrD$m<`tmrg6Dokm1yiqd0jxkK2yjwk@B_mD~=W$01D zGVwCtJSU0*2CGMrVH**|l-YvuBxm#THuHJLVzFejH)gcE$L{Vf+gn@AXLIVJq$moi z+8+k&lOB!p1VdvT*0@G;<-bprw$$4Hb0_@dzxLTC=ZAyd&$rU#|2D=pgqhb?f9vMjK5MqMmW z!qDywsLLgj-A$}9WL3!7%Q?UPJ0BsN?(n92uH(8>8+5t}%VoibKX;zr`^XnKb+}J2 z4H!)`o;WumO;Zl7uaFl7t}3|UP|SE%@${ty_uY1s+mE$a8Fm;9J50t4qy!~2MQMnW zh_uzFt__R4!dOLF)yyXoj8oJuU^-clWjRHqF~*^rROe->8%h}9e4?Cm;|=Ow zmgVIv&IxL5yk1{$g>EJA>qu*R#jh^*n+pIM36-brxG<1fsbFD|r?oMkOQP^xtJixz z43j^OoZh_uHTUUX_@!UBuJJ4#)PKBw{>Q(MY&jNd>n8`@Slv5F!&h2o-<*WvvpZ!xTQJY6D|D(wQB%}DcST7-90ko(RS+t1W#SPhF=?6-g#zVjT8W_7O^AYk zqA01gBW<1_&-QW|CUE<Nq_aX@ zSN`3jYl5Utn66RebF7F7qr~TvL@7mX*|~Iy$#_nl7tCfec6LTwyu8iM_AaB*i1B1f zk(U@1I#I%~-(_{RPp3Czu(C#fWk_!@AZ>R^l88aS#rok@(sl=3Oj*n( zM4=-J6vc8vp3OYNzJ|R?&Zj^3H1B@@=lIGuy^=TIe;jPiWN*&ARQ&#@PV?T6K0-SQ zIl9{7;?A76K5!Sk$kFO_dFu2gk39Yy#zGW@yz$PXtfYpUZ@-?j-N8vk*jlHq1$nl_ zRucj{W4X-O+nXU&LQz>3^PE6|ieidNGMz3M?e39P{smSW$D*jwPEc$AYk`z#XMM`G z@Ve?w647lX40|nB`aTb2ZP;hf?a*ztNaBbnQaEQ&D#Qt&DME7vz#)|IIAkY084Kq# z9dJY{A&5dO4y%nfrN@!qm`Z^{HI1T|!#NzKF}AGDt`Oq+tgJsLqy*)UhB>$;g{II2oF( z|2AQwmDX5m5Du)dWQ!%*ddc2wv1D&DWsw!kvx51uV4hbj%Zj4Z4H8$Nl$XhdO8SXM z&`7FcMLP{iTM22J5GsqV3*smMClNqZ`-#kOeT{aviwqN#3fbE_OVnNm1Rs0oY0g|4 z^A)eUi3_6)p%ibw?<604@EP9!;AviS$5FodYhKO6&uw$z(hkF3hf^muc;$Wfu-+Eq^YzY|?w%0&~SEfEPOp(bD6(r=d9rkv1812l+vVv^6 zU^baD8tt*Wv&(pI#AGsIHeaw<=47Qu5?2ir!a6^_@c3wdXd?wzyn9Vo@3F#SA2BS( z?RNSvHV$w6@%BtS9`f0)It6Kt%JtnpuVK*D_(maQmT0K(uMya&-1@-jJZ9E zln9kVVu=DDW)%eiQU+ADrqWGtnR8c8UBNX5X$Y01*G}m7IvhT<&f!CAtgNm!xgQ;p zR!XPSVr8XGchDhj4{&yNDdDzq2Ey) zJF$T<72R%^L#J*-gk7v&P-PRc$rhI{?^0_`XXO|+7?3X}RP)PZSxHeE2vR2F8Kdz6 z=Nw@ilNXvrR#9q4Ro8$gAnnW2wDtK8aj0k|5uG%q*G|0&z29Lt>@e)L>G#^S)08Ah zaYCT2Mk4pA$2c4{J|`kj3gH|^c*%gY01_t^+JR7lF!X;mj$-04!C_FsAywoFTz;Oi zpIq+{sw#~;t&RPR!~O9ns~)bk{=$Wmy!fLb#x=>qyr+l~RhlD3sPt2nRuk zYo#r1ts@MSPU1LIVKA3LIFTy2R9pA#Y9ac9E5p`CuMzgiG#pBOiM7xy3>uX3Qc+W>3;?|R^yzPNIPy%LqTm0c?&hY%D zIp6a3JNf!I-h^`+uO18N5BjXG52=e8op!>?#x;N?U(A@z3Y>Ec*4K&SgxO>a1Ps;= z5vFaNQ>dtg4BJ>InC|Uy`O+@)c}~8}Sj-pf?Ty&l+F@sB#B4HQv6zu%IoepN+9!=$ z>l!(%^#?PKD^&>~*vtM}Z&c$T!uQ{#Xsf25X%I6G3fg%n9PeM@+sl2ILYu8Y) zET*2TWASoFVgaNH~%3v_y=;1XEA6jK)WtHK|3cX&BB#mjeVumX%`u#2QK?_r2l{(pCaeP!@*y)>9OD#dvRzy(PTsgHQ6& z&prvx^2WPQu(LPkbI)xv>_nVgN!VR#taG%|kVC^Zue^1gJ8n5flq8h-oaLfqnQL}- z7bqne3|oXz%ycp*%X9LqVm$K(`Zx*EqD`645yDXvHASshW*N)Eu*f_j6KGP({g3;n zic$)aFrb~r3_2NQkRt;xvy&2>H8`jQ;m^uu z<0eyTW{p#ksei5u{T-}1_9UJaDvWYfQW}Ey+^B+ zB7|fzFNu2B6SfA_v(rSfq;?519->@MoOV!Qj8HLEq1oLTGa5}<%roZm1>^CA?H!-X zv9mK`Jf5(aFDdJqs;--E>B!TktV{Lr%VVh>qIO}o6rpfcLEQE^weSdZJ z=iczfS3myzbEoZxKmM7ky?hVqp#GzE(0ClwOQ!C>^Jbh7-Q{fd9r<$k{mz=}r1UQ^ zAsXyrlWidzg@N}nc+6v{0>VHxo@d!?t`!JDtDUm4y2_Em8yw!)U~O%cUcXP0rnFiq zgMPxHV~2@C#e94j?Lwl~D%s=``C`oF?L9i7rq@;s1}(xkLDx0&$sXBai4`$6KE}?p z;yv&CD3>?4sb+gT^z=oRmF3>+*Vx=!aAr4SIOwrD?6H_HI6RDb<2}bYw2~4l&0=X7 z9y&>PWdm2wnU6LZkG3hx3LCT$L7O1ylP~6Ei!rSvq^wHzcE>)mBTSjk7M@g8Y4#== zx@kW4rydC3glKIWS(YOT1JWdgW&lmZ#}JP909NvyL@ zTA7zdpGEK>xQEA?d9=`cepVFIU;NB7?wWp^PyG6CUhSoPPzQCTZhOO5b8dIRU;V1v z#5G4Z`k`=luXW`UXSbZ$aG<&#ZwgTT&DSd$Wx3Y{7K4WVXndFLLHt!LqC< zYwa7KMn)AVNv(|!0F;6tP^57{Ck;s>NfJr2tU?GuI}J&akR%F;V=r$~QHvmy#37tK zzCjqbA?#vJ$>xO@dF=3{A$*0NllJ#MS!O9A+ zz3*1u@~Ycd=^8qTBW$ga&vqD%mL7M0?Gbv1ugBIk%FYSm4x~p=L4vcIx>{oL5n*c` z8FjIyCW<1;B4@NaV{bGkTV@n_!E!!lG#at9y~ED#E~C*Nv)PQYs3^(`=b$#0stNkj z#x;|AkV-Uo>LzWx@loy1@1^kYhcHyn$F24+B(46hf}MZ%i5IT+Qa-4I`hQgijmJT~ zr0OsJrSGEKAE*!g``@~+EQ=pCb@f*3h#g$fBMba#kPn>lY09=CwFF8bC0@Q`9dV$L z(o1>LR-56V&)V7=>l>@At*z2u8PMtWNYa#cJEh%Hbo*Ue-5yfK6pL-L$rf5$qTUJ6 zQ$n*v6bQ7g&~;6=$fzn!n0BePv3 zT;NQBaf+;pF?Hr&phi&EP!t7u1zBFOSY#M&uaK{_ZSp~CulKgi&y^G;Q9vh6=(JOB zK5r%TyB!9-HobO>G!AIBl7=WIQ8EOf8tkLSSa43_FbFI{?VHpg2)&6N0fK-a2vMOV z3PKMPz+*R~C`9A0YakUtq_EZy2-PI43$%6GA$Jh!?6T62m&Sgq9R&|xnq+&=Zsohq zQUAlAKk$Ye)PMW>zVH75gdn{8M0@Snu)R_`aZf1R>)J_lhee#i*mWU9K$FF=k5qMd z0u|052;eNvS#TC>v=>=9AKvL2gR&=!?FWH+!kzzm0-VG&5-o?KHXh?@tfeY6i)GFt zTQHkvOlBE-^EuOH#&TJ*EJ}(>@0*(a1S1el<`UpMbubDP?KC2ZB%Mx+G;I-5&`u@G ztR`vqsf#7L%!wj@*nl$}y=IN1w@R^?qbP{eHoab-?d@&e_s1{t#JMRq-*7E&c*XVT z#WpW)<$U;|7uo1Wy#4;`c>MH;ho9TzH7~o4w?A+@Cy%t)SnIN~(j!en+JmE%%RNkb zoP4>Yo?c*ewM~@vz*dBD+Q_FId6pxCgedNSl&H9iR58ohjLpj<#^VL^MaFbCV{dQ5 zXlKOE?hZRUyDX+tiaf`d+E0R zsNGI}`s=>!yB-?vjm$6qvxDrTgL>)ILE~{yFS+{0uYDUYyl|S;)s^*~i|78l)%EuX zYmfLBtCtH%>65+vB)d5&LNJ@PM@h;i6C{oTf-t1rZZjATSy^3SePflCl@_xCu)5;^Y#=Nv-H_`xQr`5c8)&sc z(#`->n?+V5(Tp}vGg~^!N|4RRbb2XC?U=>q+hVdermAbgq(h|z^JU3scf_)2 zNLaRc30@%;i7PKg)0j)cC?JgjT5(LbmC$V^^g1nu-4^|BhhDesPd)?XC2z(MD2tGO zBBBBXQsG2Mtt)~+5J#;mn2#1HDrsJ`yo zzLVW$#W&t@RIaV2Eh)uahkEfXQ54=*8*_gs#7$wSR)JtYuL1EzvXn8-Hww+YXPZ1C z*`!w&S>c-zJkl6kBXjgbCJA1$B&2^{?FT#VpQMX@k`?56$$YtFzQ`F*7mOw|KjBz< zv-lz}$V;DGZj2=imB*UPhBT;@Z$JcCXK1GhNhIm^`zYaPry;%J29Yv^vcg!23PJ>$ zFp3ByOh?%sWlc^%?CbrmMDt(wy(LD!yU_fI%QG_ ze&-Xzvtm z1oadlL!$PORx3rS04E}}3kkv)6-0=jLs=ATU%Jd>yr3#eiY#Y3ow2(+VsmSU3zs(8 z8}BihE|@KIiqd=P_L~jYIK=m*XZuv*eL9?MLO-SS(~!Dp?qm>LjH37-AHDASUl#NE zh+{|j#QQ&TwU_uo9n^nP9W)*X^^&doZ#e~sAPCf(m-EFBS!3UzO%)2^Jb961d5%_dp2g~t!ve}4sr%T#DOg`OY>->50$`Y+y&(qJIVX=Ll+0GW@Qt*L? zPV>x*7a4X_loU9jc>C+_;ndL~d7ksJ&pyc`PoL+=YKw3Cid(s6Eu!0PG2UCUJ(_ds zr5ycu0c#D6daky0Uq zYEBO8LQ1)d6X%zuex@=!m`1_F%d*@#H(HKQZx@p!l=dI|gP*_JOZ}i;LiK|`@cm?^ zmNy^o^>5hdt+dm4JuB;bBFTNdG`txhj^o^#QYxXLPhCl%wqG0e^=lWlJWLoW04C$!x)RHer zKoI!fFMwKC>}>5Zn^y#(VrQW_y_s=#YtD3$u`y_I{gELLJ-5Z69r6vYyPjb;=DCYA zp1UyNxeH?!rRGpS;r8PzJaciv?!4qHUVRJS{y%;l$JP{~Sg?NRFj3m3C<=tL6j{Oc z_M9-19J}dWlI|K*W2~u>Q4blmD6<*HShA&NZ+FUUzGS(~m`rEvjrJIiMvQiM+1(v6 zold9`-l)i zKX>lZ)n4icbx{9Vb@L3{#{smR0uz!4x}Pf5rAjbH`-Z` zAN2VGp5$Ut7?hO6p+8Xv=ylty4EwCFt+2MS#`?xO!<7}{q(vBpwA(3b>pg}m9l}WY zGeoPwYVzfbY%w8D+eCrI>5L*P8P5cQg8s@H*=)qQ7te6+><*Q-$nGkSo!%o21TVaJ zjx!f_xc2BOuF5&O-e#nsG=wiy=Xpd9ob@nt}3L8sA|o4R#R$6T`ZA7#4@YM^2&b=V=4EW5bIx) zrfEp__{ zE9?5AweIOiiO};C-hYPhx_+%q zBdzhX3Tv%5g+ue~vIw{D#c&wyQMf`%tkKl9PrIhc#{heUOr?zl;ir#zUZS-o%X0Rn zbM~flrj49xw#csdHuAEj*8a~24gNL`gC?uRB84Chn*5DG5Ji$CRAPE!avSgl#k#^kjkO+tnn#|WYp2nBfWnKq8R3fS2iG2Y!J&kL5zCDX~2 z?VVjNT-;=9dxyQf3CnEh$zjbiQ3`>ik^Tlg6UVxJzlKMKSH_{Oy^?uT)jrR|KW62{ zq?P_syW9Dvqn)ko?OAoTm;FH<)PGSOG#&@_Wl>-Kwl@)kVf^CLkH5Ca@*lRkdR-tz zETzx)h{J#+@+WfEG}#r>kF;_AWZXycx+WprZ>psf1c8@d4Tl5P)>c_tTcnErq^q+y4s;P><~pE&T5wP3FF;O!a&eUVwAL)xE|!ASY&8j z(;v3!tR1BmE4*;}BERvwAK;NEo}-h-e9dcbXLqvX1D|=C>rQO&J^%CDIh^cLPLeMs z)Y%@jk(4H+ES8jcL0(!)?IH02$UiS1EMJOMso*3SpwnHS>iMT>9AUR`IJ(`!f(bb0wKIiu6CL@Y9U4F zH(x*$M+BijDG7L)mqVZepEKf|#0d$~Peq&$*v1F8yIe>)63$-E%KCIx@uX70BZ;tQ zMvHuQdbb#V^o8B|p>FK{@Bj1vxZ1bDLH!BVxBvOS#HDE_zVh~?-5U?Lheuahy`|=q zaqi{qD7Y<6qwB13hn=&lLWtB_E3EY!e{1X&eZMgVz{@?gMq58Y1TaQ}^^JrlQ2A!W zI!E0!E6!SsH4X08KeL4MhH_)~{TsGv4*WrjF&>>;lqF?VF`X}%&6dm-In#N@beT5@ z+>*Sk$!pzEuLN_-qVpp#BuDK9a9j60opi< z$`D41VXsZQyMju32t8#w9`nTWm-zfMTV#b{XHj$Wb;tPX*W5zC9dTil^R5p(M1baH z*RAon=XThd2neDS)j3AK7*pqSdMigr+dX6u;t}~mGe>^Z5*Yp$V<(1k+HoyVP$2H zL+k4t+UT>gIw0-zsq2EpJYzV^==IwqX+YXpA!@IZXBlNaBc>$k4w&tpr><&}Bw#Yh z5mIsJnxllRA=9lh1a?7hFr<6e4Gh*c_=698j51%aI*57biRY10@cMgi=7tmN1Xhv` zPh!dm^U)T0rP$uuqohwIL*j0UaW&)ZO~N>!H#kbvKhAQb(dCRZ6tvpM>8zci)&^5$ zm~u%jBX$d@>zZLZKqm<^X-HyAr5(!*kIl6H`=#-IkU%Mnm41rhV7ByOVu6%IO%u{e zB9bV)k`vNxrL@~Iolcvi)oy~zd@~}14k~cUsQjoOZ@e zRn$6nXubFB8*V%F^lXuz|B8S76Q@gUo;}`=b}sJD%`gAEN9{*H{X0&G_-fxC2lby& z&Yk1_`+it_!`Iv-uRGRJ%RFCi1>CgWjjkPZTgOZ7ZWY)YMIcVBq(L7<;B(oVFCcu% zyD=7LeD1}**}68`HzL-d0c{LbUHe3MXgnE~KzIou&1t!_HOe*U>t^zSlRlfH!QyHh z29MXp_|tZyHAPXfSmewX8MDQb`7&d^%$Q|4^JPKSOg75eP#cRU1xlj8hm7vuGfFCy z62uBxks?kL;#P-HlnAT2JSo^))Er*1oQh+@KoW%#6{XZVCRw8KeSTE{ROR;X%sN=XMxPbMAZDO^Bq; zO~+R`*7K7bV=PVu%xuKw_L#L{!hLsKM^##m9&XWXDcs^b*<#LeSrf*BwbdhZ)^8wA zF0q(Ru`&V~kS$B5(~P~njO8L{v0O5p`i5gP8nL~-%huMmZ#asaTKg%7@@(QJycV$F zgz(Qv>t$EgX>0=yaIU%TQd|iJOyb~N5=Fmw$ zgNesMec9FPUU3sh5!lK+P)sL(%^3YwV{M|1#WvwuIEOJ_105=fQi?!HpgBbpAQ1#g zHtzz*zIihYV_NMNy?&P?hu1lJ_y~jHfL^ahtJ5ZqLpq&^BZpU5SzQ4YQX4SKOC(9k ze7w!prOPNKneOgU0fv6#{AO4^+P^Gvg~J?7HIEk5(ebG+-LPjStm z0pItnui=IhJr19`gYIyhVtO8}q0k}c&TsMg=f=G7;%Pbw99s!ULV+?FE318y&I+~h zIT)s#Q(Hl;1!YlF6gfq{q&9-AaA*VQ1uBThDnVIResZ$#5)56{K0SHJ3!khm6RLkdzNa?`>K^XZ>os!5PKsZAZg+5KWiR<>{Ez$Tz z{7I$2YwM*16$CT@E@!PN5q2U`qpEg0^HN_d9M1@;z7R`yZniAvI}1H~@Wt`um;dR< z%Hbm!K z>qhHUi5rBWimi2?T_9_Z>u#nB)`B(SN`405H=WL5jKLU5Y)yYq{2Fjshh-jDG)T09HViKG1*UXHD&F|No85FSY#}gIg{Cf$#lVFzVMAk zQIR*zMp1c!mBX)ZAMl4yr3as{(|D<(ND+l05=$#|bo)J$Rts#6wU!H`jL$xG86_OA zy7dsZoE(zG5v2MGnp*Ovw}P?ym5TA z$7pxNcr;=>-eY$(VK&dm%d(;W`8*VB{j@_#L8yceL&Z-r1fmH5Y?_dU7N{-Mo>-L! zLGZpq8>>GTN%hCpIsN#H=dSjha!?2LU$qV<9tZVhS08`qSpa?8wMRcX7_2|Bn9she zST6peG4@m-1)-FlY!#|2NwEr0!c#`H@TZ8L)kfJZCy9_`^@U!0&zXMdn#W z7zT9GfV*$If!Dp_CX6uT^Ih^hN7s&4J3$JGau(qbxT z{l=7@bvyJsF`ZUQlD2523KdGyD8_qIOPma`x`xm-QWZb#2vk5U102#zPK+UFjOw~J z7(^4M<`JN1fwO|z#n?c4DVJ{KUP54P&@>|oFZ@!}+MujODTPo{I3W|^IBcaj?4+jC zis%P{6JTm>mZ1u^S9{{johMe#{Dn7PcVSu7JBvT}s@;Wl=Ztmd?>N@q+1y#U_dIsi z{*zyS+_Cd<_n&%g+8;E|^mk$iSQ2hkaDkxgsCK8EPy2gkyI~UTE^H zAX_e&O{eVc?y|kT!)Ua}bTVbRTvApw${Er?(h6ncr8AT@SSzsBksC{;{b7v1&fedl z8WNup{u4-uggPCi?f;|G?fr}8bhh{O&fe9&g%0YV{zU4a@i?e2+j{c!CBEgGUcIYS z@biy-_LCp3%KQiGs{CqeZO>=K?qeqvm37VL0hCfG>2=XwSIre3zH|QN94G<8vYd|C z);T7dV)H4KGhDe!FdPuL9(!7oFJo5LhqT)v7KN^A;y9+$Y0(+<5YnfRYhBY{Jqpn( z#Uy6DeGyHY3tPK<@WB^2vpMDX;XZfYa)>YtsH%!$v4?7R5GrE1oO1EfgvXxWW|r05 ze$y##ICX?9%XsRUGhE)8aosg599c;iBqh#T)(~_%9rB{!^6rx7FD{raDw066)=OAF zbc|41q+Q}%g>;rUiAb!aphk8hkRe)IirkUMlBzNkrJ>TIK|%VDlzzJ82`(OIT-2Jp zu3_Or$I@v=8ppKaDQO(hX(x1AF}+TQe!t70+oGLDq@g4X{1n4k=>=0tVx0BOVGSy< z)Vf3kfD5tO5Fm-d2&upsi*&+ge<+7?23=WNx4=0+b@1K_AKvHlH4Ifw>?H=%#l5CoDa zR0Pudk3yl4*5RB6>z`1@S!%7Dd&V~(RaIfNp{PpAvSzu=nJpH~vy8=}WWLOqEi>j> zNnTbImGjb2UqN)ws(*hM}jszi{R)-?OJ>sr zMV_-*ESQX^O!oE|?~NFbc9~7a%;(-0Q`Xu~!y8ZlLZFQYZ#Zl5Igk5Z4PVBBc1`1n zyW;B!l$fSz^83AZ`U`LP>aTjlXsv(wpa0s`zO@eOp#H?_pz%1UFYEe^-~M;}`2Y6z z>i7Tq-}vmyU;DbhaQV!O-&9rE_v@;DI2ngz&NNYi;}M4t=onsW3TuH1On?U zrGdNxRlsynGM+CPO{T0*rmW9rEElV+tgX@Q_pzpftu1*wVR&eRv@=BOC0SM@!<5b- z!04LUbcS=#8*17s*Q44ek)6wIE&_I@A4eO@EAPIYn{T~~cBjSqp~IxHW3h9Fo$V#_ zxh3rm=yyA;4m7X2{TPuem^s5|mpNxPCp>-nB5|l#?I#>s9dLXt=iXZc7q=$--X~sU zZ&7pN=sLqem+i|Hoyj(b`--DO|1yu01SJJ|mXRb8Vbb=#5UmMhNV^qLR+=o=Xl*>5 zrM8rn@zN~2ABbjALJ|eO5!rufPZkC9tVBsK??~d1I1XsV3I8AV{yWIhEKTpje#dhk7XX(Ckqk-h1Bn zeV*Ubi6eTc#|QU1U3#6Eez!w6iitvqQ$bCO-IYGSLkNRJ(o_a(1wv_TtBtR!vemGM z7#yPZQaUM{)12VCu8~rsqyntplqru~MoCN~P*UN91Bnk!q&25Ya43ZFntWs1ECs1h zLIz4SgQjtVrq&1vQ6Ncz1dDM}OKYsHZDSUp)+gJ2dA!{V_W#(0-u>{MuOEHozx|IF zc~LEDBW6XzNog4uhT|~c;JK^4ET2@AWyz!{?PO8#z-}}bv?)4X6^4}==5BjNj zS^`L}d|Z6+u?_LbKXzWM_1D=Mb!E4!xVSywJGW2Ld0uWsT5beN4x_-W4b!L}N!<;y z*SdfChaTQ2jk{p1+tNy{hgz&FEjk-v90(z$vrc$f`ZC-D>m zXWD~mjTd}+;6)$+=$`J*UZW`ib!2V*wi?>dA=V;0M9^GVXl+qYU zy(|foB#kv2gMcTlZ19a&?(*`jW2Vc7qgg@Ip0Xc4*W)8kou?y98qwkH85X*y*sW zEoIfv@1;l;62u8%XPr*CiBJhbI-FAoSJM;~(@Dnh@s#N_r^pNPEMq>KaddRT@$oSy z$0zLW9WtBFD2jrj@?IF96=Y8HHC8F(zIpY773*A!$CX6VwRtSA%@u;iIxBSWy(o_U z^XpGM`K#al;#U^$`Pd)lfBlz!?g#ypJgc+%XIf_-kF)wCQ$O<)AE!TBS8u=ay@$)? z?5~=#`d915Y=ZQ@11+gqiG*sWS+rgS@ayrY4a{0?KU|wHVLaZ6D2f;j2W)O`u({#! zyX)&~42FHuPKPv2>GXOekwv%yDI0VU5yd^0(?g0pqnj$a{UO#E?%cS+cs%1PFW%<2 zKK}~;?$7@e4`1vt7<32&MW>tMY|Y&pZ7<=?OyW2tTH z2Z2_WWwF+0gy8RXl=700SZm@?c}i9i(M?kNos?le@hR%P4oYhxt?48QLFAi-Kxx41 z=ap<51^i?=3_M;}IOp~HLit~`ZeVB8!duXtH3VS1Sd63Y8Tw8Ve6{%Z&UT4@5M zXit%+#F|j+Hc!NFRD5GA+R3xuzBw$`df$w*&U;O~?h9vE(^ccZHBzWj2$c)1N{h=a zbrva=W854e%F5V@ajvKucWkZ5o#VhsmQkQ)+rxBEfE7ZqoX=`wDytRLjcBskRMs^( zC(tT@mhjdXgAk6QrpXFRAf0Zk0Tfb6pEZH@52%JTiUJ`-V6Anb3S~D+L#-W77&*?$ z9b;Wz3fEDZEiGLl9qTv|Yr!xGL=*)<522E}X;LdhVvLK0GqF}gfznzjEu>JM3}r2= z`LbvadhJ8#dkVxUS;{Y{JeGDfP&l1Mx6-E8%k53UO+(YP#3EB;o5mZvUE`6q2u#z^ zRAoD)@Xf6dUJBJ%OW7EV)TdhxDFixDrvL=&1PF18`7W!9s%pscibYm1T`WC;YVOV9 zSzdbiR8^A|l_$+L=G1Jil_Ct3&n^-V-t3hU#9E=Xr}t?EVc=i8bd-_=4M~!Cv$T`M zp+G5EHW6R=-W~Q2$2|SehzlEC-rk*avMA`rnr9!`;_CSU$FrJW|Kgj>^NQ__5l=sH zjf*=Y-gxsC-+lQe=SLAAdUA)OdBx{m+$D)ae(sMy%Qs)&=Z$wxIJdpd``>ew?Jiu} z?$GPTlqH-y->2K{uq+y^>QXdGt}#14{uc+B|tg#Cj1&%1{_3*UhX@KO5285!h3;VRH{SQrkKEY5ziWT%pML%a{T6vv zXZ6pe&Ki%i`Xg7L_}Kdyj0VxGFTVVVY?=LUt1|y#2|7@MAn+!0Ed@%bmWA&p@~bJm z*4}vToIMS@TfOs0DZR%aiWm(0tgmmdxw*;u<~qaCkino&C+!eM5m6{eQ%R>AGupn4 zkP%fjVSIQCTW4rsI?HIRW;!`!_b6jMfj|qQK(|s2Nq?9Sb^06~&Cptqm5Sf`?05M` zpZN~XS}tr3c=OhMe)?n2u{8)$N;8?w`Ss7e$o-Ryk3O}-BUiTh-EZ6?jYEF=qffCj zumqwZYXW}tv#)diXvR-`=qawB3lTO4A(1+1$t()%1i}>5WqKHvH zW^L4^n}j5hrjx`dt%&2KJ=}R>8Caqyz~L~B4_E^M!h2S54}#o4AeC=Yl=3EZtUt`I zngS)|=`|*`Y8woBs@+#Yw%H^8_totvNgHe^0NGC91+v9(N+}0bp7pQ% zL3njN;t*}~?E7YckG8Q+DV&nRg<-V18CzFHCZ%Yk6gVdf>sUVU!94K0_=IJFa}-rw zxj@RqI^THsFJ#;N2|x)arRqB4V&hNYTI(oPN=SsmG)`!(gjb_jA(aq9NKCsyz&V8V z*lPi3E!#Vx1qCRdv0#h?Y1@OK2OPN8$SZ_ESO-YzrbY<&U^?yWsWIE))4{Fr;d+g= zr`|5>oX6od9=_l+G-$m6-p^s2ms=UTI)M7GAw?^@lb&d09Rx}fv<;|I5@&2H>ulO4 zy=0N)%;p*MWx;fwvB(ORZKL7M;r<|8)ek%%T8YzqBCX_UDz*@wPNlW-k}fTYHH3j6 z4mCPZ1VKQW#)NT7Q_Lxgios|^uh+p?L$;VxRF3--!{@&BCjCwfKwev(eDonUhaGyl z@9@Um34h{;p5oz48`SxXyL)4P{mZuySRTJL;+?|A}xY0fgs$@7faY{qml@!O64L-zL%IXpTc&vL4&YJ(a5 zaLKaDM|toV+cvy$mCa#R;q!R&c)hwN{PVC5W5c4Z|6jd!1NVn$E#D9nuM^+NXc#Mag8c;Na+l?c;TJwl>+=SZ8g0jb6WxNjlU`K-1KO z@r>c-dBWiaS}TgOW`1;&%?sP;c*NS|E>|9wRK*gd6QU$R>i}E>)geKk2<4cjERa>n zkG=0Ak3Vvb@i@cQCBIS>eEtt!!&t|)^Fww90Y%j?=)^pF{Slsg^eUIGU1$H`kbY_j zG+-4;0Gor5H}90(y|>H7;TBb0GM<-I&BQbOI|-wH7fXZE8m%>fNQj)m)H$YZsOyr& ze1?*s`XOamQPsw8K-qcb7hO^l|)(*=@!?U zM5Jj;%1Tpu-IOGX=%#IOVH^?1Az>hK&L1MAv#6jw&t3$T%c>r2za5dI8$VMSf1M3{lTG3jRus~5;gm*s!Ho{4a5iN??>-U8aNbG4U zw$?r$MQA~&eUt5+_D!0l-5Ru;EBxQ3R^YV%UZjOq=44$>U5hrcyIqoK-EJWqIOA{D z_-jEm5`l00Fb-QZ?Uu+thO68O(;BXgwYWA1##%7e_-01<%}ibS33gLAly!xm^>(xd zaUmRyd+@V6=P2{S%dnJ0YJt)kCAHt!IcTsRf7_C${Of`Od`Kl2PcXl)qA1C- zf<=qBU1S+~S+UFtmRaGYQ>{;T`Lk%ji2$sHl6&NO=k5?6^hvnBmum!#KYw04c&`X!tx zD5m!aw4*K;1R`f~d`PJ!))=fc^m-vn=Md3|Agoy~?_ryUhtKz!CpEWko$$$zKh2k3 zzQ>K*`y`R%<(uAs{>-BnxqNN|Tjdao0Jof0LT-q6NxObbA>5|$QvOMR` z?wD8aOt^C}r!kH&&I!9yON`6loC`7D0L8dV{Ag>zoqHcX5l5nvk3KW6X#9>Gh zM#Q0_*Ny2W5yO6uUN5DaCUoPNI1GqGe}EJgtS~-VTKFa*3N^+yG<8X+0)OaK%5O!a zKcKJ7qSmuWy-r>D5FA+U|X+6LhooU=aHrJrmXx%qpTah(P$+)sx@@0K>7_3v>0A>8cwGJ-DZDS;x$^)Pi<(ZiM{Va^KR2LWO!&$x zyL9_~M*WmGUfg5vLe68Ch9ptIoA)!;1_kBa+kEG(W3F7>;o~nn#Lm_l7k5UaVov1d z^wWfXry-1MNFvJVfbr21r9=OGGBty(OYCesPBVR-IPlWUM8pK$9VQ32$+Mg|4Nx&? zSEF=5o>iQj%qen1mKQ9S8H@RX*=)+>WX#dg5&MV7oQx+d<{3p%VB7FTsT=_U*&6*< zO`X4n{8P}K(!e;y>7Js*TgjV}>R5;2FL#Hdzxn91Pk!h17hgOh)}7TK={l2Ioz+~N(ZNUSBL8=ry85`lrmF@+wTZn_2#u4-+Gzh`iSoKHTK?miQBjK5z^AxzQXSQg1!6q`MbaIRrZf2 zeDvvyYz#YW4pYAPz1zHY=Y&VDY_qdIz?g>pgJW*opD-A7`OsqAAXd2VXvHYgPwBc*UT8!m%+9=xa$FlLeDAyt@1LY0uQRs2KQ5cd$0ZA0n zNn+B(8`S$PJu8V4I!QnhD}q21#i4(!8-v!$%dv#tl$5qT?6;HbHY{Y-cAOrXl@zDg z)lbEb>J=hKe+JFA40ak6`qG<_CqD6O#HmTij za#}so>OhYr2sBRkya=4*w0&@{ZFBtB541*zw((wh2Asn*HKs9s`X2<~plWJl>zvTa z=b{K_X>5&ftH!F8mI(=M6Jo7xF|`7V-xMJfrZH4ied=HExBFo8V~xc)gLA?+Ft)u` zjQ=;BMc~?u4G=2>uyr)8oJ&cIbD~vES_Jk%Gu5sytaI4LA+)A(AQ}Uv#^CQ)V`{R> zP?j~OZYZjnqPAp}Aul}HYMJ}czM`lps*1d-sj7ymc_5vt8`};ryx%Md16B{5lwRs7 z+oo5wze@>$5=5bwROuijPGT0zjN{poDAZis>@w^p)KyJg)oh*Hp}%&)H!<~?@xeXP zw8PrQy1$QQgS8bZ8dB>4$CI4-qT*M7=gTD8^3=5*ZjCK3Ja!IQ&G|=Pd;{w&KlT2H z_`-|#xP7qT+*+3_m(O$M(iZP~e4DVDk)$bM6!OmAlvi%-^ZsYA@`2~BAcP={Qu6tn zMImX5oC}vntZnTOq#GCm+2kHdhd3coI-tKXK`|$gktZwCYevTkceSFq>|BZh-w_7bi>&3FC zO-K8zS%zWsxlXtH|5)GJ`jT~S`MGbt_=A3{JFBz$f!Eo_W(G!oJr)wPs?`b{qEe(C0&;%EtH!nFQHX zEQ*TBJZJxS&S-edxt$X>H#ZortuYu3=yrQ7^ODKQ327QK9Chi9Hi@DTLP-=~$J7-j zKO}5wFl%J96Qrw9N;2NPO;szVlZ>i*6Dbu>yzirAlUB#aFOB$-_dmt;ht4s{N_LOue8>p;s$zV2pWWk}t@R;4_cI^l>Q0E3 zmay{>C(mEy_rCNJ-+OzP;b4f&bM75wWTm63Dxxr8ZCJ6hCK1|El@&>x(ix60bwRO! zFb=8966YM9SW{Gzvhq8ps+Cujm7%U1${MQ1wpktRAz3*>9pHqZuFa|ZWtvxrqVgLN z=^K$SP()$yAW1z6eUM=s)9=LeIx*ceA&DcxARr1fNgTD?j@GNu`cOP)*I}B*=Yj}< zwVb9sn|9hSrNlK}s^OfYp+bswgVA_Dg^-fQRM1GIRw!#Qjh95NrbM=t4576*y3$IT zlzP{x$9oRM3b7?5LMu#jdMZL{fpdbUuDu^ZiWUv)Wn!yUo>V@ux~!Tu(Os+@vj7OG zP=Ur+OR+3yOha8)SnCMF5Fu7gm8GFUtH3u%RdebckNC=&`nz5C@W^ z+ega`>IH&^Zf}56n%VIoLI^fCQqnXaiXByTj14u>pvR@15wE>{kF=BWsVC1gz46t(2-cMiF9afe#; z>91d4b9;ly-Iti(I=~?)OTpxLOt&@!QA~rzssU+loiGS-wjmvCfK14iIrCY;VqqxD zg0d)BE*DHD6ONBgI669E@8E#L!xQH7xzAQHerRCZE@HJ&bM5Q7cL@29t^6Ctwv?(? zhIJ~r^#dmphQW8DIQ%a!UAz7(-LyNI7fU|pCBA2MR)6H{tnoOjvuf*0-~J|F`ZoOR z&;0oK1AqJ{|Ld2(^5sw0`Ql%%i|j9(rn&4F>n)8&%2Vz??PPc*U+^i{0?$(NdUN4? z7?>53($q9{#d5h|I-78Kbi(?^I_v9ejMmra4+ivlJ&H0V%WKl{l5RI7>4ZdaOj(vV z(@^CLpO;ZejF}(IcGV(N3T0#uyvJ1Vlc}+P-Aqb5*-E*7u}>#aC}EKG37Cc`6vUl1 z8sjLlF;(FY1X1dz{JC^=V@=&CvO;h)^;6_->}66-W4zx1e+X59I7M29IzS+(t);2` zp>tVQ2nr7pP>N71qEHhDnm7tb;*cZ?iNkblOHFjs3@?C_)7qt-KUNA`!wL zP=)X|YXGc6ScjC7+W4=(GB<0b+T?5Nr`K9}Nsw)jN_o#ly9E$JoHi{2?`04|w2fAC zDk%z2k-iqCG{$+;w9*PC{no}g5H*1Fw~fCa+H^Z45-0q1E2VtbhJtp&OZ)S>me}JP zAJ_h)RuY46#z3@&Y=DJ02Y2{YdwA&;@QB=(5OX{j- zkrynBf-Eap7ByMXP?oI;ysRn8ilQYVm6d59e|zdjSpgH0z~{xR$ZJmd##XQVJ_177 zZt=87*M^~QpnVq^kR~CL2J0M26cGeqOv$htaQ&h4eBq&coBvvI_R>Yw|hCS9ym!g7`$)mh>bC0jTc$1UkW5%gVt+E@?R#$%D#!Wtgy)`mn9WO1(CNg8 za-Sdw&_Mu7kQX)U7ayjxc?}^f(MIS|xlw{M6^rpb7V~44%ZABh!E%w4vGP&HFmU16q~9`AwO(AGOb6M`KUrRn|#$ zGfI=c+wFD#&WAqn<9GJ&-LwDbx6iyBXLVNZt~06CS)J8?PyN~vD{4a~5_*VpOuxdO!`+oHqu4UuPcH7}j(}r~`kr4tysR)!r2cF1dfgp_N z_ByPOhOBR_v$np@a5Q2x8qy#1Ns^c#^rq0ks81LLs4zh)O%V4f7KfCJG1gk5Fd!M8 z!_+x-F(Zh22&Kp;y9iTIWk+O-DRotPvQ%ZM^%k$(JmGU+`3~QE`3(*Zj|mlAJ3r*( z?|XvBF84W^mQ0I)U;E6r3Ca1l|IGWja{WnqT}3&&&-Y%s!FOIe;I%tbM!lHt-#kW0 z#n1oA4|8E>#NPf9H}@8N@tbeZ3E_udxJW0k=rE$cwvCj5KU5k+z8I4&mRJ!Xr2kl5 z*U;qTdC4NLxOI^8*6xf)&W~8{1Qcb3X>3cE@p1^C53-t4ic_N}xE77+<#3H@P2y6Z zgr6jbfhN$Na2AJ}G!6*^O&DsD$ji0j$V=xsaX=bHgkk6(8>vxR`^|__EzQa|B(?Ff zB%ze2ph4?KH7E&b5)%fpJ@EUDgtel@HhcP)bt{}R)Qva5x>n{@H-0kiHwX`m*Fv}! z(k0M3@U$gi+l`255C5Xw;zard@*gS(a7`>AZZ) z8G~&D9(`U#YeHWg`UN!YRs!cKVnVivVX05e^Gc{we6=SA8J`f|KIScLi^esyesH7% zFn*JPMG6JZdqR{BkSz_&i{T7NPvmmeQ?Ba9Z&IpO7PZVXX7h}qtSPI?lab1bylUEv zho*%jw9VBj2c*S|g@JCty*`}M11c<%)~g|D2|xGw$(KBdI4)|JvO$_vzRa0-#g^(J9GY<-+7tu-#lj6jp%d|u0Q+`KmDWc*nu%<2zgvlCwIiv-}*g z1yweuTudn!L#oUZu;NaSI!E`PDc1`@i#T{>)E) zkfLn3_0AnGZ>GF*W6G;H_qlL>jmj$WWyV{(HNW@mJ+{_*JaPR3d((#f;~9s0cj>8& zSXiFA7&D!0^Vx5{#p}1neDwWKGaPhC(gd3y(NrZ?Iif+Aw7-Ev(o~iA>6Q!fS%q~W zG6*>@I~>ky&aHQelpzir)_F~TReK7OX`2pXp=oS;0QBd45vbDxd>|my-XBpl)`zb( zt7H!UxO!QOKahvon{}1eB;jf^qP?L#R4q9xB#r};F!ltkP@$C|)Y>CeT`NJtPrU1< zCa)T@tRajel+p-cKsYR>r7xM*haspcze!k`x^pp`NCO%ukprhJ8#>eS4w zf)Hcs_Unq48f6JWjn)cVTbyk?VM+V%*8-+4(9++Qa~9M1MC-=*$5JT0Qd?mg3)WCu zi$%4zeT&py9*1okEoTwVO91P}LkdjO(9}K)qbNN+s;CS_S$PmbS&^3&Rn-!vs@eww zHs)R44AWw_-KozD;hT*}_^r<>sLz}Dt(Q)z0NU+}$7Bnz-rEsHs%>5%jTKwxH;AJM zO^L9jCuQjlWj5t_ykwF&j^;J9MMhrK3_2lK&s)~}F&EZ5e1DpAFt;qrImgqSMbR+J zYXT*y%95=QU1B)eVC~`)^hW0q&4kIFZ{Nd+xvfdslTRdlD4Tnv`VzH!qctWq=XEYiyT3=&r zeU1KLNT=H+Uc}6%Q<5Yi?Z$LFA)Qf*bCo{;ni3Vn2o<6=WQVVlFQ+VK3&KD!*f>Yj z+W}irGbZZPM5;&99kFQ>u3x>x$KQK}Tkkx{KmF3n>>VES^!05%`}Je$GN-By|Kw}8 z5K{4;Ya@ESE+6^mM~K$0aDFXdBV5q!q)e)mej?}$Ba|zdO%54G-pg<}Z-_!kCu|Va zKG|Y|l#(C_2$djcO7bj2I)_x6Cep~!HmZ@7%PEbS&`$&I?2Wm)of1U>f$~(R%4n=} z6h(nEHC1iN3qKjQ&SPX(dhViXXq@nJns#~_2HwYDt)GTE=cpP(R@QAIxj+huR>DIA zlt1`~S`jFP*6MUS5=S9v6q1CRDAbqVGt3A5!ONJz&2hgC50tU zVuC0D1h%p0ARvr_Qv<&bY%|^-^?bFZ#I;U38I8RSxl9o!Va2(VHW~bc`}!auufwT+?!iozByws z9P-E`7YU-6w{Gw8(yKQ(obB`Ul_A@Mn47WY=I#V3Ac!J%w$723C6kk5veJR>v3}uk zwzt>mcM8hc1hy^_s)nGm!CI$-31^68!qwd+!Haj<+aI&`p(h!xog?UsNPB`#H$W?i zqhUOm5(GKD;XXpARMiRf;(%C4lC_Yg@4ZPX2nf4tbkY$)_Y%!=pX0q7NEKnMm${Ym zC9`S9d|r^}1$my4EtgCt6UO5)$44ie93L|ok6A1ha6|;)Ia&+HvyQveDkO&n*ZX5Kk~xwO(&DTQkCV;Icu(*hDza(#Ives%Wr=E`@DU7pF4*$e&Kgt=J_Wd;zQ3}vWa<=KMmf#ULJgf*6X#|2emu}uZl6zssunK~mHD~RF%Fc?=7c7`Zn z!5J)?P;>~g99uY|xWkiI6|dYr;qJKM(Mtm&ZHeNLo(yodp)m2j{V1sMN4p|4M5ZyWBh#>hKi=HS(KH>j3xoL z5kzrBn1tkoL@GfTcfAkB$8DlR<*~@tC$d}9&@?R>$Vqe*qO{*sSXw-_bSMSj{SkH5 z&^U{A0@GNEq9iYhHW$Q8n;PRcM8-i;`YlFTH|^%YQP(w9QEW^b)q3YI&S#u*>hNT^jy9P>>_AO(SL1zkbBd_7*?(k*9F^7;AH`yl??& zOTP8;ZFVOmI~yZLoq$25zo_tI5Dkzukun&mMj+h=)vL@zX?k>>vKf`eJZ`_4VY+Au`b2Q23qo&EqJ6-Fdz z1hzOvh!cXKi`u*jw#L>uMNxs&B;6r-kyFpcbX5d6r0!rE%gM=-`JyDxN~*FV&vWLp zIkVZ6lan!rhesS99dmp%W{$6*``#Ych>}TGV(SWV(9c~|th5XZ1g#`uzcb+Zhes zy#B;f|HtwEJHIubP5-T?D*uFlfow5CN`O+Lr4b2EVOYXX-J2D$2hnC6fE2c!?6xRb zV`%D{B3m+_O_`35nXRv}v9Zo@IHErs(CKw)s+6j%h@+5BH>Ee|(p8qGDlm;D4!qoC zuzn8L^fBdxvdoDh4Xc!CA&^=U>yWe))3}hb?2|5+Y;6?SrlQ|ZxqfAxH*OvA)^5g| zH}CVxTeo@f^}9TKeVw2D@HLJnB}trc?_kF7ef4FAy$<#Hb#})M)q7wX%DQfw3xD`*+J?e3onv&KUDvgnHnweBjcpr^ZQDj;+i7gujT_sxZGY$e zKI8k9e;FAg*S_}JbIp0oMpPQBEwt}Tr}jy=9Hv`5VaihobSY|(UpyG~$%#;Q;uy2i zx8E-gbhs2Kccy3U=A04p`_eio9VVyG%o8MxMrZBJD#?^mBrwzs!V`(^Rs!2C?3-_9 zS&S5=;to>0_A>S~MjoU?4sLw<7=@t4Iadm5sEIGA^b2(VE;5?>X-<SQL4Q?rcm)=;PCzArF+K%t$xyi2cJ{#j=@?h9^+D`I6J@)wjPpbuwL&}fcw`gyD z%1b?+@vge#M-6%m4TC)6Sn{_%9R<6Cb^WW9d4c{m+-ygGQ@r8K6w(7)LtR<6ieVG*RoX|L^3O%j3unamW zB77G_zYZ1B0;(Sq%h_HwgeG>{MKUYqII20FU{jxy4(pzKa~T`ndOxK7+5i zTocWzAH#plV0Rf6DnVYjtW{G{R)pzt5yJxqmTL67QRkNdEq1;bQZN;eqOJ-@n^%oy z4e4~G`#oxuAd5qdQ3|o+flo;Yr8!%1^ff0p1!j2QY04^zMrXO?MeY37k-+y`yXv~E zr02~_5-8A^WhY+^9sa#q5ZLr+>bijM?{dZ)-=|z4H#eoh%8#6N3vs9r(6C&5LrS${ zvy^Z1_q3RE0}|@QhaUfEPj+*%2`=RxmHOZ^V(3;S}24T+|?S>r&>pqI#Nx>quZ8wP9e5TpL(t?Fu>$al#vr<`n#gh4*FWokbIy5&Yq#%-Rkj(p+n@9}ZE)uwM z9+#_J1-b%BG1Dpo0SMpcgPcT8{5jhDcY2vzRE60ETb>P(?y>HraUs0V9(3wE_kUcq z{jJ4t@>aT=>`l_G>353FHZ$Wj{NgBVGia^rF*cen!pHD_o8Y~~n{q?i0ZuvPRQoj}~VJp$GK_ZHz!oyorW#iY`$9JC!b1EHmWW?_lrT zH!>3ZVkoJLUV=lHtF(34)ff$S;qv~c7ZvjeXH~V~gk7!i8yG%pWMS5C!RtZao+oyQ zf?l&Irbxf%wcIbk9x%W&@R3*bv;03%^=sf2h5y|lC0x+N)cNW#T0Xt_m1a_fV5nW8 zscc5~3$Egi;MC6q!u2wkI;PJ8oU(L`|VPf}P? zy^qJW_tPU?F>oXT?;WVcP@nFX3pLDG`7jb$Ov>u>dybwJGH6r`wvGXVe|FwCvfUqx zO!BcY^++PMVYpw6hj@~!a^(}J&6 z@>Sve|MEBIn-sko+xY#_!wW4!h`zy;-?^hniYvLNqf#R!m6y|EWw`o0_yIp?@x$r$ z=5bZWC&seHL8{_W&stjfw(Pcfp`}KtH`^-}PwijI>OSMpSVZShUsK4nYou}4x{1l9 zj%=P?Tp0=BBA61YL?H82jFRguV=ho?6w@RQgK zDIP-q8Ba@|jelDqmO-ysF7s6$oj4X;;bIUplG&+U=|jjA87xrKTtZ+>lPN4NJ6*gM zyFYof|EB7-597GSxStEfKJm57wV>pw`a8w+){3ugiC77Cap<>~_9j`|+Qb32jaHw5 ziJ$~sY=eIKIg^Qni7;zEgJxQ)sNKFIX&zUTf#kfr@t@sKi@|y?>u0D~PGG zFMnZT?`phvsqi~6=g!=YD|LvV@W{FID=oLI*Zep2gIVDKU=KI@mGVMh?;$)+YK}lA z!2PB#X0o6Az?;5Y3h8UfC8tlTKh{}%o*TWKuOTbi71?Gx(1-$&55Yk`}KY>oR@I^AT+Qf|l> zZ!26VWf0}FBj%9b*^W5D#{|nlA}xydqqN?;V9!C;e}}5N0dUU!-=Pu*T8h4XZGIir z#C+ZBy;S%;*?mxa_41cRoXh`OxvkN^xa(`1#tdrMy;mqJ6o$<8wZ)12+c?MOmOmQCx5B)X*VUCe9gbh_Gi6pGCo6v-F>0M}Si zV*0Qi$$N|gieap1CUh3%WZAY6DM-;^8Q4@{XGb-)=#@m{Nsh1Zinv4tNOExI85!;B zwcX8v2j5d7=|0u`>UQV!UifM@0k`5M(w4hT>Qz_U*vkY6f%hjA&@foQ1V{KX{d38E z$1<0AEWF7efz_Ivw|UZzse-!uYr<<@b;2pO@@@x+8@I6$LX*Q94L>*mhpRc-N@{2r zf&-!>F+=<`>_u_%qKip&4>d&#!?Y#_whSFepUI$Ymi~ ziw)D!3IRlO+BB+Dy@?UT*b$?hWurTS~uGj*&OeyHWd1m@@q@O>hNGbtgD^Qion}CIZuCUfidfIt%I-m zl@tGc9YI1^3M#h321={13bxOtwFE<`Df|c_11U?e=d3$`tw)f@d^BGo_mx8g1|O0o z3Y^IS^-q%+O*kuDGMYJE_zyu;w`oBU9Zc%7us#3AOa<2O>8aYQ=hurHM${PjyKqt2 ziH}PPfRK*->jw~8VYTyU7kt<1Ei3to{t>Olt{4ept?yoa2rKMx3JulFD?hdJE zsq8Q51U?LzI-ymmxG^x;7}HqRb0?ks_x6CpbgyfQXS6?c$>Z^riyMb(Lrx$`B$!zk zGZZFtpdY5RRyY=XhW4MFt)dxBh?P}gHe}Tg72hDrMQ40Zcq&5U?@|RtB1L$?ut?QI ztUn8(!*=lnGqxc$E|<$Q40{f_OD z&HgHcvK2!3Zvz7rpB zU%h_+xoS9pT|;XABpd#JlIG7P7zDfP=|qBk5K=R<+VAKU&?_ceGky(IB( z`;aaN5bAvjKKpF(nhXCRzt;t|ms>a6THdJVeFz3%H`&X+$+1(V_0-mw6E_5+anf4H zexTpEpXVejwh_0q#kn(DgqP!YON1M!rrR^v^%i;#D-5In2%*qEW+0c6_B8}mS+ls= zuf?2Dl}aRt)F)vPM1H~b{li(yOyEcW6_l8pPbxvxgfq&uB?!Tn(Z|822?yMB&{r)`EGCKy z3od8D zw{7dXDHUKdd@MV?|GU$IH2ZfKvGuj6^LdlHxw|Tt`^58#sEK{KzO}ch7!wzPf#37* zB@)B^>=G(g7yGt*xJ5#!|4=OU!s$q27PE*&6Dn9v;ROD>c)aXLiTJ$9q@$v}W2b!OB$B9T(pmxk=6MR<<|bD>i{$^x8$c?xw^Is z(fMP-XJ5n+zoYfvcVG7Im!lhRHQqs2n_Mq~Jw^XrUDB%mnZC0Z_zPZ_A-2S`NM9eL zTNp4r9-UOs63-oN2%*g`v3zRWHQ%zH}i`d$cz&BKn4A}7R_$hI52+jDx}@dnl} zbqIXp7yS`>?;CbEi&eZi9_Pq)=;s8o!1o#$c>@-LjY})wb$ROXiVdBLeO_)rU`mf+ zqTX@Oa4>rcE|3gyiE~w&ysEt09|Y0+y3E9#r13p8lD~dF0FhO$qr~0nVA`|V@<}YI zw!P~uVW@B0EJ(vsUO^NM_%)3wp7a>k9>mB8vND2s0bS{GP9Vdl*=5y

cj~jTcUmz)`v*;ao<@;UmVG?;Fm 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 z Date: Sat, 13 Aug 2011 17:40:43 +0200 Subject: [PATCH 075/215] Fixed: Dll symbols export under Windows for qtpropertybrowser --- .../src/3rdparty/qtpropertybrowser/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt index 38131aeb3..f4ed13e0a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/3rdparty/qtpropertybrowser/CMakeLists.txt @@ -88,7 +88,7 @@ TARGET_LINK_LIBRARIES(qt_property_browser ${QT_LIBRARIES}) ADD_DEFINITIONS(${QT_DEFINITIONS}) ADD_DEFINITIONS(-DQT_DLL) -#ADD_DEFINITIONS(-DQT_QTPROPERTYBROWSER_EXPORT) +ADD_DEFINITIONS(-DQT_QTPROPERTYBROWSER_EXPORT) ADD_DEFINITIONS(-DQT_PLUGIN) #ADD_DEFINITIONS(-DQT_NO_DEBUG) ADD_DEFINITIONS(-DQT_SHARED) From 28c4623f27ded096b0b24a020b60cc661b7b3134 Mon Sep 17 00:00:00 2001 From: aquiles Date: Sat, 13 Aug 2011 19:11:15 +0200 Subject: [PATCH 076/215] Changed: #1206 fixed bug when exception in sound system wouldnt close the settings group leading to inconsistent ini file --- .../src/plugins/object_viewer/sound_system.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.cpp index ba64d834e..07101aa85 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.cpp @@ -107,6 +107,9 @@ void CSoundSystem::init() std::string mess = std::string("Unable to init sound :") + e.what(); nlwarning ("Init sound: %s", mess.c_str()); _AudioMixer = NULL; + QSettings *settings = Core::ICore::instance()->settings(); + if (settings->group() == Constants::OBJECT_VIEWER_SECTION) + settings->endGroup(); return; } From d98511337d02ac8899d4e833043c04ca66551f17 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 13 Aug 2011 23:36:37 +0200 Subject: [PATCH 077/215] Fixed: #1333 Support 3ds Max 2012 SDK. --- code/CMakeModules/Find3dsMaxSDK.cmake | 11 +++-- code/CMakeModules/FindFreeType.cmake | 2 +- .../nel/tools/3d/ligo/plugin_max/DllEntry.cpp | 3 ++ .../tools/3d/ligo/plugin_max/max_to_ligo.cpp | 7 +++- code/nel/tools/3d/ligo/plugin_max/script.cpp | 31 +++++++++----- .../3d/plugin_max/nel_export/DllEntry.cpp | 4 +- .../nel_export/nel_export_script.cpp | 7 +++- .../tools/3d/plugin_max/nel_export/std_afx.h | 31 +++++++++----- .../tools/3d/plugin_max/nel_mesh_lib/StdAfx.h | 7 +++- .../plugin_max/nel_mesh_lib/export_misc.cpp | 4 ++ .../plugin_max/nel_mesh_lib/export_script.cpp | 10 ++++- .../nel_patch_converter/DllEntry.cpp | 3 ++ .../plugin_max/nel_patch_converter/script.cpp | 40 +++++++++++++------ .../3d/plugin_max/nel_patch_edit/np_mods.cpp | 4 ++ .../3d/plugin_max/nel_patch_lib/rpo2nel.cpp | 7 +++- .../plugin_max/nel_patch_paint/DllEntry.cpp | 3 ++ .../nel_patch_paint/nel_patch_paint.h | 7 +++- .../nel_vertex_tree_paint/dllmain.cpp | 4 +- .../3d/plugin_max/tile_utility/DllEntry.cpp | 3 ++ 19 files changed, 143 insertions(+), 45 deletions(-) diff --git a/code/CMakeModules/Find3dsMaxSDK.cmake b/code/CMakeModules/Find3dsMaxSDK.cmake index 52c4cc011..ddec22f90 100644 --- a/code/CMakeModules/Find3dsMaxSDK.cmake +++ b/code/CMakeModules/Find3dsMaxSDK.cmake @@ -12,32 +12,35 @@ endif(MAXSDK_INCLUDE_DIR) find_path(MAXSDK_INCLUDE_DIR max.h PATHS + "$ENV{ADSK_3DSMAX_SDK_2012}/maxsdk/include" + "$ENV{3DSMAX_2011_SDK_PATH}/maxsdk/include" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2010 SDK/maxsdk/include" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2009 SDK/maxsdk/include" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2008 SDK/maxsdk/include" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 9 SDK/maxsdk/include" - "$ENV{3DSMAX_2011_SDK_PATH}/maxsdk/include" ) find_path(MAXSDK_CS_INCLUDE_DIR bipexp.h PATHS + "$ENV{ADSK_3DSMAX_SDK_2012}/maxsdk/include/CS" + "$ENV{3DSMAX_2011_SDK_PATH}/maxsdk/include/CS" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2010 SDK/maxsdk/include/CS" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2009 SDK/maxsdk/include/CS" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2008 SDK/maxsdk/include/CS" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 9 SDK/maxsdk/include/CS" - "$ENV{3DSMAX_2011_SDK_PATH}/maxsdk/include/CS" ) MACRO(FIND_3DS_LIBRARY MYLIBRARY MYLIBRARYNAME) FIND_LIBRARY(${MYLIBRARY} NAMES ${MYLIBRARYNAME} PATHS + "$ENV{ADSK_3DSMAX_SDK_2012}/maxsdk/lib" + "$ENV{3DSMAX_2011_SDK_PATH}/maxsdk/lib" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2010 SDK/maxsdk/lib" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2009 SDK/maxsdk/lib" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 2008 SDK/maxsdk/lib" "$ENV{PROGRAMFILES}/Autodesk/3ds Max 9 SDK/maxsdk/lib" - "$ENV{3DSMAX_2011_SDK_PATH}/maxsdk/lib" - ) + ) ENDMACRO(FIND_3DS_LIBRARY MYLIBRARY MYLIBRARYNAME) FIND_3DS_LIBRARY(MAXSDK_CORE_LIBRARY core) diff --git a/code/CMakeModules/FindFreeType.cmake b/code/CMakeModules/FindFreeType.cmake index 0ba439fb9..b9d00d96a 100644 --- a/code/CMakeModules/FindFreeType.cmake +++ b/code/CMakeModules/FindFreeType.cmake @@ -41,7 +41,7 @@ IF(FREETYPE_ADDITIONAL_INCLUDE_DIR) ENDIF(FREETYPE_ADDITIONAL_INCLUDE_DIR) FIND_LIBRARY(FREETYPE_LIBRARY - NAMES freetype libfreetype freetype219 + NAMES freetype libfreetype freetype219 freetype246 PATHS $ENV{FREETYPE_DIR}/lib /usr/local/lib diff --git a/code/nel/tools/3d/ligo/plugin_max/DllEntry.cpp b/code/nel/tools/3d/ligo/plugin_max/DllEntry.cpp index 3cf88f4a7..ee9964566 100644 --- a/code/nel/tools/3d/ligo/plugin_max/DllEntry.cpp +++ b/code/nel/tools/3d/ligo/plugin_max/DllEntry.cpp @@ -18,6 +18,7 @@ #include "nel/misc/app_context.h" #include #include "../../plugin_max/nel_3dsmax_shared/nel_3dsmax_shared.h" +#include extern ClassDesc2* GetLigoscapeDesc(); @@ -44,7 +45,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) if (!controlsInit) { controlsInit = TRUE; +#if MAX_VERSION_MAJOR < 14 InitCustomControls(hInstance); // Initialize MAX's custom controls +#endif InitCommonControls(); // Initialize Win95 controls } diff --git a/code/nel/tools/3d/ligo/plugin_max/max_to_ligo.cpp b/code/nel/tools/3d/ligo/plugin_max/max_to_ligo.cpp index 7500b027b..eb4951991 100644 --- a/code/nel/tools/3d/ligo/plugin_max/max_to_ligo.cpp +++ b/code/nel/tools/3d/ligo/plugin_max/max_to_ligo.cpp @@ -17,7 +17,12 @@ #include // From MAXSDK -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +#else +# include +#endif #include "max_to_ligo.h" diff --git a/code/nel/tools/3d/ligo/plugin_max/script.cpp b/code/nel/tools/3d/ligo/plugin_max/script.cpp index 5d6381303..90050304f 100644 --- a/code/nel/tools/3d/ligo/plugin_max/script.cpp +++ b/code/nel/tools/3d/ligo/plugin_max/script.cpp @@ -19,17 +19,30 @@ #include // Various MAX and MXS includes -#include -#include -#include -#include -#include -#include -#include -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +# include +# include +# include +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif #include #include -#include // Visual #include diff --git a/code/nel/tools/3d/plugin_max/nel_export/DllEntry.cpp b/code/nel/tools/3d/plugin_max/nel_export/DllEntry.cpp index 61337b328..7e6ded529 100644 --- a/code/nel/tools/3d/plugin_max/nel_export/DllEntry.cpp +++ b/code/nel/tools/3d/plugin_max/nel_export/DllEntry.cpp @@ -19,7 +19,7 @@ #include "nel/3d/register_3d.h" #include "nel/misc/app_context.h" #include "../nel_3dsmax_shared/nel_3dsmax_shared.h" - +#include extern ClassDesc2* GetCNelExportDesc(); @@ -42,7 +42,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) if (!controlsInit) { controlsInit = TRUE; +#if MAX_VERSION_MAJOR < 14 InitCustomControls(hInstance); // Initialize MAX's custom controls +#endif InitCommonControls(); // Initialize Win95 controls } diff --git a/code/nel/tools/3d/plugin_max/nel_export/nel_export_script.cpp b/code/nel/tools/3d/plugin_max/nel_export/nel_export_script.cpp index 9218e73ce..d26af56ca 100644 --- a/code/nel/tools/3d/plugin_max/nel_export/nel_export_script.cpp +++ b/code/nel/tools/3d/plugin_max/nel_export/nel_export_script.cpp @@ -16,7 +16,12 @@ #include "std_afx.h" #include "nel_export.h" -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +#else +# include +#endif #include "../nel_mesh_lib/export_nel.h" #include "../nel_mesh_lib/export_appdata.h" diff --git a/code/nel/tools/3d/plugin_max/nel_export/std_afx.h b/code/nel/tools/3d/plugin_max/nel_export/std_afx.h index 326c0b9c1..099f2f882 100644 --- a/code/nel/tools/3d/plugin_max/nel_export/std_afx.h +++ b/code/nel/tools/3d/plugin_max/nel_export/std_afx.h @@ -26,16 +26,29 @@ #include #include #undef STRICT -#include -#include -#include -#include -#include -#include -#include -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +# include +# include +# include +# include +# include +# include +# include +# include +#else +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif #include -#include #include #ifdef min #undef min diff --git a/code/nel/tools/3d/plugin_max/nel_mesh_lib/StdAfx.h b/code/nel/tools/3d/plugin_max/nel_mesh_lib/StdAfx.h index 65b65651e..b3626b896 100644 --- a/code/nel/tools/3d/plugin_max/nel_mesh_lib/StdAfx.h +++ b/code/nel/tools/3d/plugin_max/nel_mesh_lib/StdAfx.h @@ -32,7 +32,12 @@ #include #include #include -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +#else +# include +#endif //#include // Character Studio SDK include diff --git a/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_misc.cpp b/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_misc.cpp index 0b2426758..a03beb000 100644 --- a/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_misc.cpp +++ b/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_misc.cpp @@ -260,7 +260,11 @@ Control* CExportNel::getControlerByName (Animatable& node, const char* sName) if (strcmp (paramDef.int_name, sName)==0) { // ok, return this subanim +#if MAX_VERSION_MAJOR >= 14 + return param->GetControllerByID(id); +#else return param->GetController(id); +#endif } } } diff --git a/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_script.cpp b/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_script.cpp index 6f1713683..7fd0ad150 100644 --- a/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_script.cpp +++ b/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_script.cpp @@ -15,8 +15,14 @@ // along with this program. If not, see . #include "stdafx.h" -#include -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +# include +#else +# include +# include +#endif #include "export_nel.h" #include "export_appdata.h" diff --git a/code/nel/tools/3d/plugin_max/nel_patch_converter/DllEntry.cpp b/code/nel/tools/3d/plugin_max/nel_patch_converter/DllEntry.cpp index 22ae76790..50f249c31 100644 --- a/code/nel/tools/3d/plugin_max/nel_patch_converter/DllEntry.cpp +++ b/code/nel/tools/3d/plugin_max/nel_patch_converter/DllEntry.cpp @@ -20,6 +20,7 @@ #include "nel/misc/debug.h" #include "nel/misc/app_context.h" #include "../nel_3dsmax_shared/nel_3dsmax_shared.h" +#include extern ClassDesc2* GetPO2RPODesc(); extern ClassDesc* GetRPODesc(); @@ -58,7 +59,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) { // This method has been deprecated. controlsInit = TRUE; +#if MAX_VERSION_MAJOR < 14 InitCustomControls(hInstance); // Initialize MAX's custom controls +#endif InitCommonControls(); // Initialize Win95 controls } return (TRUE); diff --git a/code/nel/tools/3d/plugin_max/nel_patch_converter/script.cpp b/code/nel/tools/3d/plugin_max/nel_patch_converter/script.cpp index b324c2cc6..1dfdc97f4 100644 --- a/code/nel/tools/3d/plugin_max/nel_patch_converter/script.cpp +++ b/code/nel/tools/3d/plugin_max/nel_patch_converter/script.cpp @@ -22,24 +22,38 @@ #define _CRT_SECURE_NO_DEPRECATE #include -#include -#include -// Various MAX and MXS includes -#include -#include -#include -#include -#include -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +# include +# include +# include +# include +# include +# include +# include +# include +#else +# include +# include +// Various MAX and MXS includes +# include +# include +# include +# include +# include +# include +// define the new primitives using macros from SDK +# include +#endif + #include #include #include #include -// define the new primitives using macros from SDK -#include #undef _CRT_SECURE_NO_DEPRECATE @@ -109,9 +123,9 @@ def_visible_primitive( set_tile_bank, "NelSetTileBank"); def_visible_primitive( export_zone, "ExportRykolZone"); def_visible_primitive( import_zone, "NeLImportZone"); -/* permettre l'acces à auto/manual intrior edges +/* permettre l'acces Eauto/manual intrior edges faire une methode pour interfacer la fonction compute interior edge -donner un acces à tiledmode/patchmode (on/off) +donner un acces Etiledmode/patchmode (on/off) faire un getselectedvertex faire un getselectedpatch faire un getselectedtile */ diff --git a/code/nel/tools/3d/plugin_max/nel_patch_edit/np_mods.cpp b/code/nel/tools/3d/plugin_max/nel_patch_edit/np_mods.cpp index c2696674a..8d7467a62 100644 --- a/code/nel/tools/3d/plugin_max/nel_patch_edit/np_mods.cpp +++ b/code/nel/tools/3d/plugin_max/nel_patch_edit/np_mods.cpp @@ -17,6 +17,8 @@ #include #include "../nel_3dsmax_shared/nel_3dsmax_shared.h" +#include + HINSTANCE hInstance; int controlsInit = FALSE; @@ -42,7 +44,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) controlsInit = TRUE; // jaguar controls +#if MAX_VERSION_MAJOR < 14 InitCustomControls(hInstance); +#endif #ifdef OLD3DCONTROLS // initialize 3D controls diff --git a/code/nel/tools/3d/plugin_max/nel_patch_lib/rpo2nel.cpp b/code/nel/tools/3d/plugin_max/nel_patch_lib/rpo2nel.cpp index edf2f77e6..d99dc0f98 100644 --- a/code/nel/tools/3d/plugin_max/nel_patch_lib/rpo2nel.cpp +++ b/code/nel/tools/3d/plugin_max/nel_patch_lib/rpo2nel.cpp @@ -18,7 +18,12 @@ // For MAX_RELEASE #include -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +#else +# include +#endif #include "rpo.h" #include "nel/3d/zone.h" diff --git a/code/nel/tools/3d/plugin_max/nel_patch_paint/DllEntry.cpp b/code/nel/tools/3d/plugin_max/nel_patch_paint/DllEntry.cpp index a3578a0bc..b4b79da80 100644 --- a/code/nel/tools/3d/plugin_max/nel_patch_paint/DllEntry.cpp +++ b/code/nel/tools/3d/plugin_max/nel_patch_paint/DllEntry.cpp @@ -3,6 +3,7 @@ #include "nel/misc/debug.h" #include "nel/misc/app_context.h" #include "../nel_3dsmax_shared/nel_3dsmax_shared.h" +#include HINSTANCE hInstance; int controlsInit = FALSE; @@ -26,7 +27,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) controlsInit = TRUE; // jaguar controls +#if MAX_VERSION_MAJOR < 14 InitCustomControls(hInstance); +#endif #ifdef OLD3DCONTROLS // initialize 3D controls diff --git a/code/nel/tools/3d/plugin_max/nel_patch_paint/nel_patch_paint.h b/code/nel/tools/3d/plugin_max/nel_patch_paint/nel_patch_paint.h index 944807aca..3563b4712 100644 --- a/code/nel/tools/3d/plugin_max/nel_patch_paint/nel_patch_paint.h +++ b/code/nel/tools/3d/plugin_max/nel_patch_paint/nel_patch_paint.h @@ -3,7 +3,12 @@ #include "resource.h" #include -#include +#include +#if MAX_VERSION_MAJOR >= 14 +# include +#else +# include +#endif #include "namesel.h" #include "nsclip.h" #include "sbmtlapi.h" diff --git a/code/nel/tools/3d/plugin_max/nel_vertex_tree_paint/dllmain.cpp b/code/nel/tools/3d/plugin_max/nel_vertex_tree_paint/dllmain.cpp index 4dc068eea..1f04fe088 100644 --- a/code/nel/tools/3d/plugin_max/nel_vertex_tree_paint/dllmain.cpp +++ b/code/nel/tools/3d/plugin_max/nel_vertex_tree_paint/dllmain.cpp @@ -1,6 +1,6 @@ #include "vertex_tree_paint.h" #include "../nel_3dsmax_shared/nel_3dsmax_shared.h" - +#include HINSTANCE hInstance; @@ -18,7 +18,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) switch (fdwReason) { case DLL_PROCESS_ATTACH: +#if MAX_VERSION_MAJOR < 14 InitCustomControls(hInstance); // Initialize MAX's custom controls +#endif InitCommonControls(); // Initialize Win95 controls break; } diff --git a/code/nel/tools/3d/plugin_max/tile_utility/DllEntry.cpp b/code/nel/tools/3d/plugin_max/tile_utility/DllEntry.cpp index d1c6c7d6b..26e2d14f8 100644 --- a/code/nel/tools/3d/plugin_max/tile_utility/DllEntry.cpp +++ b/code/nel/tools/3d/plugin_max/tile_utility/DllEntry.cpp @@ -20,6 +20,7 @@ #include "nel/misc/app_context.h" #include "../nel_3dsmax_shared/nel_3dsmax_shared.h" #include +#include extern ClassDesc2* GetTile_utilityDesc(); extern ClassDesc* GetRGBAddDesc(); @@ -46,7 +47,9 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL,ULONG fdwReason,LPVOID lpvReserved) if (!controlsInit) { controlsInit = TRUE; +#if MAX_VERSION_MAJOR < 14 InitCustomControls(hInstance); // Initialize MAX's custom controls +#endif InitCommonControls(); // Initialize Win95 controls } From 80983a5ab67d642ff30b53c4a2921d2a447d89de Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 14 Aug 2011 11:47:34 +0200 Subject: [PATCH 078/215] Added: Hicolor and XPM icons for GNU/Linux --- code/ryzom/client/unix/CMakeLists.txt | 9 +- code/ryzom/client/unix/ryzom.xpm | 621 +++++++++++++++++++++++ code/ryzom/client/unix/ryzom_128x128.png | Bin 0 -> 37774 bytes code/ryzom/client/unix/ryzom_16x16.png | Bin 0 -> 1018 bytes code/ryzom/client/unix/ryzom_22x22.png | Bin 0 -> 1684 bytes code/ryzom/client/unix/ryzom_24x24.png | Bin 0 -> 1988 bytes code/ryzom/client/unix/ryzom_32x32.png | Bin 0 -> 3022 bytes code/ryzom/client/unix/ryzom_48x48.png | Bin 0 -> 6266 bytes 8 files changed, 629 insertions(+), 1 deletion(-) create mode 100644 code/ryzom/client/unix/ryzom.xpm create mode 100644 code/ryzom/client/unix/ryzom_128x128.png create mode 100644 code/ryzom/client/unix/ryzom_16x16.png create mode 100644 code/ryzom/client/unix/ryzom_22x22.png create mode 100644 code/ryzom/client/unix/ryzom_24x24.png create mode 100644 code/ryzom/client/unix/ryzom_32x32.png create mode 100644 code/ryzom/client/unix/ryzom_48x48.png diff --git a/code/ryzom/client/unix/CMakeLists.txt b/code/ryzom/client/unix/CMakeLists.txt index df17996fe..005c6bdb4 100644 --- a/code/ryzom/client/unix/CMakeLists.txt +++ b/code/ryzom/client/unix/CMakeLists.txt @@ -1,4 +1,11 @@ CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/ryzom.desktop.in" "${CMAKE_CURRENT_BINARY_DIR}/ryzom.desktop") -INSTALL(FILES ryzom.png DESTINATION share/pixmaps) INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/ryzom.desktop" DESTINATION share/applications) +INSTALL(FILES ryzom.png DESTINATION share/pixmaps) +INSTALL(FILES ryzom.xpm DESTINATION share/pixmaps) +INSTALL(FILES res/ryzom16x16.png DESTINATION share/icons/hicolor/16x16/apps RENAME ryzom.png) +INSTALL(FILES res/ryzom22x22.png DESTINATION share/icons/hicolor/22x22/apps RENAME ryzom.png) +INSTALL(FILES res/ryzom24x24.png DESTINATION share/icons/hicolor/24x24/apps RENAME ryzom.png) +INSTALL(FILES res/ryzom32x32.png DESTINATION share/icons/hicolor/32x32/apps RENAME ryzom.png) +INSTALL(FILES res/ryzom48x48.png DESTINATION share/icons/hicolor/48x48/apps RENAME ryzom.png) +INSTALL(FILES res/ryzom128x128.png DESTINATION share/icons/hicolor/128x128/apps RENAME ryzom.png) diff --git a/code/ryzom/client/unix/ryzom.xpm b/code/ryzom/client/unix/ryzom.xpm new file mode 100644 index 000000000..43748c2ce --- /dev/null +++ b/code/ryzom/client/unix/ryzom.xpm @@ -0,0 +1,621 @@ +/* XPM */ +static char * ryzom_32x32_xpm[] = { +"32 32 586 2", +" c None", +". c #CACCCA", +"+ c #737766", +"@ c #747674", +"# c #717175", +"$ c #6E6E71", +"% c #8A9293", +"& c #888477", +"* c #63605B", +"= c #83856E", +"- c #888C7E", +"; c #666D60", +"> c #4A483C", +", c #433729", +"' c #8A816C", +") c #48453A", +"! c #3D3A32", +"~ c #6B6A61", +"{ c #8A8B8A", +"] c #989592", +"^ c #948F87", +"/ c #7E7861", +"( c #59513F", +"_ c #65604F", +": c #918F86", +"< c #5F5E45", +"[ c #4B5735", +"} c #3F462F", +"| c #443F31", +"1 c #49483C", +"2 c #7F7B6F", +"3 c #6E6049", +"4 c #696C6D", +"5 c #483C2F", +"6 c #403623", +"7 c #5B4C3B", +"8 c #514237", +"9 c #66553A", +"0 c #645B3E", +"a c #878B79", +"b c #93948B", +"c c #605C3B", +"d c #4D5032", +"e c #373117", +"f c #48512C", +"g c #52633A", +"h c #2B2818", +"i c #664E32", +"j c #37341B", +"k c #636156", +"l c #737769", +"m c #454935", +"n c #3F4430", +"o c #666637", +"p c #5E712F", +"q c #535435", +"r c #7D7E6A", +"s c #717267", +"t c #6E6B6A", +"u c #545B46", +"v c #6D715E", +"w c #767960", +"x c #575736", +"y c #42512E", +"z c #303828", +"A c #3E3C22", +"B c #4D4B1E", +"C c #323916", +"D c #565137", +"E c #4A3D26", +"F c #3E3E24", +"G c #746E37", +"H c #5F5B2F", +"I c #232A10", +"J c #1F2513", +"K c #5B5B3C", +"L c #807B63", +"M c #625C4B", +"N c #6B675B", +"O c #90968D", +"P c #7B7E7B", +"Q c #7C8380", +"R c #4B503E", +"S c #1D2212", +"T c #4D4F31", +"U c #494524", +"V c #7D9442", +"W c #4F6429", +"X c #57512D", +"Y c #4F3C1F", +"Z c #423F1D", +"` c #5F6A35", +" . c #3D481F", +".. c #404024", +"+. c #5F512B", +"@. c #3F381E", +"#. c #6E6C3F", +"$. c #717448", +"%. c #646C48", +"&. c #B4ADA3", +"*. c #E5E6E4", +"=. c #BEBEBE", +"-. c #444737", +";. c #2D311B", +">. c #222911", +",. c #1D220E", +"'. c #231E09", +"). c #3C3922", +"!. c #4C5324", +"~. c #4E4D27", +"{. c #3F3420", +"]. c #3C401F", +"^. c #4E552A", +"/. c #5C703B", +"(. c #778649", +"_. c #43442C", +":. c #736039", +"<. c #897648", +"[. c #847644", +"}. c #635F2D", +"|. c #525B31", +"1. c #647444", +"2. c #504E43", +"3. c #7F7774", +"4. c #8B8788", +"5. c #969591", +"6. c #4B542F", +"7. c #5C6041", +"8. c #5D513D", +"9. c #474638", +"0. c #4B442E", +"a. c #859F4A", +"b. c #373F1C", +"c. c #24260C", +"d. c #161407", +"e. c #2C2D14", +"f. c #3C4420", +"g. c #38401D", +"h. c #657B43", +"i. c #70804E", +"j. c #595C35", +"k. c #706B3A", +"l. c #66562B", +"m. c #3C4120", +"n. c #383B20", +"o. c #444B2E", +"p. c #75766C", +"q. c #DEDBD8", +"r. c #A49E98", +"s. c #7A726F", +"t. c #ADAFAA", +"u. c #9CA098", +"v. c #767164", +"w. c #402815", +"x. c #46362F", +"y. c #47341D", +"z. c #484A2B", +"A. c #576B2A", +"B. c #252210", +"C. c #3F3B17", +"D. c #4A2B12", +"E. c #442812", +"F. c #58582B", +"G. c #555F32", +"H. c #5C6433", +"I. c #48532E", +"J. c #636D42", +"K. c #574E30", +"L. c #524C2B", +"M. c #69623A", +"N. c #888661", +"O. c #CECDBF", +"P. c #E3DFDC", +"Q. c #C9C3C3", +"R. c #898481", +"S. c #5D574D", +"T. c #797271", +"U. c #8D8987", +"V. c #484638", +"W. c #534830", +"X. c #4A4C48", +"Y. c #5E4327", +"Z. c #252108", +"`. c #424030", +" + c #675F3D", +".+ c #32100A", +"++ c #2F180F", +"@+ c #887F5A", +"#+ c #817950", +"$+ c #615F39", +"%+ c #2C2D18", +"&+ c #2A2113", +"*+ c #695534", +"=+ c #947E52", +"-+ c #B8A37E", +";+ c #C3B29E", +">+ c #ACA09D", +",+ c #A09795", +"'+ c #BCBABA", +")+ c #857F7C", +"!+ c #544D3D", +"~+ c #30362B", +"{+ c #59564A", +"]+ c #A2A8A6", +"^+ c #62645A", +"/+ c #535453", +"(+ c #474534", +"_+ c #3D3C1F", +":+ c #575033", +"<+ c #776D51", +"[+ c #ACB2AA", +"}+ c #8A9080", +"|+ c #5A432F", +"1+ c #8B7B65", +"2+ c #746D50", +"3+ c #5F502C", +"4+ c #484224", +"5+ c #414122", +"6+ c #75673B", +"7+ c #9E8149", +"8+ c #BDB7A9", +"9+ c #E5E4E1", +"0+ c #EAE7E2", +"a+ c #9C9491", +"b+ c #ADABA6", +"c+ c #A0A3A0", +"d+ c #A8AFAE", +"e+ c #5C5F51", +"f+ c #5E5D32", +"g+ c #525431", +"h+ c #43382B", +"i+ c #453E34", +"j+ c #41412F", +"k+ c #5D6454", +"l+ c #464941", +"m+ c #60624D", +"n+ c #494644", +"o+ c #39382E", +"p+ c #444630", +"q+ c #584F2D", +"r+ c #444823", +"s+ c #30381B", +"t+ c #49562C", +"u+ c #7E8D54", +"v+ c #939472", +"w+ c #7F7F60", +"x+ c #7C8259", +"y+ c #373727", +"z+ c #3F3834", +"A+ c #85817E", +"B+ c #6D6D66", +"C+ c #666765", +"D+ c #352E27", +"E+ c #49493C", +"F+ c #969985", +"G+ c #A39D98", +"H+ c #928E81", +"I+ c #8C9892", +"J+ c #7E8B8A", +"K+ c #444130", +"L+ c #343021", +"M+ c #2D2D19", +"N+ c #28301C", +"O+ c #5E6257", +"P+ c #596044", +"Q+ c #3F4523", +"R+ c #384123", +"S+ c #65724F", +"T+ c #889176", +"U+ c #E6E6DF", +"V+ c #C2C0BD", +"W+ c #827B6D", +"X+ c #656551", +"Y+ c #3C3B28", +"Z+ c #524F43", +"`+ c #6A644D", +" @ c #655A42", +".@ c #7C735E", +"+@ c #9F9F8D", +"@@ c #B2B5B0", +"#@ c #C9D0CD", +"$@ c #8F9490", +"%@ c #3F4037", +"&@ c #4F504A", +"*@ c #292420", +"=@ c #2F2F19", +"-@ c #2C2710", +";@ c #252514", +">@ c #3F412A", +",@ c #5B6D68", +"'@ c #4D4C3D", +")@ c #282F1A", +"!@ c #526038", +"~@ c #49512D", +"{@ c #444B2D", +"]@ c #7F7B76", +"^@ c #5B5453", +"/@ c #535137", +"(@ c #525535", +"_@ c #565136", +":@ c #48432A", +"<@ c #483F23", +"[@ c #635636", +"}@ c #777664", +"|@ c #D5DBDA", +"1@ c #BECFD0", +"2@ c #A8BCC1", +"3@ c #525553", +"4@ c #4E4432", +"5@ c #4B402D", +"6@ c #403F26", +"7@ c #3B4A23", +"8@ c #323517", +"9@ c #454936", +"0@ c #30330E", +"a@ c #424513", +"b@ c #444F29", +"c@ c #434629", +"d@ c #746D5B", +"e@ c #585532", +"f@ c #4F552C", +"g@ c #677E43", +"h@ c #5A6E3D", +"i@ c #30361E", +"j@ c #424521", +"k@ c #5F5831", +"l@ c #3C3D1D", +"m@ c #414024", +"n@ c #3D391A", +"o@ c #413719", +"p@ c #5E593E", +"q@ c #929D9E", +"r@ c #77868D", +"s@ c #454C50", +"t@ c #2F2F2B", +"u@ c #4A3D1A", +"v@ c #3C451C", +"w@ c #313B19", +"x@ c #30401D", +"y@ c #23270F", +"z@ c #363617", +"A@ c #261605", +"B@ c #43360D", +"C@ c #5C5D29", +"D@ c #8F8D6B", +"E@ c #7F7563", +"F@ c #65512D", +"G@ c #433E23", +"H@ c #546137", +"I@ c #485332", +"J@ c #454D45", +"K@ c #60644A", +"L@ c #666C36", +"M@ c #57462C", +"N@ c #634E2A", +"O@ c #5D4A25", +"P@ c #4A3819", +"Q@ c #312513", +"R@ c #2B1F15", +"S@ c #1F2119", +"T@ c #16170F", +"U@ c #37391C", +"V@ c #373216", +"W@ c #313718", +"X@ c #1F2B11", +"Y@ c #111208", +"Z@ c #566631", +"`@ c #493915", +" # c #4A4314", +".# c #47501C", +"+# c #4A5A23", +"@# c #7B806A", +"## c #757260", +"$# c #3B3E23", +"%# c #3C4124", +"&# c #252314", +"*# c #3A413E", +"=# c #405B52", +"-# c #3E4331", +";# c #4C462A", +"># c #5E4C29", +",# c #654E26", +"'# c #634C2A", +")# c #583E1E", +"!# c #2C1D0E", +"~# c #3E2C17", +"{# c #42230F", +"]# c #443216", +"^# c #423817", +"/# c #1C200E", +"(# c #161605", +"_# c #394328", +":# c #232212", +"<# c #423E17", +"[# c #26370C", +"}# c #30371A", +"|# c #5A6B38", +"1# c #6E7364", +"2# c #5C6251", +"3# c #3D3E2D", +"4# c #4A473C", +"5# c #71664C", +"6# c #775E33", +"7# c #463921", +"8# c #574326", +"9# c #514528", +"0# c #524C2D", +"a# c #606139", +"b# c #4C502A", +"c# c #4B4923", +"d# c #3F351E", +"e# c #4F3F1E", +"f# c #53532B", +"g# c #202A13", +"h# c #2A2C16", +"i# c #2B2813", +"j# c #343525", +"k# c #32311A", +"l# c #3B3B1C", +"m# c #3F3214", +"n# c #3F3B19", +"o# c #333D1B", +"p# c #2B3215", +"q# c #272B27", +"r# c #636E75", +"s# c #766856", +"t# c #6D6B3A", +"u# c #5D6D38", +"v# c #565E35", +"w# c #56663A", +"x# c #504F2F", +"y# c #495331", +"z# c #404528", +"A# c #50542E", +"B# c #607239", +"C# c #4B5D2F", +"D# c #364A22", +"E# c #26301C", +"F# c #171C12", +"G# c #222417", +"H# c #353D24", +"I# c #666C6B", +"J# c #3A3216", +"K# c #4B4B2D", +"L# c #8D9384", +"M# c #8A9379", +"N# c #353C21", +"O# c #363820", +"P# c #3A3728", +"Q# c #434233", +"R# c #515034", +"S# c #4C5734", +"T# c #3C4929", +"U# c #353B26", +"V# c #3B3A24", +"W# c #575032", +"X# c #555C35", +"Y# c #495531", +"Z# c #4F6034", +"`# c #2F391F", +" $ c #2D3A20", +".$ c #353E30", +"+$ c #1C2417", +"@$ c #1D1D14", +"#$ c #2D311D", +"$$ c #333E2F", +"%$ c #585842", +"&$ c #34372B", +"*$ c #889191", +"=$ c #919E91", +"-$ c #4D5C34", +";$ c #565C4A", +">$ c #647171", +",$ c #595F52", +"'$ c #6E6E5F", +")$ c #403E34", +"!$ c #363125", +"~$ c #594C38", +"{$ c #655938", +"]$ c #3E4527", +"^$ c #343825", +"/$ c #404D2B", +"($ c #2D3521", +"_$ c #1A1B12", +":$ c #7D8988", +"<$ c #333936", +"[$ c #2B2D29", +"}$ c #262C1B", +"|$ c #3D3E34", +"1$ c #4F5A4C", +"2$ c #94A0A3", +"3$ c #A3B0B1", +"4$ c #454840", +"5$ c #3F3D2E", +"6$ c #6D6F64", +"7$ c #666150", +"8$ c #554F33", +"9$ c #666242", +"0$ c #5D553C", +"a$ c #4C503C", +"b$ c #545B41", +"c$ c #3B3D32", +"d$ c #8B928E", +"e$ c #25261B", +"f$ c #6D7877", +"g$ c #4F5D63", +"h$ c #343731", +"i$ c #555D58", +"j$ c #566259", +"k$ c #4E4B3E", +"l$ c #3F4232", +"m$ c #616A68", +"n$ c #252620", +"o$ c #383D30", +"p$ c #6A7366", +"q$ c #707569", +"r$ c #4F5443", +"s$ c #485038", +"t$ c #505641", +"u$ c #6F7861", +"v$ c #95A094", +"w$ c #8B958F", +"x$ c #676F67", +"y$ c #7E8582", +"z$ c #474B4A", +"A$ c #414137", +"B$ c #5F6B6A", +"C$ c #52626A", +"D$ c #393E3A", +"E$ c #5A6363", +"F$ c #484C44", +"G$ c #424A3E", +"H$ c #535C53", +"I$ c #474738", +"J$ c #5C5945", +"K$ c #7F8173", +"L$ c #939A95", +"M$ c #929C9C", +"N$ c #9EABAD", +"O$ c #A5B4B7", +"P$ c #89959A", +"Q$ c #3E443E", +"R$ c #3C463B", +"S$ c #485450", +"T$ c #53626A", +"U$ c #5C6B6C", +"V$ c #515F5E", +"W$ c #65767B", +"X$ c #899DA4", +"Y$ c #88999F", +"Z$ c #8E9A9B", +"`$ c #828B88", +" % c #696F66", +".% c #7F8A8A", +"+% c #7E8788", +"@% c #849196", +"#% c #899AA0", +"$% c #646F75", +"%% c #3C413F", +"&% c #252925", +"*% c #647074", +"=% c #36464E", +"-% c #505F67", +";% c #606F76", +">% c #55646E", +",% c #687B85", +"'% c #748890", +")% c #75878D", +"!% c #6F7F82", +"~% c #75858A", +"{% c #798D95", +"]% c #6C7E85", +"^% c #5E6F77", +"/% c #4D5A62", +"(% c #48555E", +"_% c #4B5C65", +":% c #50606A", +"<% c #5B6E7A", +"[% c #5D707B", +"}% c #5D6F79", +"|% c #5C6D77", +"1% c #5D6D78", +"2% c #5B6974", +"3% c #4F5C68", +"4% c #51606C", +"5% c #515E6A", +" ", +" ", +" . ", +" + @ # $ % & * = - ; > , ", +" ' ) ! ~ { ] ^ / ( _ : < [ } | ", +" 1 2 3 4 5 6 7 8 9 0 a b c d e f g ", +" h i j k l m n o p q r s t u v w x y z ", +" A B C D E F o G H I J K L M N O P Q R S ", +" T U V W X Y Z ` ...+.@.#.$.%.&.*.=.-.;.>.,.'. ", +" ).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0. ", +" a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z. ", +" A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z. ", +" e.`. +.+++@+#+$+%+&+*+=+-+;+>+,+'+)+!+~+{+]+^+/+(+_+ ", +" :+<+[+}+|+1+2+3+4+5+6+7+8+9+0+a+b+c+d+e+f+g+h+i+j+k+l+ ", +" m+n+o+p+q+r+s+t+u+v+w+x+y+z+A+B+C+D+E+F+G+H+I+J+K+L+ ", +" M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @.@+@@@#@$@%@&@*@=@-@ ", +" ;@>@,@'@)@!@~@{@]@^@/@(@_@:@<@[@}@|@1@2@3@4@5@6@7@8@9@ ", +" 0@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@ ", +" z@A@B@C@D@E@F@G@H@I@J@K@L@M@N@O@P@Q@R@S@T@U@V@W@X@Y@ ", +" Z@`@ #.#+#@###$#%#&#*#=#-#;#>#,#'#y.)#!#~#{#]#^#/#(# ", +" _#:#<#[#}#|#1#2#3#4#5#6#7#8#9#0#a#b#c#d#e#f#g#h#i#j# ", +" k#l#m#n#o#p#q#r#s#t#u#v#w#x#y#z#A#B#C#D#E#F#G#H#I# ", +" J#K#L#M#N#O#P#Q#R#S#T#U#V#W#X#Y#Z#`# $.$+$@$#$$$ ", +" %$&$*$=$-$;$>$,$'$)$!$~${$]$^$/$($_$:$<$[$}$ ", +" |$1$2$3$4$5$6$7$8$9$0$a$b$c$d$! e$f$g$h$i$ ", +" j$k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$z$A$B$C$ ", +" D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$ ", +" U$V$W$X$Y$Z$`$ %.%+%@%#%$%%%&%*% ", +" =%-%;%>%,%'%)%!%~%{%]%^%/% ", +" (%_%:%<%[%}%|%1%2% ", +" 3%4% 5% ", +" "}; diff --git a/code/ryzom/client/unix/ryzom_128x128.png b/code/ryzom/client/unix/ryzom_128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..3b1146210ff229294f2f2b7cb7837c3a6f99bfeb GIT binary patch literal 37774 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

>adLO7#iU}!nC6#gh{KehJUp)0#Cx{AO7?oEx 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 literal 0 HcmV?d00001 diff --git a/code/ryzom/client/unix/ryzom_22x22.png b/code/ryzom/client/unix/ryzom_22x22.png new file mode 100644 index 0000000000000000000000000000000000000000..4030743e220cfb8940562eb324031e82f1e21d07 GIT binary patch literal 1684 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 Qn*aa+07*qoM6N<$g6;YED*ylh literal 0 HcmV?d00001 diff --git a/code/ryzom/client/unix/ryzom_48x48.png b/code/ryzom/client/unix/ryzom_48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..b0ce667082b5e284b0e50f0e5b76456d6b6eabf4 GIT binary patch literal 6266 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 literal 0 HcmV?d00001 From 84fd111146ab3dca7a847cd549167acc9c6eead9 Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 14 Aug 2011 14:15:20 +0200 Subject: [PATCH 079/215] Fixed: Icons path --- code/ryzom/client/unix/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/ryzom/client/unix/CMakeLists.txt b/code/ryzom/client/unix/CMakeLists.txt index 005c6bdb4..c6b50b37c 100644 --- a/code/ryzom/client/unix/CMakeLists.txt +++ b/code/ryzom/client/unix/CMakeLists.txt @@ -3,9 +3,9 @@ CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/ryzom.desktop.in" "${CMAKE_CURRENT_B INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/ryzom.desktop" DESTINATION share/applications) INSTALL(FILES ryzom.png DESTINATION share/pixmaps) INSTALL(FILES ryzom.xpm DESTINATION share/pixmaps) -INSTALL(FILES res/ryzom16x16.png DESTINATION share/icons/hicolor/16x16/apps RENAME ryzom.png) -INSTALL(FILES res/ryzom22x22.png DESTINATION share/icons/hicolor/22x22/apps RENAME ryzom.png) -INSTALL(FILES res/ryzom24x24.png DESTINATION share/icons/hicolor/24x24/apps RENAME ryzom.png) -INSTALL(FILES res/ryzom32x32.png DESTINATION share/icons/hicolor/32x32/apps RENAME ryzom.png) -INSTALL(FILES res/ryzom48x48.png DESTINATION share/icons/hicolor/48x48/apps RENAME ryzom.png) -INSTALL(FILES res/ryzom128x128.png DESTINATION share/icons/hicolor/128x128/apps RENAME ryzom.png) +INSTALL(FILES ryzom16x16.png DESTINATION share/icons/hicolor/16x16/apps RENAME ryzom.png) +INSTALL(FILES ryzom22x22.png DESTINATION share/icons/hicolor/22x22/apps RENAME ryzom.png) +INSTALL(FILES ryzom24x24.png DESTINATION share/icons/hicolor/24x24/apps RENAME ryzom.png) +INSTALL(FILES ryzom32x32.png DESTINATION share/icons/hicolor/32x32/apps RENAME ryzom.png) +INSTALL(FILES ryzom48x48.png DESTINATION share/icons/hicolor/48x48/apps RENAME ryzom.png) +INSTALL(FILES ryzom128x128.png DESTINATION share/icons/hicolor/128x128/apps RENAME ryzom.png) From 55f26e7f162c91c859e8fd23fd65008f7574ed61 Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 14 Aug 2011 16:05:47 +0200 Subject: [PATCH 080/215] Fixed: Icons name --- code/ryzom/client/unix/CMakeLists.txt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/ryzom/client/unix/CMakeLists.txt b/code/ryzom/client/unix/CMakeLists.txt index c6b50b37c..fef098783 100644 --- a/code/ryzom/client/unix/CMakeLists.txt +++ b/code/ryzom/client/unix/CMakeLists.txt @@ -3,9 +3,9 @@ CONFIGURE_FILE("${CMAKE_CURRENT_SOURCE_DIR}/ryzom.desktop.in" "${CMAKE_CURRENT_B INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/ryzom.desktop" DESTINATION share/applications) INSTALL(FILES ryzom.png DESTINATION share/pixmaps) INSTALL(FILES ryzom.xpm DESTINATION share/pixmaps) -INSTALL(FILES ryzom16x16.png DESTINATION share/icons/hicolor/16x16/apps RENAME ryzom.png) -INSTALL(FILES ryzom22x22.png DESTINATION share/icons/hicolor/22x22/apps RENAME ryzom.png) -INSTALL(FILES ryzom24x24.png DESTINATION share/icons/hicolor/24x24/apps RENAME ryzom.png) -INSTALL(FILES ryzom32x32.png DESTINATION share/icons/hicolor/32x32/apps RENAME ryzom.png) -INSTALL(FILES ryzom48x48.png DESTINATION share/icons/hicolor/48x48/apps RENAME ryzom.png) -INSTALL(FILES ryzom128x128.png DESTINATION share/icons/hicolor/128x128/apps RENAME ryzom.png) +INSTALL(FILES ryzom_16x16.png DESTINATION share/icons/hicolor/16x16/apps RENAME ryzom.png) +INSTALL(FILES ryzom_22x22.png DESTINATION share/icons/hicolor/22x22/apps RENAME ryzom.png) +INSTALL(FILES ryzom_24x24.png DESTINATION share/icons/hicolor/24x24/apps RENAME ryzom.png) +INSTALL(FILES ryzom_32x32.png DESTINATION share/icons/hicolor/32x32/apps RENAME ryzom.png) +INSTALL(FILES ryzom_48x48.png DESTINATION share/icons/hicolor/48x48/apps RENAME ryzom.png) +INSTALL(FILES ryzom_128x128.png DESTINATION share/icons/hicolor/128x128/apps RENAME ryzom.png) From 55e1452fe12e9abde04ed30867459b0e49acccd5 Mon Sep 17 00:00:00 2001 From: Fabien_HENON Date: Sun, 14 Aug 2011 19:39:35 +0200 Subject: [PATCH 081/215] Changed: #1304: Updating world_editor documentation. Adding a primitive for guild missions --HG-- branch : gsoc2011-guildmissions --- .../class_doc/destroy_item.html | 8 + .../class_doc/do_mission.html | 3 +- .../class_doc/recv_fame.html | 7 + .../class_doc/recv_item.html | 9 + .../class_doc/recv_money.html | 8 + .../class_doc/spawn_mission.html | 192 ++++++++ .../newbieland/guild_missions.primitive | 448 ++++++++++++++++++ 7 files changed, 674 insertions(+), 1 deletion(-) create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/spawn_mission.html create mode 100644 code/ryzom/common/data_leveldesign/primitives/newbieland/guild_missions.primitive diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/destroy_item.html b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/destroy_item.html index 366a02977..9eaf62bfd 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/destroy_item.html +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/destroy_item.html @@ -253,6 +253,14 @@ NPC tag name. Used to write in the sys info who destroyed the items

item description. See item_guidelines for the formatting requirements.

+

 

+ +

guild (Boolean): This parameter is only for guild missions. If it is set to true the action is done for the guild (not for the players that completed the mission).

+ +

 

diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/do_mission.html b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/do_mission.html index 0c516a984..4a480e1b8 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/do_mission.html +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/do_mission.html @@ -160,7 +160,8 @@ mso-ansi-language:EN-US'> 

mission_names: Mission names with the specific mission -objective texts.

+objective texts. The name can be followed by a space and a number representing the number of +times this mission needs to be done (useful for guild missions to specify the number of members needed to complete the mission)

 

diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_fame.html b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_fame.html index 391814c2b..1921ec67b 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_fame.html +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_fame.html @@ -166,6 +166,13 @@ reduce fame, use a negative figure.

 

+

guild (Boolean): This parameter is only for guild missions. If it is set to true the action is done for the guild (not for the players that completed the mission).

+ +

 

+

Default variables:

diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_item.html b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_item.html index 96b454cd2..aa4ba81f3 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_item.html +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_item.html @@ -178,11 +178,20 @@ lang=EN-US style='mso-ansi-language:EN-US'>: Array containing given item description. See item_guidelines for the formatting requirements.

+

 

+ +

guild (Boolean): This parameter is only for guild missions. If it is set to true the action is done for the guild (not for the players that completed the mission).

+ +

 

+

Default variables:

diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_money.html b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_money.html index 88030c8d1..7de97c411 100644 --- a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_money.html +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/recv_money.html @@ -169,6 +169,14 @@ lang=EN-US style='mso-ansi-language:EN-US'>amount
: Amount of money to add to the player’s funds. To withdraw money, use a negative figure.

+

 

+ +

guild (Boolean): This parameter is only for guild missions. If it is set to true the action is done for the guild (not for the players that completed the mission).

+ +

 

Default diff --git a/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/spawn_mission.html b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/spawn_mission.html new file mode 100644 index 000000000..53b4f4cc9 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/world_editor_files/class_doc/spawn_mission.html @@ -0,0 +1,192 @@ + + + + + + + + + + + + + + + +
+ +

spawn_mission

+ +

 

+ +

Properties:

+ +

 

+ +

name: Action name, only used by the +mission designer.

+ +

 

+ +

giver_name: The NPC that gives the mission.

+ +

 

+ +

guild (Boolean): This parameter is only for guild missions. .

+ + +

 

+ +

mission_name: The name of the mission to spawn.

+ +

 

+ + +
+ + + + diff --git a/code/ryzom/common/data_leveldesign/primitives/newbieland/guild_missions.primitive b/code/ryzom/common/data_leveldesign/primitives/newbieland/guild_missions.primitive new file mode 100644 index 000000000..568d89398 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/primitives/newbieland/guild_missions.primitive @@ -0,0 +1,448 @@ + + + + + + + class + missions_editor + + + name + guild_missions + + + + audience + guild + + + auto_remove_from_journal + false + + + automatic + false + + + class + mission_tree + + + fail_if_inventory_is_full + false + + + giver_primitive + urban_newbieland.primitive + + + mission_category + Killing + + + mission_description + GUILD_MISSION_DESC + + + mission_giver + $givervar@fullname$ + + + mission_title + GUILD_MISSION_TITLE + + + mono_instance + false + + + name + GUILD_MISSION + + + need_validation + false + + + non_abandonnable + false + + + not_in_journal + false + + + not_proposed + false + + + replayable + true + + + run_only_once + false + + + + class + variables + + + name + variables + + + + class + var_npc + + + npc_function + fct_ranger_leader + + + npc_name + chiang_the_strong + + + var_name + givervar + + + + + + class + pre_requisite + + + name + pre_requisite + + + require_guild_grade + Leader + + + require_guild_membership + true + + + + + + class + alias + + + name + alias + + + + + class + step + + + name + step + + + + class + actions + + + name + pre_actions + + + + class + spawn_mission + + + giver_name + chiang_the_strong + + + guild + true + + + mission_name + SOLO_GUILD_MISSION + + + + + + class + mission_objectives + + + name + objectives + + + + class + do_mission + + + mission_names + SOLO_GUILD_MISSION 2 + + + overload_objective + MIS_DO_MISSION + + + + + class + kill + + + fauna/quantity + chdfa1 2 + + + name + kill chdfa1 2 + + + + + + class + actions + + + name + post_actions + + + + amount + 100 + + + class + recv_money + + + guild + true + + + name + recv_money 100 guild + + + + + amount + 50 + + + class + recv_money + + + + + + + + audience + solo + + + auto_remove_from_journal + false + + + automatic + false + + + class + mission_tree + + + fail_if_inventory_is_full + false + + + giver_primitive + urban_newbieland.primitive + + + mission_category + Killing + + + mission_description + SOLO_GUILD_MISSION_DESC + + + mission_giver + $givervar@fullname$ + + + mission_title + SOLO_GUILD_MISSION_TITLE + + + mono_instance + false + + + name + SOLO_GUILD_MISSION + + + need_validation + false + + + non_abandonnable + false + + + not_in_journal + false + + + not_proposed + true + + + replayable + true + + + run_only_once + false + + + + class + variables + + + name + variables + + + + class + var_npc + + + npc_function + fct_ranger_leader + + + npc_name + chiang_the_strong + + + var_name + givervar + + + + + + class + pre_requisite + + + name + pre_requisite + + + + + + class + alias + + + name + alias + + + + + class + step + + + name + step + + + + class + actions + + + name + pre_actions + + + + + class + mission_objectives + + + name + objectives + + + + class + kill + + + fauna/quantity + chdfa1 1 + + + name + kill chdfa1 1 + + + + + + class + actions + + + name + post_actions + + + + amount + 20 + + + class + recv_money + + + + + + + + From 1e4a4b6724539698b81b43aa6e5209edb2131346 Mon Sep 17 00:00:00 2001 From: aquiles Date: Sun, 14 Aug 2011 20:28:16 +0200 Subject: [PATCH 082/215] Changed: #1306 added basic sheet viewing --- .../plugins/core/general_settings_page.cpp | 2 + .../src/plugins/georges_editor/CMakeLists.txt | 9 +- .../src/plugins/georges_editor/formitem.cpp | 162 +++++ .../src/plugins/georges_editor/formitem.h | 69 ++ .../src/plugins/georges_editor/georges.cpp | 64 ++ .../src/plugins/georges_editor/georges.h | 69 ++ .../georges_editor/georges_dirtree_dialog.cpp | 34 +- .../georges_editor/georges_dirtree_dialog.h | 2 +- .../georges_editor/georges_dirtree_form.ui | 34 +- .../georges_editor/georges_editor_form.cpp | 279 +++++--- .../georges_editor/georges_editor_form.h | 8 + .../georges_editor/georges_editor_form.ui | 4 + .../georges_editor/georges_editor_plugin.cpp | 2 +- .../georges_filesystem_model.cpp | 65 ++ .../georges_editor/georges_filesystem_model.h | 69 +- .../georges_treeview_dialog.cpp | 411 +++++++++++ .../georges_editor/georges_treeview_dialog.h | 89 +++ .../georges_editor/georges_treeview_form.ui | 124 ++++ .../georges_editor/georgesform_model.cpp | 641 ++++++++++++++++++ .../georges_editor/georgesform_model.h | 79 +++ .../georgesform_proxy_model.cpp | 94 +++ .../georges_editor/georgesform_proxy_model.h | 45 ++ 22 files changed, 2218 insertions(+), 137 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formitem.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formitem.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_form.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp index a050f4789..88bd3a298 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp @@ -87,9 +87,11 @@ void GeneralSettingsPage::applyGeneralSettings() settings->beginGroup(Core::Constants::DATA_PATH_SECTION); QString primitivePath = settings->value(Core::Constants::PRIMITIVES_PATH, "l:/primitives").toString(); QString ligoConfigFile = settings->value(Core::Constants::LIGOCONFIG_FILE, "l:/leveldesign/world_editor_files/world_editor_classes.xml").toString(); + QString leveldesignPath = settings->value(Core::Constants::LEVELDESIGN_PATH, "l:/leveldesign").toString(); NLMISC::CPath::addSearchPath(primitivePath.toStdString(), true, false); NLMISC::CPath::display(); NLMISC::CPath::addSearchFile(ligoConfigFile.toStdString()); + NLMISC::CPath::addSearchPath(leveldesignPath.toStdString(), true, false); settings->endGroup(); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/CMakeLists.txt index d1dd346ba..7c668ec8a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/CMakeLists.txt @@ -10,10 +10,13 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin SET(OVQT_PLUG_GEORGES_EDITOR_HDR georges_editor_plugin.h georges_editor_form.h - georges_dirtree_dialog.h) + georges_dirtree_dialog.h + georges_filesystem_model.h + georges_treeview_dialog.h) SET(OVQT_PLUG_GEORGES_EDITOR_UIS georges_editor_form.ui - georges_dirtree_form.ui) + georges_dirtree_form.ui + georges_treeview_form.ui) SET(OVQT_PLUGIN_GEORGES_EDITOR_RCS georges_editor.qrc) @@ -32,7 +35,7 @@ SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) ADD_LIBRARY(ovqt_plugin_georges_editor MODULE ${SRC} ${OVQT_PLUG_GEORGES_EDITOR_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_GEORGES_EDITOR_UI_HDRS} ${OVQT_PLUGIN_GEORGES_EDITOR_RC_SRCS}) -TARGET_LINK_LIBRARIES(ovqt_plugin_georges_editor ovqt_plugin_core nelmisc ${QT_LIBRARIES}) +TARGET_LINK_LIBRARIES(ovqt_plugin_georges_editor ovqt_plugin_core nelmisc nelgeorges ${QT_LIBRARIES}) NL_DEFAULT_PROPS(ovqt_plugin_georges_editor "NeL, Tools, 3D: Object Viewer Qt Plugin: Georges Editor") NL_ADD_RUNTIME_FLAGS(ovqt_plugin_georges_editor) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formitem.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formitem.cpp new file mode 100644 index 000000000..205e18a52 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formitem.cpp @@ -0,0 +1,162 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "formitem.h" + +// Qt includes + +// NeL includes +#include +#include +#include + +namespace Plugin +{ + + CFormItem::CFormItem(NLGEORGES::UFormElm* elm, const QList &data, CFormItem *parent, + NLGEORGES::UFormElm::TWhereIsValue wV, NLGEORGES::UFormElm::TWhereIsNode wN) + { + parentItem = parent; + itemData = data; + formElm = elm; + whereV = wV; + whereN = wN; + } + + CFormItem::~CFormItem() + { + qDeleteAll(childItems); + } + + void CFormItem::appendChild(CFormItem *item) + { + childItems.append(item); + } + + CFormItem *CFormItem::child(int row) + { + return childItems.value(row); + } + + int CFormItem::childCount() const + { + return childItems.count(); + } + + int CFormItem::columnCount() const + { + //nlinfo("columnCount %d",itemData.count()); + return itemData.count(); + } + + QVariant CFormItem::data(int column) const + { + return itemData.value(column); + } + + CFormItem *CFormItem::parent() + { + return parentItem; + } + + int CFormItem::row() const + { + if (parentItem) + return parentItem->childItems.indexOf(const_cast(this)); + + return 0; + } + + bool CFormItem::setData(int column, const QVariant &value) + { + if (column < 0 || column >= itemData.size()) + return false; + + // TODO: default values + if (!formElm) + return false; + + itemData[column] = value; + if (formElm->isAtom()) + { + const NLGEORGES::UType *type = formElm->getType(); + if (type) + { + switch (type->getType()) + { + case NLGEORGES::UType::UnsignedInt: + case NLGEORGES::UType::SignedInt: + case NLGEORGES::UType::Double: + case NLGEORGES::UType::String: + if (parentItem->formElm->isArray()) + { + //((NLGEORGES::CFormElm*)parentItem->formElm);//->arrayInsertNodeByName( + //if(parentItem->formElm->getArrayNode(elmName, num)) + //{ + //} + + bool ok; + // TODO: the node can be renamed from eg "#0" to "foobar" + int arrayIndex = itemData[0].toString().remove("#").toInt(&ok); + if(ok) + { + NLGEORGES::UFormElm *elmt = 0; + if(parentItem->formElm->getArrayNode(&elmt, arrayIndex) && elmt) + { + if (elmt->isAtom()) + { + ((NLGEORGES::CFormElmAtom*)elmt)->setValue(value.toString().toStdString().c_str()); + nldebug(QString("array element string %1 %2") + .arg(itemData[0].toString()).arg(value.toString()) + .toStdString().c_str()); + } + } + } + } + else + { + if(parentItem->formElm->setValueByName( + value.toString().toStdString().c_str(), + itemData[0].toString().toStdString().c_str())) + { + nldebug(QString("string %1 %2") + .arg(itemData[0].toString()).arg(value.toString()) + .toStdString().c_str()); + } + else + { + nldebug(QString("FAILED string %1 %2") + .arg(itemData[0].toString()).arg(value.toString()) + .toStdString().c_str()); + } + } + break; + case NLGEORGES::UType::Color: + nldebug("Color is TODO"); + break; + default: + break; + } + } + } + else + { + nldebug("setting sth other than Atom"); + } + //formElm->setValueByName(); + return true; + } +} diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formitem.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formitem.h new file mode 100644 index 000000000..b85b12275 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formitem.h @@ -0,0 +1,69 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef FORMITEM_H +#define FORMITEM_H + +// NeL includes +#include + +// Qt includes +#include +#include + +namespace Plugin +{ + + class CFormItem + + { + public: + CFormItem(NLGEORGES::UFormElm *elm, const QList &data, + CFormItem *parent = 0, + NLGEORGES::UFormElm::TWhereIsValue = NLGEORGES::UFormElm::ValueForm, + NLGEORGES::UFormElm::TWhereIsNode = NLGEORGES::UFormElm::NodeForm); + ~CFormItem(); + + void appendChild(CFormItem *child); + + CFormItem *child(int row); + int childCount() const; + int columnCount() const; + QVariant data(int column) const; + int row() const; + CFormItem *parent(); + bool setData(int column, const QVariant &value); + NLGEORGES::UFormElm* getFormElm() {return formElm;} + NLGEORGES::UFormElm::TWhereIsValue valueFrom() + { + return whereV; + } + NLGEORGES::UFormElm::TWhereIsNode nodeFrom() + { + return whereN; + } + + private: + QList childItems; + QList itemData; + CFormItem *parentItem; + NLGEORGES::UFormElm* formElm; + NLGEORGES::UFormElm::TWhereIsValue whereV; + NLGEORGES::UFormElm::TWhereIsNode whereN; + }; // CFormItem + +} +#endif // FORMITEM_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges.cpp new file mode 100644 index 000000000..b93f93ec4 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges.cpp @@ -0,0 +1,64 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "georges.h" +#include "nel/misc/o_xml.h" + +// STL includes + +// NeL includes +#include +#include +#include + +// Project includes + +using namespace NLGEORGES; + +namespace Plugin +{ + + CGeorges::CGeorges(): FormLoader(0) + { + FormLoader = UFormLoader::createLoader(); + } + + CGeorges::~CGeorges() + { + } + + UForm *CGeorges::loadForm(std::string formName) + { + UForm *form = FormLoader->loadForm(formName.c_str()); + + return form; + } + + UFormDfn *CGeorges::loadFormDfn(std::string formName) + { + UFormDfn *formdfn = FormLoader->loadFormDfn(formName.c_str()); + + return formdfn; + } + + UType *CGeorges::loadFormType(std::string formName) + { + UType *type = FormLoader->loadFormType(formName.c_str()); + + return type; + } + +} /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges.h new file mode 100644 index 000000000..eb9a6b7da --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges.h @@ -0,0 +1,69 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef GEORGES_H +#define GEORGES_H + +// Misc + +// STL includes +#include + +// NeL includes + +// Qt includes + +// Project includes + +namespace NLGEORGES +{ + class UType; + class UForm; + class UFormDfn; + class UFormLoader; +} + +using namespace NLGEORGES; + +namespace Plugin +{ + + /** + @class CGeorges + A CGeorges class loading and viewing sheets. + */ + class CGeorges + { + public: + /// Default constructor. + CGeorges(); + virtual ~CGeorges(); + + // Load the given form root + UForm* loadForm(std::string formName); + // Load a dfn + UFormDfn* loadFormDfn(std::string formName); + // Load a type + UType *loadFormType (std::string formName); + + // A form loader + UFormLoader *FormLoader; + + };/* class CGeorges */ + +} /* namespace Plugin */ + +#endif // GEORGES_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.cpp index f7e250cce..2a1151164 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.cpp @@ -23,24 +23,35 @@ // NeL includes -//using namespace NLMISC; - namespace Plugin { CGeorgesDirTreeDialog::CGeorgesDirTreeDialog(QString ldPath, QWidget *parent) - :QDockWidget(parent), m_ldPath(ldPath) + :QDockWidget(parent), + m_ldPath(ldPath), + m_proxyModel(0) { m_ui.setupUi(this); + m_ui.filterResetButton->setIcon( + QApplication::style()->standardIcon(QStyle::SP_DialogCancelButton)); + m_dirModel = new CGeorgesFileSystemModel(m_ldPath); - m_ui.dirTree->setModel(m_dirModel); + m_proxyModel = new CGeorgesFileSystemProxyModel(this); + + m_proxyModel->setSourceModel(m_dirModel); + m_ui.dirTree->setModel(m_proxyModel); + + // TODO: filtering in tree model is ... complicated - so hide it for now + m_ui.filterLineEdit->hide(); + m_ui.filterResetButton->hide(); + m_ui.label->hide(); if (m_dirModel->isCorrectLDPath()) { m_dirModel->setRootPath(m_ldPath); - m_ui.dirTree->setRootIndex(m_dirModel->index(m_ldPath)); + m_ui.dirTree->setRootIndex(m_proxyModel->mapFromSource(m_dirModel->index(m_ldPath))); } else { @@ -61,10 +72,9 @@ CGeorgesDirTreeDialog::~CGeorgesDirTreeDialog() void CGeorgesDirTreeDialog::fileSelected(QModelIndex index) { - QString name; - if (index.isValid() && !m_dirModel->isDir(index)) + if (index.isValid() && !m_dirModel->isDir(m_proxyModel->mapToSource(index))) { - Q_EMIT selectedForm(m_dirModel->fileName(index)); + Q_EMIT selectedForm(m_dirModel->fileName(m_proxyModel->mapToSource(index))); } } @@ -81,14 +91,18 @@ void CGeorgesDirTreeDialog::ldPathChanged(QString path) m_ldPath = path; delete m_dirModel; + delete m_proxyModel; m_dirModel = new CGeorgesFileSystemModel(m_ldPath); - m_ui.dirTree->setModel(m_dirModel); + m_proxyModel = new CGeorgesFileSystemProxyModel(this); + + m_proxyModel->setSourceModel(m_dirModel); + m_ui.dirTree->setModel(m_proxyModel); if (m_dirModel->isCorrectLDPath()) { m_dirModel->setRootPath(m_ldPath); - m_ui.dirTree->setRootIndex(m_dirModel->index(m_ldPath)); + m_ui.dirTree->setRootIndex(m_proxyModel->mapFromSource(m_dirModel->index(m_ldPath))); } else { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.h index 3079f76c2..d81897bf3 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.h @@ -45,6 +45,7 @@ private: Ui::CGeorgesDirTreeDialog m_ui; CGeorgesFileSystemModel *m_dirModel; + CGeorgesFileSystemProxyModel *m_proxyModel; QString m_ldPath; Q_SIGNALS: @@ -54,7 +55,6 @@ private Q_SLOTS: void fileSelected(QModelIndex index); void changeFile(QString file); - friend class CMainWindow; }; /* CGEorgesDirTreeDialog */ } /* namespace NLQT */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_form.ui index 8731d1ca8..4a429af1f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_form.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_form.ui @@ -19,7 +19,7 @@ 200 - 111 + 141 @@ -36,7 +36,7 @@ - + @@ -46,9 +46,37 @@ + + + + + + + ... + + + + :/images/ic_nel_georges_editor.png:/images/ic_nel_georges_editor.png + + + true + + + + + + + Filter + + + - + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp index aa614a161..21aa6d8c6 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp @@ -18,6 +18,7 @@ #include "georges_editor_form.h" #include "georges_editor_constants.h" #include "georges_dirtree_dialog.h" +#include "georges_treeview_dialog.h" #include "../core/icore.h" #include "../core/imenu_manager.h" @@ -30,110 +31,190 @@ #include #include #include +#include namespace Plugin { -GeorgesEditorForm::GeorgesEditorForm(QWidget *parent) - : QMainWindow(parent), - m_georgesDirTreeDialog(0) -{ - m_ui.setupUi(this); - - m_undoStack = new QUndoStack(this); - - _openAction = new QAction(tr("&Open..."), this); - _openAction->setIcon(QIcon(Core::Constants::ICON_OPEN)); - _openAction->setShortcut(QKeySequence::Open); - _openAction->setStatusTip(tr("Open an existing file")); - connect(_openAction, SIGNAL(triggered()), this, SLOT(open())); - - _newAction = new QAction(tr("&New..."), this); - _newAction->setIcon(QIcon(Core::Constants::ICON_NEW)); - _newAction->setShortcut(QKeySequence::New); - _newAction->setStatusTip(tr("Create a new file")); - connect(_newAction, SIGNAL(triggered()), this, SLOT(newFile())); - - _saveAction = new QAction(tr("&Save..."), this); - _saveAction->setIcon(QIcon(Core::Constants::ICON_SAVE)); - _saveAction->setShortcut(QKeySequence::Save); - _saveAction->setStatusTip(tr("Save the current file")); - connect(_saveAction, SIGNAL(triggered()), this, SLOT(save())); - - _fileToolBar = addToolBar(tr("&File")); - _fileToolBar->addAction(_openAction); - _fileToolBar->addAction(_newAction); - _fileToolBar->addAction(_saveAction); - - readSettings(); - - // create leveldesign directory tree dockwidget - m_georgesDirTreeDialog = new CGeorgesDirTreeDialog(m_leveldesignPath, this); - addDockWidget(Qt::LeftDockWidgetArea, m_georgesDirTreeDialog); - //m_georgesDirTreeDialog->setVisible(false); - connect(Core::ICore::instance(), SIGNAL(changeSettings()), - this, SLOT(settingsChanged())); -} - -GeorgesEditorForm::~GeorgesEditorForm() -{ - writeSettings(); -} - -QUndoStack *GeorgesEditorForm::undoStack() const -{ - return m_undoStack; -} - -void GeorgesEditorForm::open() -{ - // TODO: FileDialog & loadFile(); - //QString fileName = QFileDialog::getOpenFileName(); - //loadFile(fileName); -} - -void GeorgesEditorForm::newFile() -{ - -} - -void GeorgesEditorForm::save() -{ - -} - -void GeorgesEditorForm::readSettings() -{ - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup(Constants::GEORGES_EDITOR_SECTION); - settings->endGroup(); - - settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - m_leveldesignPath = settings->value(Core::Constants::LEVELDESIGN_PATH, "l:/leveldesign").toString(); - settings->endGroup(); -} - -void GeorgesEditorForm::writeSettings() -{ - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup(Constants::GEORGES_EDITOR_SECTION); - settings->endGroup(); - settings->sync(); -} - -void GeorgesEditorForm::settingsChanged() -{ - QSettings *settings = Core::ICore::instance()->settings(); - - settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - QString oldLDPath = m_leveldesignPath; - m_leveldesignPath = settings->value(Core::Constants::LEVELDESIGN_PATH, "l:/leveldesign").toString(); - settings->endGroup(); - - if (oldLDPath != m_leveldesignPath) + GeorgesEditorForm::GeorgesEditorForm(QWidget *parent) + : QMainWindow(parent), + m_georgesDirTreeDialog(0), + m_emptyDock(0), + m_mainDock(0) { - m_georgesDirTreeDialog->ldPathChanged(m_leveldesignPath); + m_ui.setupUi(this); + + // background for the mainwindow + QString css = "QWidget#centralwidget {"; + css += "image: url(:/images/ic_nel_georges_editor.png);"; + css += "}"; + + // add new mainwindow for sheet dockwidgets + QWidget *widget = new QWidget(this); + widget->setObjectName("centralwidget"); + widget->setStyleSheet(css); + setCentralWidget(widget); + QGridLayout *layout = new QGridLayout(widget); + layout->setContentsMargins(0,0,0,0); + widget->setLayout(layout); + m_mainDock = new QMainWindow(this); + m_mainDock->setDockNestingEnabled(true); + layout->addWidget(m_mainDock); + + m_undoStack = new QUndoStack(this); + + Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); + _openAction = menuManager->action(Core::Constants::OPEN); + + /*_openAction = new QAction(tr("&Open..."), this); + _openAction->setIcon(QIcon(Core::Constants::ICON_OPEN)); + _openAction->setShortcut(QKeySequence::Open); + _openAction->setStatusTip(tr("Open an existing file")); + connect(_openAction, SIGNAL(triggered()), this, SLOT(open()));*/ + + _newAction = new QAction(tr("&New..."), this); + _newAction->setIcon(QIcon(Core::Constants::ICON_NEW)); + _newAction->setShortcut(QKeySequence::New); + _newAction->setStatusTip(tr("Create a new file")); + connect(_newAction, SIGNAL(triggered()), this, SLOT(newFile())); + + _saveAction = new QAction(tr("&Save..."), this); + _saveAction->setIcon(QIcon(Core::Constants::ICON_SAVE)); + _saveAction->setShortcut(QKeySequence::Save); + _saveAction->setStatusTip(tr("Save the current file")); + connect(_saveAction, SIGNAL(triggered()), this, SLOT(save())); + + _fileToolBar = addToolBar(tr("&File")); + _fileToolBar->addAction(_openAction); + _fileToolBar->addAction(_newAction); + _fileToolBar->addAction(_saveAction); + + //_openAction->setEnabled(false); + //_newAction->setEnabled(false); + //_saveAction->setEnabled(false); + + readSettings(); + + // create leveldesign directory tree dockwidget + m_georgesDirTreeDialog = new CGeorgesDirTreeDialog(m_leveldesignPath, this); + addDockWidget(Qt::LeftDockWidgetArea, m_georgesDirTreeDialog); + restoreDockWidget(m_georgesDirTreeDialog); + + connect(Core::ICore::instance(), SIGNAL(changeSettings()), + this, SLOT(settingsChanged())); + connect(m_georgesDirTreeDialog, SIGNAL(selectedForm(const QString)), + this, SLOT(loadFile(const QString))); + } + + GeorgesEditorForm::~GeorgesEditorForm() + { + writeSettings(); + } + + QUndoStack *GeorgesEditorForm::undoStack() const + { + return m_undoStack; + } + + void GeorgesEditorForm::open() + { + /*qDebug() << "GeorgesEditorForm::open()"; + if (!m_dockedWidgets.size()) + { + m_dockedWidgets.append(new CGeorgesTreeViewDialog(m_mainDock)); + m_mainDock->addDockWidget(Qt::RightDockWidgetArea, m_dockedWidgets.last()); + } + else + { + m_dockedWidgets.append(new CGeorgesTreeViewDialog(m_mainDock)); + Q_ASSERT(m_dockedWidgets.size() > 1); + m_mainDock->tabifyDockWidget(m_dockedWidgets.at(m_dockedWidgets.size() - 2), m_dockedWidgets.last()); + }*/ + + // TODO: FileDialog & loadFile(); + //m_mainDock->addDockWidget(Qt::TopDockWidgetArea, new CGeorgesTreeViewDialog(m_mainDock, true)); + //m_mainDock->addDockWidget(Qt::LeftDockWidgetArea, new CGeorgesTreeViewDialog(m_mainDock, true)); + //QString fileName = QFileDialog::getOpenFileName(); + //loadFile(fileName); + } + + void GeorgesEditorForm::newFile() + { + + } + + void GeorgesEditorForm::save() + { + + } + + void GeorgesEditorForm::readSettings() + { + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(Constants::GEORGES_EDITOR_SECTION); + + restoreGeometry(settings->value("geometry").toByteArray()); + restoreState(settings->value("windowState").toByteArray()); + + settings->endGroup(); + + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + m_leveldesignPath = settings->value(Core::Constants::LEVELDESIGN_PATH, "l:/leveldesign").toString(); + settings->endGroup(); + } + + void GeorgesEditorForm::writeSettings() + { + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup(Constants::GEORGES_EDITOR_SECTION); + + settings->setValue("geometry", saveGeometry()); + settings->setValue("windowState", saveState()); + + settings->endGroup(); + settings->sync(); + } + + void GeorgesEditorForm::settingsChanged() + { + QSettings *settings = Core::ICore::instance()->settings(); + + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + QString oldLDPath = m_leveldesignPath; + m_leveldesignPath = settings->value(Core::Constants::LEVELDESIGN_PATH, "l:/leveldesign").toString(); + settings->endGroup(); + + if (oldLDPath != m_leveldesignPath) + { + m_georgesDirTreeDialog->ldPathChanged(m_leveldesignPath); + } + } + + void GeorgesEditorForm::loadFile(const QString fileName) + { + QFileInfo info(fileName); + + if (!m_dockedWidgets.size()) + { + m_dockedWidgets.append(new CGeorgesTreeViewDialog(m_mainDock)); + m_mainDock->addDockWidget(Qt::RightDockWidgetArea, m_dockedWidgets.last()); + } + else + { + m_dockedWidgets.append(new CGeorgesTreeViewDialog(m_mainDock)); + Q_ASSERT(m_dockedWidgets.size() > 1); + m_mainDock->tabifyDockWidget(m_dockedWidgets.at(m_dockedWidgets.size() - 2), m_dockedWidgets.last()); + } + CForm *form = m_dockedWidgets.last()->getFormByName(info.fileName()); + if (form) + { + // delete newView; + // newView = createTreeView(fileName); + m_dockedWidgets.last()->setForm(form); + m_dockedWidgets.last()->loadFormIntoDialog(form); + //setCurrentFile(info.fileName()); + // return; + } } -} } /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h index 96fceb4ed..9e938f62c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h @@ -27,6 +27,7 @@ namespace Plugin { class CGeorgesDirTreeDialog; +class CGeorgesTreeViewDialog; class GeorgesEditorForm: public QMainWindow { Q_OBJECT @@ -42,6 +43,7 @@ public Q_SLOTS: void newFile(); void save(); void settingsChanged(); + void loadFile(const QString fileName); private: void readSettings(); @@ -57,6 +59,12 @@ private: QAction *_saveAction; QString m_leveldesignPath; + + QDockWidget *m_emptyDock; + QMainWindow *m_mainDock; + + QList m_dockedWidgets; + QList m_tabBars; }; /* class GeorgesEditorForm */ } /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.ui index 905814cc7..3f8cde0f7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.ui @@ -13,12 +13,16 @@ Georges Editor + + + QWidget#centralwidget { image: url(:/images/ic_nel_georges_editor.png); } + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp index a97188751..e6cad341d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp @@ -74,7 +74,7 @@ QString GeorgesEditorPlugin::name() const QString GeorgesEditorPlugin::version() const { - return "0.2"; + return "0.3"; } QString GeorgesEditorPlugin::vendor() const diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.cpp index 20cb30b33..796b3c457 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.cpp @@ -28,6 +28,8 @@ CGeorgesFileSystemModel::CGeorgesFileSystemModel(QString ldPath, QObject *parent m_correct(false) { checkLDPath(); + connect(this, SIGNAL(directoryLoaded(QString)), + this, SLOT(dir(const QString))); } CGeorgesFileSystemModel::~CGeorgesFileSystemModel() @@ -35,6 +37,21 @@ CGeorgesFileSystemModel::~CGeorgesFileSystemModel() } +void CGeorgesFileSystemModel::dir(const QString &dir) +{ + QModelIndex i = index(dir); + + if (hasChildren(i)) { + int childCount = rowCount(i); + for (int c=0; cnode(parent); +// return (!indexNode->populatedChildren);*/ +//} + +CGeorgesFileSystemProxyModel::CGeorgesFileSystemProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +{ + setFilterCaseSensitivity(Qt::CaseInsensitive); +} + +bool CGeorgesFileSystemProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +{ + // TODO this is not perfect as it could be + // eg it should filter all dirs which have no entry + QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); + if (sourceModel()->hasChildren(idx)) + { + // QString d = sourceModel()->data(idx).toString(); + // //QModelIndex i = mapFromSource(source_parent); + // //if (hasChildren(i)) { + // int childCount = sourceModel()->rowCount(idx); + // for (int c=0; cindex(c, 0, idx); + // if (child.isValid()) { + // bool test = filterAcceptsRow(c, child); + // }*/ + // } + return true; + } + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +} + +//QVariant CGeorgesFileSystemProxyModel::data ( const QModelIndex & index, int role ) const +//{ +// if (role == Qt::DisplayRole) +// { +// QString test = QSortFilterProxyModel::data(index, role).toString(); +// return test.append(QString(" (%1/%2)")). +// arg(rowCount(index)). +// arg(sourceModel()->rowCount(mapToSource(index))); +// } +// return QSortFilterProxyModel::data(index, role); +//} } /* namespace NLQT */ /* end of file */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.h index 775dcb18d..8ee06b3ef 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.h @@ -18,32 +18,61 @@ #define GEORGES_FILESYSTEM_MODEL_H #include +#include namespace Plugin { -class CGeorgesFileSystemModel : public QFileSystemModel -{ - QString m_ldPath; - -public: - CGeorgesFileSystemModel(QString ldPath, QObject *parent = 0); - ~CGeorgesFileSystemModel(); - - int columnCount(const QModelIndex &/*parent*/) const; - int rowCount(const QModelIndex &/*parent*/) const; - - QVariant data(const QModelIndex& index, int role) const ; - - bool isCorrectLDPath() + class CGeorgesFileSystemModel : public QFileSystemModel { - return m_correct; - } - void checkLDPath(); + Q_OBJECT -private: - bool m_correct; -};/* class CGeorgesFileSystemModel */ + public: + CGeorgesFileSystemModel(QString ldPath, QObject *parent = 0); + ~CGeorgesFileSystemModel(); + + int columnCount(const QModelIndex &/*parent*/) const; + int rowCount(const QModelIndex &/*parent*/) const; + + QVariant data(const QModelIndex& index, int role) const ; + + bool isCorrectLDPath() + { + return m_correct; + } + bool isInitialized() + { + return m_initialized; + } + void setInitialized( bool init) + { + m_initialized = init; + } + void checkLDPath(); + + private: + bool m_correct; + bool m_initialized; + QString m_ldPath; + +private Q_SLOTS: + void dir(const QString&); + };/* class CGeorgesFileSystemModel */ + + // A modified QSortFilterProxyModel that always accepts the root nodes in the tree + // so filtering is only done on the children. + class CGeorgesFileSystemProxyModel : public QSortFilterProxyModel + { + Q_OBJECT + + public: + CGeorgesFileSystemProxyModel(QObject *parent = 0); + + //QVariant data(const QModelIndex& index, int role) const ; + + protected: + bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + }; } /* namespace NLQT */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp new file mode 100644 index 000000000..2bcef52cb --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp @@ -0,0 +1,411 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "georges_treeview_dialog.h" + +// Qt includes +#include +#include +#include + +// NeL includes +#include +#include +#include +#include + +// Project includes +#include "georges.h" +#include "georgesform_model.h" +#include "georgesform_proxy_model.h" +//#include "formitem.h" +//#include "formdelegate.h" + +using namespace NLMISC; +using namespace NLGEORGES; + +namespace Plugin +{ + + CGeorgesTreeViewDialog::CGeorgesTreeViewDialog(QWidget *parent /*= 0*/) + : QDockWidget(parent) + { + m_georges = new CGeorges; + + loadedForm = ""; + m_modified = false; + + m_ui.setupUi(this); + m_ui.treeView->header()->setResizeMode(QHeaderView::ResizeToContents); + m_ui.treeViewTabWidget->setTabEnabled (2,false); + + m_ui.checkBoxParent->setStyleSheet("background-color: rgba(0,255,0,30)"); + m_ui.checkBoxDefaults->setStyleSheet("background-color: rgba(255,0,0,30)"); + m_form = 0; + + //FormDelegate *formdelegate = new FormDelegate(this); + //_ui.treeView->setItemDelegateForColumn(1, formdelegate); + + + connect(m_ui.treeView, SIGNAL(doubleClicked (QModelIndex)), + this, SLOT(doubleClicked (QModelIndex))); + connect(m_ui.checkBoxParent, SIGNAL(toggled(bool)), + this, SLOT(filterRows())); + connect(m_ui.checkBoxDefaults, SIGNAL(toggled(bool)), + this, SLOT(filterRows())); + } + + CGeorgesTreeViewDialog::~CGeorgesTreeViewDialog() + { + //delete _ui.treeView->itemDelegateForColumn(1); + delete m_form; + deleteLater(); + //QSettings settings("RyzomCore", "GeorgesQt"); + //settings.setValue("dirViewGeometry", saveGeometry()); + } + + void CGeorgesTreeViewDialog::setForm(const CForm *form) + { + m_form = (UForm*)form; + } + + CForm* CGeorgesTreeViewDialog::getFormByName(const QString formName) + { + if(NLMISC::CPath::exists(formName.toStdString())) + { + return (CForm*)m_georges->loadForm(formName.toStdString()); + } + //else + //{ + // CForm *form = 0; + // // Load the DFN + // std::string extStr = NLMISC::CFile::getExtension( formName.toStdString() ); + // QString dfnName = QString("%1.dfn").arg(extStr.c_str()); + // UFormDfn *formdfn; + // if (NLMISC::CPath::exists(dfnName.toStdString())) + // { + // formdfn = _georges->loadFormDfn (dfnName.toStdString()); + // if (!formdfn) + // { + // nlwarning("Failed to load dfn: %s", dfnName.toStdString().c_str()); + // return 0; + // } + // } + // else + // { + // nlwarning("Cannot find dfn: %s", dfnName.toStdString().c_str()); + // return 0; + // } + + // form = new CForm; + + // // Build the root element + // ((CFormElmStruct*)&form->getRootNode())->build((CFormDfn*)formdfn); + + // uint i; + // for (i=0; iHeldElements[i]))->build ((CFormDfn*)formdfn); + // } + // return form; + //} + return 0; + } + + void CGeorgesTreeViewDialog::loadFormIntoDialog(CForm *form) + { + + if(form) + m_form = form; + else + return; + + UFormElm *root = 0; + root = &m_form->getRootNode(); + + QStringList parents; + for (uint i = 0; i < m_form->getNumParent(); i++) + { + UForm *u = m_form->getParentForm(i); + parents << u->getFilename().c_str(); + } + + QString comments; + comments = m_form->getComment().c_str(); + + if (!comments.isEmpty()) + { + m_ui.treeViewTabWidget->setTabEnabled (1,true); + m_ui.commentEdit->setPlainText(comments); + } + + QStringList strList; + std::set dependencies; + m_form->getDependencies(dependencies); + + QMap< QString, QStringList> deps; + Q_FOREACH(std::string str, dependencies) + { + QString file = str.c_str(); + if (str == m_form->getFilename()) continue; + deps[file.remove(0,file.indexOf(".")+1)] << str.c_str(); + } + nlinfo("typ's %d",deps["typ"].count()); + nlinfo("dfn's %d",deps["dfn"].count()); + + //nlwarning(strList.join(";").toStdString().c_str()); + if (root) + { + loadedForm = m_form->getFilename().c_str(); + + CGeorgesFormModel *model = new CGeorgesFormModel(root,deps,comments,parents); + CGeorgesFormProxyModel *proxyModel = new CGeorgesFormProxyModel(); + proxyModel->setSourceModel(model); + m_ui.treeView->setModel(proxyModel); + m_ui.treeView->expandAll(); + // this is a debug output row + m_ui.treeView->hideColumn(3); + + filterRows(); + + // //_ui.treeView->setRowHidden(0,QModelIndex(),true); + // connect(model, SIGNAL(dataChanged(const QModelIndex, const QModelIndex)), + // this, SLOT(modifiedFile())); + + setWindowTitle(loadedForm); + // //Modules::mainWin().getTabBar(); + } + } + + void CGeorgesTreeViewDialog::addParentForm(CForm *form) + { + //((CForm*)_form)->insertParent(((CForm*)_form)->getParentCount(), form->getFilename().c_str(), form); + } + + void CGeorgesTreeViewDialog::modifiedFile( ) + { + /*if (!_modified) + { + _modified = true; + setWindowTitle(windowTitle()+"*"); + Modules::mainWin().setWindowTitle(Modules::mainWin().windowTitle()+"*"); + Q_EMIT modified(_modified); + }*/ + } + + void CGeorgesTreeViewDialog::write( ) + { + + //COFile file; + //std::string s = CPath::lookup(loadedForm.toStdString(), false); + //if (file.open (s)) + //{ + // try + // { + // if (loadedForm.contains(".typ")) + // { + // //nlassert (Type != NULL); + + // //// Write the file + // //// Modified ? + // //if (IsModified ()) + // //{ + // // Type->Header.MinorVersion++; + // // flushValueChange (); + // //} + // //Type->write (xmlStream.getDocument (), theApp.Georges4CVS); + // //modify (NULL, NULL, false); + // //flushValueChange (); + // //UpdateAllViews (NULL); + // //return TRUE; + // } + // else if (loadedForm.contains(".dfn")) + // { + // //nlassert (Dfn != NULL); + + // //// Write the file + // //if (IsModified ()) + // //{ + // // Dfn->Header.MinorVersion++; + // // flushValueChange (); + // //} + // //Dfn->write (xmlStream.getDocument (), lpszPathName, theApp.Georges4CVS); + // //modify (NULL, NULL, false); + // //UpdateAllViews (NULL); + // //return TRUE; + // } + // else + // { + // nlassert (_form != NULL); + + // // Write the file + // /*if (IsModified ()) + // { + // ((CForm*)(UForm*)Form)->Header.MinorVersion++; + // }*/ + // //((CForm*)(UForm*)Form)->write (xmlStream.getDocument (), lpszPathName, theApp.Georges4CVS); + // _form->write(file, false); + // setWindowTitle(windowTitle().remove("*")); + // _modified = false; + // //if (strcmp (xmlStream.getErrorString (), "") != 0) + // //{ + // // char message[512]; + // // smprintf (message, 512, "Error while saving file: %s", xmlStream.getErrorString ()); + // //theApp.outputError (message); + // //} + // //modify (NULL, NULL, false); + // //flushValueChange (); + // //UpdateAllViews (NULL); + + // // Get the left view + // //CView* pView = getLeftView (); + // } + // } + // catch (Exception &e) + // { + // nlerror("Error while loading file: %s", e.what()); + // } + //} + //else + //{ //if (!file.open()) + // nlerror("Can't open the file %s for writing.", s.c_str()); + //} + } + + void CGeorgesTreeViewDialog::doubleClicked ( const QModelIndex & index ) + { + // TODO: this is messy :( perhaps this can be done better + //CGeorgesFormProxyModel * mp = + // dynamic_cast(_ui.treeView->model()); + //CGeorgesFormModel *m = + // dynamic_cast(mp->sourceModel()); + //QModelIndex in = mp->mapToSource(index); + + //// col containing additional stuff like icons + //if (index.column() == 2) + //{ + // QModelIndex in2 = m->index(in.row(),in.column()-1,in.parent()); + // CFormItem *item = m->getItem(in2); + // QString value = item->data(1).toString(); + + // QString path = CPath::lookup(value.toStdString(),false).c_str(); + + // if(value.contains(".tga") || value.contains(".png")) + // { + // QString file = QFileDialog::getOpenFileName( + // this, + // "Select a new image", + // path, + // "Images (*.png *.tga)" + // ); + // if (file.isNull()) + // return; + // QFileInfo info = QFileInfo(file); + + // // TODO? + // // right way would be another delegate but im too lazy :) + // // so for now i just call it directly + // m->setData(in2, info.fileName()); + // return; + // } + // else + // { + // if (path.contains(".shape") || path.contains(".ps")) + // { + // if (Modules::objViewInt()) + // { + // Modules::objViewInt()->resetScene(); + // //Modules::config().configRemapExtensions(); + // Modules::objViewInt()->loadMesh(path.toStdString(),""); + // } + // return; + // } + // } + + // // open eg parent files + // if (!path.isEmpty()) + // Q_EMIT changeFile(path); + + //} + } + + void CGeorgesTreeViewDialog::closeEvent(QCloseEvent *event) + { + /*if (Modules::mainWin().getEmptyView() == this) + { + event->ignore(); + } + else + { + if(Modules::mainWin().getTreeViewList().size() == 1) + { + Modules::mainWin().createEmptyView( + Modules::mainWin().getTreeViewList().takeFirst()); + } + Modules::mainWin().getTreeViewList().removeOne(this); + deleteLater(); + }*/ + } + + void CGeorgesTreeViewDialog::filterRows() + { + nlinfo("CGeorgesTreeViewDialog::filterRows"); + CGeorgesFormProxyModel * mp = dynamic_cast(m_ui.treeView->model()); + CGeorgesFormModel *m = dynamic_cast(mp->sourceModel()); + if (m) { + m->setShowParents(m_ui.checkBoxParent->isChecked()); + m->setShowDefaults(m_ui.checkBoxDefaults->isChecked()); + } + + //CGeorgesFormProxyModel * mp = dynamic_cast(_ui.treeView->model()); + //CGeorgesFormModel *m = dynamic_cast(mp->sourceModel()); + + //for (int i = 0; i < m->rowCount(); i++) + //{ + // const QModelIndex in = m->index(i,0); + // if (m->getItem(in)->nodeFrom() == UFormElm::NodeParentForm) + // { + // if (newState == Qt::Checked) + // { + // _ui.treeView->setRowHidden(in.row(),in.parent(),false); + // } + // else + // { + // _ui.treeView->setRowHidden(in.row(),in.parent(),true); + // } + // } + // else + // { // search childs // recursive? + // for (int j = 0; j < m->rowCount(in); j++) + // { + // const QModelIndex in2 = m->index(j,0,in); + // if (m->getItem(in2)->nodeFrom() == UFormElm::NodeParentForm) + // { + // if (newState == Qt::Checked) + // { + // _ui.treeView->setRowHidden(in2.row(),in,false); + // } + // else + // { + // _ui.treeView->setRowHidden(in2.row(),in,true); + // } + // } + // } + // } // end of search childs + //} + } + +} /* namespace NLQT */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h new file mode 100644 index 000000000..dee1216ca --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h @@ -0,0 +1,89 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef GEORGES_TREEVIEWER_DIALOG_H +#define GEORGES_TREEVIEWER_DIALOG_H + +#include "ui_georges_treeview_form.h" + +// Qt includes +#include + +// STL includes + +// NeL includes + +// Project includes + +namespace NLGEORGES +{ + class UForm; + class CForm; +} + +using namespace NLGEORGES; + +namespace Plugin +{ + + class CGeorges; + + class CGeorgesTreeViewDialog: public QDockWidget + { + Q_OBJECT + + public: + CGeorgesTreeViewDialog(QWidget *parent = 0); + ~CGeorgesTreeViewDialog(); + + bool modified() {return m_modified;} + void setModified(bool m) {m_modified = m;} + + CForm* getFormByName(const QString); + void addParentForm(CForm *form); + + void write ( ); + + QTabWidget* tabWidget() { return m_ui.treeViewTabWidget; } + + QString loadedForm; + + protected: + void closeEvent(QCloseEvent *event); + + Q_SIGNALS: + void changeFile(QString); + void modified(bool); + public Q_SLOTS: + void setForm(const CForm*); + void loadFormIntoDialog(CForm *form = 0); + void modifiedFile( ); + private Q_SLOTS: + void doubleClicked ( const QModelIndex & index ); + void filterRows(); + + private: + Ui::CGeorgesTreeViewDialog m_ui; + UForm *m_form; + CGeorges *m_georges; + + bool m_modified; + + }; /* CGeorgesTreeViewDialog */ + +} /* namespace NLQT */ + +#endif // GEORGES_TREEVIEWER_DIALOG_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_form.ui new file mode 100644 index 000000000..71db74326 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_form.ui @@ -0,0 +1,124 @@ + + + CGeorgesTreeViewDialog + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + + 199 + 165 + + + + + + + + + + + QTabWidget::West + + + 0 + + + + Form + + + + 0 + + + + + + 0 + 0 + + + + + 0 + 0 + + + + + + + + Parent + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Defaults + + + + + + + + Comment + + + + + + false + + + + + + + + Log + + + + + + + + + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp new file mode 100644 index 000000000..234c2d169 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp @@ -0,0 +1,641 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "georgesform_model.h" + +// NeL includes +#include +#include +#include +#include +#include +#include + +// Qt includes +#include +#include +#include +#include +#include + +// project includes +#include "formitem.h" +//#include "modules.h" + +using namespace NLGEORGES; + +namespace Plugin +{ + + CGeorgesFormModel::CGeorgesFormModel(UFormElm *rootElm, QMap< QString, QStringList> deps, + QString comment, QStringList parents, QObject *parent) : QAbstractItemModel(parent) + { + QList rootData; + rootData << "Value" << "Data" << "Extra";// << "Type"; + m_rootElm = rootElm; + m_rootItem = new CFormItem(m_rootElm, rootData); + m_dependencies = deps; + m_comments = comment; + m_parents = parents; + m_parentRows = new QList; + + setupModelData(); + } + + CGeorgesFormModel::~CGeorgesFormModel() + { + delete m_rootItem; + } + + /******************************************************************************/ + + QVariant CGeorgesFormModel::data(const QModelIndex &p_index, int p_role) const + { + if (!p_index.isValid()) + return QVariant(); + + switch (p_role) + { + case Qt::DisplayRole: + { + return getItem(p_index)->data(p_index.column()); + } + case Qt::BackgroundRole: + { + QBrush defaultBrush = QBrush(QColor(255,0,0,30)); + QBrush parentBrush = QBrush(QColor(0,255,0,30)); + + // if elm not existing it must be some kind of default or type value + if(!getItem(p_index)->getFormElm()) + { + return defaultBrush; + } + + // else it might be some parent elm + switch (getItem(p_index)->nodeFrom()) + { + case NLGEORGES::UFormElm::NodeParentForm: + { + return parentBrush; + } + case NLGEORGES::UFormElm::NodeForm: + { + switch (getItem(p_index)->valueFrom()) + { + case NLGEORGES::UFormElm::ValueParentForm: + { + return parentBrush; + } + default: + { + // parent status test kindof ugly, testing only 2 steps deep + // only needed for colorization as treeview default hides childs + // when parent is hidden + CFormItem *parent = getItem(p_index)->parent(); + if (parent) + { + if (parent->nodeFrom() == NLGEORGES::UFormElm::NodeParentForm) + { + return parentBrush; + } + + CFormItem *parentParent = parent->parent(); + if (parentParent) + { + if (parentParent->nodeFrom() == NLGEORGES::UFormElm::NodeParentForm) + { + return parentBrush; + } + } // endif parentParent + } // endif parent + } // end default + } // end switch valueFrom + } // end case nodeForm + } // end switch nodeFrom + return QVariant(); + } + case Qt::DecorationRole: + { + if (p_index.column() == 2) + { + //p_index. + QModelIndex in = index(p_index.row(),p_index.column()-1,p_index.parent()); + CFormItem *item = getItem(in); + + QString value = item->data(1).toString(); + //QString path = NLMISC::CPath::lookup(value.toStdString(),false).c_str(); + + /*if (value.contains(".shape")) + { + if (Modules::objViewInt()) + { + QIcon *icon = Modules::objViewInt()->saveOneImage(value.toStdString()); + if (icon) + { + if(icon->isNull()) + return QIcon(":/images/pqrticles.png"); + else + return QIcon(*icon); + } + else + { + return QIcon(); + } + } + }*/ + if(value.contains(".tga") || value.contains(".png")) + { + QString path = NLMISC::CPath::lookup(value.toStdString(),false).c_str(); + if(path.isEmpty()) + { + path = ":/images/pqrticles.png"; + } + return QIcon(path); + } + } + return QVariant(); + break; + } + case Qt::ToolTipRole: + { + if (p_index.column() == 2) + { + QModelIndex in = index(p_index.row(),p_index.column()-1,p_index.parent()); + CFormItem *item = getItem(in); + QString value = item->data(1).toString(); + + /*if (value.contains(".shape")) + { + if (Modules::objViewInt()) + { + QIcon *icon = Modules::objViewInt()->saveOneImage(value.toStdString()); + if (icon) + { + if(icon->isNull()) + return QIcon(":/images/pqrticles.png"); + else + return QIcon(*icon); + } + else + { + return QIcon(); + } + } + }*/ + if(value.contains(".tga") || value.contains(".png")) + { + QString path = NLMISC::CPath::lookup(value.toStdString(),false).c_str(); + if(path.isEmpty()) + { + path = ":/images/pqrticles.png"; + } + + QString imageTooltip = QString("").arg(path); + + return imageTooltip; + } + } + return QVariant(); + break; + } + default: + return QVariant(); + } + } + + /******************************************************************************/ + + CFormItem *CGeorgesFormModel::getItem(const QModelIndex &index) const + { + if (index.isValid()) + { + CFormItem *item = static_cast(index.internalPointer()); + if (item) + return item; + } + return m_rootItem; + } + + /******************************************************************************/ + + //bool CGeorgesFormModel::setData(const QModelIndex &index, const QVariant &value, + // int role) + //{ + + // if (role != Qt::EditRole) + // return false; + + // CFormItem *item = getItem(index); + // bool result = item->setData(index.column(), value); + + // // TODO: ugly hack for updating icon too + // if (result) + // Q_EMIT dataChanged(index, this->index(index.row(),index.column()+1,index.parent())); + + // setupModelData(); + // return result; + //} + + /******************************************************************************/ + + Qt::ItemFlags CGeorgesFormModel::flags(const QModelIndex& index) const { + + if (!index.isValid()) + return 0; + + Qt::ItemFlags returnValue = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + + //if(index.column() == 1) + // returnValue |= Qt::ItemIsEditable; + // TODO? + // col 2 should go here too but i dont want to do another delegate + // so for now i just connected the dblClick in the dialog + + return returnValue; + + } + + /******************************************************************************/ + + QVariant CGeorgesFormModel::headerData(int section, + Qt::Orientation orientation, int role) const + { + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + return m_rootItem->data(section); + + return QVariant(); + + } + + /******************************************************************************/ + + QModelIndex CGeorgesFormModel::index(int row, int column, const QModelIndex &parent) + const + { + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + CFormItem *parentItem; + + if (!parent.isValid()) + parentItem = m_rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + CFormItem *childItem = parentItem->child(row); + if (childItem) + return createIndex(row, column, childItem); + else + return QModelIndex(); + } + + /******************************************************************************/ + + QModelIndex CGeorgesFormModel::parent(const QModelIndex &index) const + { + if (!index.isValid()) + return QModelIndex(); + + CFormItem *childItem = static_cast(index.internalPointer()); + CFormItem *parentItem = childItem->parent(); + + if (parentItem == m_rootItem) + return QModelIndex(); + + return createIndex(parentItem->row(), 0, parentItem); + } + + /******************************************************************************/ + + int CGeorgesFormModel::rowCount(const QModelIndex &parent) const { + + CFormItem *parentItem; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parentItem = m_rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + return parentItem->childCount(); + + } + + /******************************************************************************/ + + int CGeorgesFormModel::columnCount(const QModelIndex &parent) const { + + if (parent.isValid()) + return static_cast(parent.internalPointer())->columnCount(); + else + return m_rootItem->columnCount(); + + } + + /******************************************************************************/ + + void CGeorgesFormModel::loadFormData(UFormElm *root, CFormItem *parent) { + + if (!root) + return; + + uint num = 0; + + + if (root->isStruct()) + { + //((CFormElm*)root)->getForm()->getComment(); + uint structSize = 0; + root->getStructSize(structSize); + while (num < structSize) + { + UFormElm::TWhereIsNode *whereN = new UFormElm::TWhereIsNode; + UFormElm::TWhereIsValue *whereV = new UFormElm::TWhereIsValue; + // Append a new item to the current parent's list of children. + std::string elmName; + if(root->getStructNodeName(num, elmName)) + { + QList columnData; + //QVariant value; + std::string value; + //NLMISC::CRGBA value_color; + //uint value_uint; + //sint value_sint; + //double value_double; + QString elmtType = ""; + UFormElm *elmt = 0; + if(root->getNodeByName(&elmt, elmName.c_str(), whereN, true)) + { + if (elmt) + { + if (elmt->isArray()) + elmtType = "Array"; + if (elmt->isStruct()) + elmtType = "Struct"; + if (elmt->isAtom()) + { + elmtType = "Atom"; + uint numDefinitions = 0; + const UType *type = elmt->getType(); + if (type) + { + numDefinitions = type->getNumDefinition(); + root->getValueByName(value, elmName.c_str(),UFormElm::Eval,whereV); + switch (type->getType()) + { + case UType::UnsignedInt: + value = QString("%1").arg(QString("%1").arg(value.c_str()).toDouble()).toStdString(); + elmtType.append("_uint");break; + case UType::SignedInt: + value = QString("%1").arg(QString("%1").arg(value.c_str()).toDouble()).toStdString(); + elmtType.append("_sint");break; + case UType::Double: + value = QString("%1").arg(QString("%1").arg(value.c_str()).toDouble(),0,'f',1).toStdString(); + elmtType.append("_double");break; + case UType::String: + elmtType.append("_string");break; + case UType::Color: + elmtType.append("_color");break; + default: + elmtType.append("_unknownType"); + } + } + else + { + elmtType.append("_noType"); + } + + if (numDefinitions) + { + std::string l, v; + QString tmpLabel, tmpValue; + for (uint i = 0; i < numDefinitions; i++) + { + type->getDefinition(i,l,v); + tmpLabel = l.c_str(); + tmpValue = v.c_str(); + if (type->getType() == UType::SignedInt) + { + if (QString("%1").arg(value.c_str()).toDouble() == tmpValue.toDouble()) { + value = l; + break; + } + } + if (type->getType() == UType::String) + { + if (QString(value.c_str()) == tmpValue) + { + value = l; + break; + } + } + } + } + } + if (elmt->isVirtualStruct()) + { + root->getValueByName(value, elmName.c_str(),UFormElm::Eval,whereV); + elmtType = "VirtualStruct"; + } + switch (*whereN) + { + case UFormElm::NodeForm: + elmtType.append("_fromForm"); break; + case UFormElm::NodeParentForm: + elmtType.append("_fromParentForm"); break; + case UFormElm::NodeDfn: + elmtType.append("_isDFN"); break; + case UFormElm::NodeType: + elmtType.append("_isType"); break; + default: + elmtType.append("_noNode"); + } + switch (*whereV) + { + case UFormElm::ValueForm: + elmtType.append("_formValue"); break; + case UFormElm::ValueParentForm: + elmtType.append("_parentValue"); break; + case UFormElm::ValueDefaultDfn: + elmtType.append("_dfnValue"); break; + case UFormElm::ValueDefaultType: + elmtType.append("_typeValue"); break; + default: + elmtType.append("_noValue"); + } + columnData << QString(elmName.c_str()) << QString(value.c_str()) << "";// << elmtType; + parent->appendChild(new CFormItem(elmt, columnData, parent, *whereV, *whereN)); + //if (parents.last()->childCount() > 0) { + // parents << parents.last()->child(parents.last()->childCount()-1); + //} + loadFormData(elmt, parent->child(parent->childCount()-1)); + } + else + { + // add Defaults + // TODO: spams warnings for non ATOM values but i dont get type of non existing nodes + bool success = root->getValueByName(value, elmName.c_str(),UFormElm::Eval,whereV); + switch (*whereN) + { + case UFormElm::NodeForm: + elmtType.append("_fromForm"); break; + case UFormElm::NodeParentForm: + elmtType.append("_fromParentForm"); break; + case UFormElm::NodeDfn: + elmtType.append("_isDFN"); break; + case UFormElm::NodeType: + elmtType.append("_isType"); break; + default: + elmtType.append("_noNode"); + } + switch (*whereV) + { + case UFormElm::ValueForm: + elmtType.append("_formValue"); break; + case UFormElm::ValueParentForm: + elmtType.append("_parentValue"); break; + case UFormElm::ValueDefaultDfn: + elmtType.append("_dfnValue"); break; + case UFormElm::ValueDefaultType: + elmtType.append("_typeValue"); break; + default: + elmtType.append("_noValue"); + } + + columnData << QString(elmName.c_str()) << QString(value.c_str()) << "";// << elmtType; + parent->appendChild(new CFormItem(elmt, columnData, parent, *whereV, *whereN)); + } + } + else + { + nlinfo("getNodeByName returned false"); + } + } + num++; + } + } + if (root->isArray()) + { + uint arraySize = 0; + root->getArraySize(arraySize); + while (num < arraySize) + { + std::string elmName; + if(root->getArrayNodeName(elmName, num)) + { + QList columnData; + std::string value; + QString elmtType = ""; + + UFormElm *elmt = 0; + if(root->getArrayNode(&elmt,0) && elmt) + { + if (elmt->isArray()) + elmtType = "Array"; + if (elmt->isStruct()) { + elmtType = "Struct"; + } + if (elmt->isAtom()) + { + elmt->getValue(value); + elmtType = "Atom"; + } + if (elmt->isVirtualStruct()) + elmtType = "VirtualStruct"; + + elmtType.append("_arrayValue"); + columnData << QString(elmName.c_str()) << QString(value.c_str()) << "";// << elmtType; + parent->appendChild(new CFormItem(elmt, columnData, parent)); + loadFormData(elmt, parent->child(parent->childCount()-1)); + } + } + num++; + } + } + } + + /******************************************************************************/ + + void CGeorgesFormModel::loadFormHeader() + { + + CFormItem *fi_pars = new CFormItem(m_rootElm, QList() << "parents", m_rootItem); + m_rootItem->appendChild(fi_pars); + + Q_FOREACH(QString str, m_parents) + { + fi_pars->appendChild(new CFormItem(m_rootElm, QList() << str, fi_pars)); + } + + /*QStringList dfns = _dependencies["dfn"]; + QStringList typs = _dependencies["typ"]; + + _dependencies.remove("dfn"); + _dependencies.remove("typ"); + + CFormItem *fi_dep = new CFormItem(_rootElm, QList() << "dependencies", _rootItem); + _rootItem->appendChild(fi_dep); + + if (!dfns.isEmpty()) { + CFormItem *fi_dfn = new CFormItem(_rootElm, QList() << "dfn", fi_dep); + fi_dep->appendChild(fi_dfn); + foreach(QString str, dfns) { + fi_dfn->appendChild(new CFormItem(_rootElm, QList() << str, fi_dfn)); + } + } + if (!typs.isEmpty()) { + CFormItem *fi_typ = new CFormItem(_rootElm, QList() << "typ", fi_dep); + fi_dep->appendChild(fi_typ); + foreach(QString str, typs) { + fi_typ->appendChild(new CFormItem(_rootElm, QList() << str, fi_typ)); + } + } + if (!_dependencies.isEmpty()) { + CFormItem *fi_other = new CFormItem(_rootElm, QList() << "other", fi_dep); + fi_dep->appendChild(fi_other); + foreach(QStringList list, _dependencies) { + foreach(QString str, list) { + fi_other->appendChild(new CFormItem(_rootElm, QList() << str, fi_other)); + } + } + }*/ + } + + /******************************************************************************/ + + void CGeorgesFormModel::setupModelData() + { + loadFormHeader(); + loadFormData(m_rootElm, m_rootItem); + } + + /******************************************************************************/ + + void CGeorgesFormModel::setShowParents( bool show ) { + m_showParents = show; + Q_EMIT layoutAboutToBeChanged(); + Q_EMIT layoutChanged(); + } + void CGeorgesFormModel::setShowDefaults( bool show ) + { + m_showDefaults = show; + Q_EMIT layoutAboutToBeChanged(); + Q_EMIT layoutChanged(); + } +} /* namespace Plugin */ + +/* end of file */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.h new file mode 100644 index 000000000..7668f9e7f --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.h @@ -0,0 +1,79 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef GEORGESFORM_MODEL_H +#define GEORGESFORM_MODEL_H + +// Qt includes +#include +#include +#include +#include + +// project includes + +namespace NLGEORGES { + class UFormElm; +} + +namespace Plugin +{ + + class CFormItem; + + class CGeorgesFormModel : public QAbstractItemModel + { + + public: + CGeorgesFormModel(NLGEORGES::UFormElm *root, QMap< QString, QStringList> deps, + QString comment, QStringList parents, QObject *parent = 0); + ~CGeorgesFormModel(); + + QVariant data(const QModelIndex &index, int role) const; + //bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + Qt::ItemFlags flags(const QModelIndex &index) 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; + CFormItem *getItem(const QModelIndex &index) const; + CGeorgesFormModel *model() { return this; } + bool showParents() { return m_showParents;} + bool showDefaults() { return m_showDefaults;} + void setShowParents( bool show ); + void setShowDefaults( bool show ); + + private: + void setupModelData(); + void loadFormData(NLGEORGES::UFormElm *rootElm, CFormItem *parent); + void loadFormHeader(); + + CFormItem* m_rootItem; + NLGEORGES::UFormElm* m_rootElm; + QMap< QString, QStringList> m_dependencies; + QString m_comments; + QStringList m_parents; + QList* m_parentRows; + + bool m_showParents; + bool m_showDefaults; + + };/* class CGeorgesFormModel */ + +} /* namespace Plugin */ + +#endif // GEORGESFORM_MODEL_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.cpp new file mode 100644 index 000000000..0950e1b04 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.cpp @@ -0,0 +1,94 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "georgesform_proxy_model.h" +#include "georgesform_model.h" + +// NeL includes +#include +#include + +// project includes +#include "formitem.h" + +#include + +namespace Plugin +{ + + bool CGeorgesFormProxyModel::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const + { + //nlinfo("CGeorgesFormProxyModel::filterAcceptsRow"); + + // column doesnt matter for item + CGeorgesFormModel *smodel = dynamic_cast(sourceModel()); + QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); + CFormItem *item = smodel->getItem(index); + + //qDebug() << smodel->showParents() << (item->valueFrom() == NLGEORGES::UFormElm::NodeParentForm); + //nlinfo("%s %d %d %d %d", item->data(index.column()).toString().toStdString().c_str(), + // item->valueFrom(), + // item->nodeFrom(), + // smodel->showParents(), + // (item->valueFrom() == NLGEORGES::UFormElm::NodeParentForm)); + + // if elm not existing it must be some kind of default or type value + if(!item->getFormElm()) + { + return smodel->showDefaults(); + } + + // else it might be some parent elm + switch (item->nodeFrom()) + { + case NLGEORGES::UFormElm::NodeParentForm: + { + return smodel->showParents(); + } + case NLGEORGES::UFormElm::NodeForm: + { + switch (item->valueFrom()) + { + case NLGEORGES::UFormElm::ValueParentForm: + { + return smodel->showParents(); + } + default: + { + CFormItem *parent = item->parent(); + if (parent && (parent->nodeFrom() == NLGEORGES::UFormElm::NodeParentForm)) + { + return smodel->showParents(); + } + } + } + } + } + return true; + } + +/******************************************************************************/ + + bool CGeorgesFormProxyModel::filterAcceptsColumn(int sourceRow, + const QModelIndex &sourceParent) const + { + //nlinfo("CGeorgesFormProxyModel::filterAcceptsColumn"); + return QSortFilterProxyModel::filterAcceptsColumn(sourceRow, sourceParent); + } +} /* namespace Plugin */ + +/* end of file */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.h new file mode 100644 index 000000000..570913dab --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.h @@ -0,0 +1,45 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef GEORGESFORM_PROXY_MODEL_H +#define GEORGESFORM_PROXY_MODEL_H + +// Qt includes +#include + +namespace Plugin +{ + + class CGeorgesFormProxyModel : public QSortFilterProxyModel + { + + public: + CGeorgesFormProxyModel(QObject *parent = 0): QSortFilterProxyModel(parent) + { + } + ~CGeorgesFormProxyModel() + { + } + + protected: + virtual bool filterAcceptsColumn ( int source_column, const QModelIndex & source_parent ) const ; + virtual bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const ; + + };/* class CGeorgesFormProxyModel */ + +} /* namespace NLQT */ + +#endif // GEORGESFORM_PROXY_MODEL_H From 232712531ceea69186442897ebf324695c61b037 Mon Sep 17 00:00:00 2001 From: aquiles Date: Tue, 16 Aug 2011 19:27:12 +0200 Subject: [PATCH 083/215] Changed: #1206 added pill icon to object_viewer tab --- .../plugins/object_viewer/icons/ic_nel_pill.png | Bin 0 -> 9291 bytes .../src/plugins/object_viewer/object_viewer.qrc | 1 + .../plugins/object_viewer/object_viewer_plugin.h | 2 +- 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/icons/ic_nel_pill.png diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/icons/ic_nel_pill.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/icons/ic_nel_pill.png new file mode 100644 index 0000000000000000000000000000000000000000..e748b8190c5dcc4e24d157fa45a71216d27ec7e7 GIT binary patch literal 9291 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 t_zc8nAU*@}8Hmq7dicons/ic_nel_water.png icons/ic_nel_wind.png icons/ic_nel_workspace_item.png + icons/ic_nel_pill.png images/nel.png icons/particles_system_24/ic_nel_collision_zone_item_24.png icons/particles_system_24/ic_nel_emitter_item_24.png diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.h index b5f9c2881..f0f23d398 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.h @@ -74,7 +74,7 @@ public: virtual QIcon icon() const { - return QIcon(); + return QIcon(":/icons/ic_nel_pill.png"); } virtual QUndoStack *undoStack(); From de16eef8fa96403d61c75bef933968677b5df8d3 Mon Sep 17 00:00:00 2001 From: aquiles Date: Tue, 16 Aug 2011 20:48:24 +0200 Subject: [PATCH 084/215] Changed: #1306 small additions/bug fixed here and there for your viewing pleasure :) --- .../georges_editor/georges_dirtree_dialog.cpp | 25 ++++++------- .../georges_editor/georges_dirtree_dialog.h | 2 +- .../georges_editor/georges_editor_form.cpp | 37 ++++++++++++++++--- .../georges_editor/georges_editor_form.h | 1 + .../georges_editor/georges_editor_plugin.cpp | 5 ++- .../georges_filesystem_model.cpp | 34 +++++++++-------- .../georges_editor/georges_filesystem_model.h | 16 ++++---- .../georges_treeview_dialog.cpp | 36 ++++++++---------- .../georges_editor/georges_treeview_dialog.h | 5 ++- .../georges_editor/georgesform_model.cpp | 13 ++++--- 10 files changed, 102 insertions(+), 72 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.cpp index 2a1151164..de7a105ab 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.cpp @@ -28,8 +28,7 @@ namespace Plugin CGeorgesDirTreeDialog::CGeorgesDirTreeDialog(QString ldPath, QWidget *parent) :QDockWidget(parent), - m_ldPath(ldPath), - m_proxyModel(0) + m_ldPath(ldPath) { m_ui.setupUi(this); @@ -38,10 +37,10 @@ CGeorgesDirTreeDialog::CGeorgesDirTreeDialog(QString ldPath, QWidget *parent) QApplication::style()->standardIcon(QStyle::SP_DialogCancelButton)); m_dirModel = new CGeorgesFileSystemModel(m_ldPath); - m_proxyModel = new CGeorgesFileSystemProxyModel(this); + //m_proxyModel = new CGeorgesFileSystemProxyModel(this); - m_proxyModel->setSourceModel(m_dirModel); - m_ui.dirTree->setModel(m_proxyModel); + //m_proxyModel->setSourceModel(m_dirModel); + m_ui.dirTree->setModel(m_dirModel); // TODO: filtering in tree model is ... complicated - so hide it for now m_ui.filterLineEdit->hide(); @@ -51,7 +50,7 @@ CGeorgesDirTreeDialog::CGeorgesDirTreeDialog(QString ldPath, QWidget *parent) if (m_dirModel->isCorrectLDPath()) { m_dirModel->setRootPath(m_ldPath); - m_ui.dirTree->setRootIndex(m_proxyModel->mapFromSource(m_dirModel->index(m_ldPath))); + m_ui.dirTree->setRootIndex(m_dirModel->index(m_ldPath)); } else { @@ -72,9 +71,9 @@ CGeorgesDirTreeDialog::~CGeorgesDirTreeDialog() void CGeorgesDirTreeDialog::fileSelected(QModelIndex index) { - if (index.isValid() && !m_dirModel->isDir(m_proxyModel->mapToSource(index))) + if (index.isValid() && !m_dirModel->isDir(index)) { - Q_EMIT selectedForm(m_dirModel->fileName(m_proxyModel->mapToSource(index))); + Q_EMIT selectedForm(m_dirModel->fileName(index)); } } @@ -91,18 +90,18 @@ void CGeorgesDirTreeDialog::ldPathChanged(QString path) m_ldPath = path; delete m_dirModel; - delete m_proxyModel; + //delete m_proxyModel; m_dirModel = new CGeorgesFileSystemModel(m_ldPath); - m_proxyModel = new CGeorgesFileSystemProxyModel(this); + //m_proxyModel = new CGeorgesFileSystemProxyModel(this); - m_proxyModel->setSourceModel(m_dirModel); - m_ui.dirTree->setModel(m_proxyModel); + //m_proxyModel->setSourceModel(m_dirModel); + m_ui.dirTree->setModel(m_dirModel); if (m_dirModel->isCorrectLDPath()) { m_dirModel->setRootPath(m_ldPath); - m_ui.dirTree->setRootIndex(m_proxyModel->mapFromSource(m_dirModel->index(m_ldPath))); + m_ui.dirTree->setRootIndex(m_dirModel->index(m_ldPath)); } else { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.h index d81897bf3..fa783bca0 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_dirtree_dialog.h @@ -45,7 +45,7 @@ private: Ui::CGeorgesDirTreeDialog m_ui; CGeorgesFileSystemModel *m_dirModel; - CGeorgesFileSystemProxyModel *m_proxyModel; + //CGeorgesFileSystemProxyModel *m_proxyModel; QString m_ldPath; Q_SIGNALS: diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp index 21aa6d8c6..c623d440b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp @@ -198,23 +198,48 @@ namespace Plugin { m_dockedWidgets.append(new CGeorgesTreeViewDialog(m_mainDock)); m_mainDock->addDockWidget(Qt::RightDockWidgetArea, m_dockedWidgets.last()); + connect(m_dockedWidgets.last(), SIGNAL(closing()), + this, SLOT(closingTreeView())); } else { + + Q_FOREACH(QDockWidget *wgt, m_dockedWidgets) + { + if (info.fileName() == wgt->windowTitle()) + { + wgt->raise(); + return; + } + } m_dockedWidgets.append(new CGeorgesTreeViewDialog(m_mainDock)); + connect(m_dockedWidgets.last(), SIGNAL(closing()), + this, SLOT(closingTreeView())); Q_ASSERT(m_dockedWidgets.size() > 1); m_mainDock->tabifyDockWidget(m_dockedWidgets.at(m_dockedWidgets.size() - 2), m_dockedWidgets.last()); } CForm *form = m_dockedWidgets.last()->getFormByName(info.fileName()); if (form) { - // delete newView; - // newView = createTreeView(fileName); - m_dockedWidgets.last()->setForm(form); - m_dockedWidgets.last()->loadFormIntoDialog(form); - //setCurrentFile(info.fileName()); - // return; + m_dockedWidgets.last()->setForm(form); + m_dockedWidgets.last()->loadFormIntoDialog(form); + QApplication::processEvents(); + m_dockedWidgets.last()->raise(); + connect(m_dockedWidgets.last(), SIGNAL(changeFile(QString)), + m_georgesDirTreeDialog, SLOT(changeFile(QString))); + } + else + { + m_dockedWidgets.last()->close(); } } + void GeorgesEditorForm::closingTreeView() + { + int i = m_dockedWidgets.size(); + m_dockedWidgets.removeAll(qobject_cast(sender())); + i = m_dockedWidgets.size(); + int j = i; + } + } /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h index 9e938f62c..4ed91a5f6 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h @@ -44,6 +44,7 @@ public Q_SLOTS: void save(); void settingsChanged(); void loadFile(const QString fileName); + void closingTreeView(); private: void readSettings(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp index e6cad341d..9ea9ae5ff 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp @@ -90,8 +90,9 @@ QString GeorgesEditorPlugin::description() const QStringList GeorgesEditorPlugin::dependencies() const { QStringList list; - list.append(Core::Constants::OVQT_CORE_PLUGIN); - list.append("ObjectViewer"); // TODO + // TODO + //list.append(Core::Constants::OVQT_CORE_PLUGIN); + //list.append("ObjectViewer"); return list; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.cpp index 796b3c457..4e021f681 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.cpp @@ -28,8 +28,10 @@ CGeorgesFileSystemModel::CGeorgesFileSystemModel(QString ldPath, QObject *parent m_correct(false) { checkLDPath(); - connect(this, SIGNAL(directoryLoaded(QString)), - this, SLOT(dir(const QString))); + + // this yielded no relevant performance boost on my observations + //connect(this, SIGNAL(directoryLoaded(QString)), + // this, SLOT(dir(const QString))); } CGeorgesFileSystemModel::~CGeorgesFileSystemModel() @@ -39,6 +41,8 @@ CGeorgesFileSystemModel::~CGeorgesFileSystemModel() void CGeorgesFileSystemModel::dir(const QString &dir) { + // in theory this should prefetch all directory entries for the + // filesystem model to speed up later work QModelIndex i = index(dir); if (hasChildren(i)) { @@ -117,18 +121,18 @@ void CGeorgesFileSystemModel::checkLDPath() // return (!indexNode->populatedChildren);*/ //} -CGeorgesFileSystemProxyModel::CGeorgesFileSystemProxyModel(QObject *parent) : QSortFilterProxyModel(parent) -{ - setFilterCaseSensitivity(Qt::CaseInsensitive); -} +//CGeorgesFileSystemProxyModel::CGeorgesFileSystemProxyModel(QObject *parent) : QSortFilterProxyModel(parent) +//{ +// setFilterCaseSensitivity(Qt::CaseInsensitive); +//} -bool CGeorgesFileSystemProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const -{ +//bool CGeorgesFileSystemProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const +//{ // TODO this is not perfect as it could be // eg it should filter all dirs which have no entry - QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); - if (sourceModel()->hasChildren(idx)) - { + //QModelIndex idx = sourceModel()->index(source_row, 0, source_parent); + //if (sourceModel()->hasChildren(idx)) + //{ // QString d = sourceModel()->data(idx).toString(); // //QModelIndex i = mapFromSource(source_parent); // //if (hasChildren(i)) { @@ -139,10 +143,10 @@ bool CGeorgesFileSystemProxyModel::filterAcceptsRow(int source_row, const QModel // bool test = filterAcceptsRow(c, child); // }*/ // } - return true; - } - return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); -} + // return true; + //} + //return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); +//} //QVariant CGeorgesFileSystemProxyModel::data ( const QModelIndex & index, int role ) const //{ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.h index 8ee06b3ef..03eb5ecc2 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_filesystem_model.h @@ -61,18 +61,18 @@ private Q_SLOTS: // A modified QSortFilterProxyModel that always accepts the root nodes in the tree // so filtering is only done on the children. - class CGeorgesFileSystemProxyModel : public QSortFilterProxyModel - { - Q_OBJECT + //class CGeorgesFileSystemProxyModel : public QSortFilterProxyModel + //{ + // Q_OBJECT - public: - CGeorgesFileSystemProxyModel(QObject *parent = 0); + //public: + // CGeorgesFileSystemProxyModel(QObject *parent = 0); //QVariant data(const QModelIndex& index, int role) const ; - protected: - bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; - }; + //protected: + // bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const; + //}; } /* namespace NLQT */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp index 2bcef52cb..09556a21a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp @@ -31,7 +31,7 @@ #include "georges.h" #include "georgesform_model.h" #include "georgesform_proxy_model.h" -//#include "formitem.h" +#include "formitem.h" //#include "formdelegate.h" using namespace NLMISC; @@ -72,7 +72,6 @@ namespace Plugin { //delete _ui.treeView->itemDelegateForColumn(1); delete m_form; - deleteLater(); //QSettings settings("RyzomCore", "GeorgesQt"); //settings.setValue("dirViewGeometry", saveGeometry()); } @@ -288,11 +287,18 @@ namespace Plugin void CGeorgesTreeViewDialog::doubleClicked ( const QModelIndex & index ) { // TODO: this is messy :( perhaps this can be done better - //CGeorgesFormProxyModel * mp = - // dynamic_cast(_ui.treeView->model()); - //CGeorgesFormModel *m = - // dynamic_cast(mp->sourceModel()); - //QModelIndex in = mp->mapToSource(index); + CGeorgesFormProxyModel * proxyModel = + dynamic_cast(m_ui.treeView->model()); + CGeorgesFormModel *model = + dynamic_cast(proxyModel->sourceModel()); + QModelIndex sourceIndex = proxyModel->mapToSource(index); + + CFormItem *item = model->getItem(sourceIndex); + + if (item->parent() && item->parent()->data(0) == "parents") + { + Q_EMIT changeFile(CPath::lookup(item->data(0).toString().toStdString(),false).c_str()); + } //// col containing additional stuff like icons //if (index.column() == 2) @@ -344,20 +350,8 @@ namespace Plugin void CGeorgesTreeViewDialog::closeEvent(QCloseEvent *event) { - /*if (Modules::mainWin().getEmptyView() == this) - { - event->ignore(); - } - else - { - if(Modules::mainWin().getTreeViewList().size() == 1) - { - Modules::mainWin().createEmptyView( - Modules::mainWin().getTreeViewList().takeFirst()); - } - Modules::mainWin().getTreeViewList().removeOne(this); - deleteLater(); - }*/ + Q_EMIT closing(); + deleteLater(); } void CGeorgesTreeViewDialog::filterRows() diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h index dee1216ca..9d4f02e5d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h @@ -64,13 +64,16 @@ namespace Plugin protected: void closeEvent(QCloseEvent *event); - Q_SIGNALS: + Q_SIGNALS: void changeFile(QString); void modified(bool); + void closing(); + public Q_SLOTS: void setForm(const CForm*); void loadFormIntoDialog(CForm *form = 0); void modifiedFile( ); + private Q_SLOTS: void doubleClicked ( const QModelIndex & index ); void filterRows(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp index 234c2d169..0ec8f785a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp @@ -573,12 +573,15 @@ namespace Plugin void CGeorgesFormModel::loadFormHeader() { - CFormItem *fi_pars = new CFormItem(m_rootElm, QList() << "parents", m_rootItem); - m_rootItem->appendChild(fi_pars); - - Q_FOREACH(QString str, m_parents) + if (m_parents.size()) { - fi_pars->appendChild(new CFormItem(m_rootElm, QList() << str, fi_pars)); + CFormItem *fi_pars = new CFormItem(m_rootElm, QList() << "parents" << "" << "", m_rootItem); + m_rootItem->appendChild(fi_pars); + + Q_FOREACH(QString str, m_parents) + { + fi_pars->appendChild(new CFormItem(m_rootElm, QList() << str << "" << "", fi_pars)); + } } /*QStringList dfns = _dependencies["dfn"]; From b83fb39a9431172a60db1af01439cb83bc89bb21 Mon Sep 17 00:00:00 2001 From: cemycc Date: Wed, 17 Aug 2011 11:54:00 +0300 Subject: [PATCH 085/215] Changed: #1307 Added support for unicode format on editors --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_phrase.cpp | 10 ++++++++-- .../translation_manager/editor_worksheet.cpp | 16 +++++++++------- .../translation_manager_main_window.cpp | 2 +- 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp index 6033ecb38..696bd41b1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp @@ -27,7 +27,8 @@ #include #include #include - +#include +#include // Project includes #include "editor_phrase.h" @@ -51,8 +52,9 @@ void CEditorPhrase::open(QString filename) // read the file content QFile file(filename); file.open(QIODevice::ReadOnly | QIODevice::Text); + QTextStream in(&file); // set the file content to the text edit - QByteArray data = file.readAll(); + QString data = in.readAll(); text_edit->append(data); // window settings setCurrentFile(filename); @@ -111,6 +113,8 @@ void CEditorPhrase::save() QFile file(current_file); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); + out.setCodec("UTF-8"); + out.setGenerateByteOrderMark(true); out<toPlainText(); setCurrentFile(current_file); } @@ -120,6 +124,8 @@ void CEditorPhrase::saveAs(QString filename) QFile file(filename); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); + out.setCodec("UTF-8"); + out.setGenerateByteOrderMark(true); out<toPlainText(); current_file = filename; setCurrentFile(current_file); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index ce02837c8..6e71a9290 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -1,4 +1,4 @@ -// Translation Manager Plugin - OVQT Plugin +// Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited // Copyright (C) 2011 Emanuel Costea // @@ -82,7 +82,7 @@ void CEditorWorksheet::open(QString filename) } else { QTableWidgetItem *row = new QTableWidgetItem(); ucstring row_value = wk_file.getData(i, j); - row->setText(tr(row_value.toString().c_str())); + row->setText(QString::fromUtf8(row_value.toUtf8().c_str())); if(hasHashValue) { table_editor->setItem(i - 1, j - 1, row); @@ -157,8 +157,8 @@ void CEditorWorksheet::save() ucstring tvalue; ucstring colname; uint rowIdf; - QString tvalueQt = table_editor->item(i, j)->text(); - tvalue = ucstring(tvalueQt.toStdString()); + QString tvalueQt = table_editor->item(i, j)->text(); + tvalue.fromUtf8(std::string(tvalueQt.toUtf8())); colname = wk_file.getData(0, j + colIdx); rowIdf = uint(i + 1); @@ -166,10 +166,10 @@ void CEditorWorksheet::save() { if(wk_file.getData(i + 1, j + colIdx) != tvalue) // verify the current value { - wk_file.setData(i + 1, j + colIdx, tvalue); // change the value + wk_file.setData(i + 1, j + colIdx, tvalue); // change the value } } else { - wk_file.setData(i + 1, j + colIdx, tvalue); // insert the value + wk_file.setData(i + 1, j + colIdx, tvalue); // insert the value } } } @@ -209,10 +209,12 @@ void CEditorWorksheet::saveAs(QString filename) { rowIdx = new_file.size(); new_file.resize(new_file.size() + 1); + ucstring tvalue; for(int j = 0; j < table_editor->columnCount(); j++) { QTableWidgetItem* item = table_editor->item(i, j); - new_file.setData(rowIdx, j + colIdx, ucstring(item->text().toStdString())); + tvalue.fromUtf8(std::string(item->text().toUtf8())); + new_file.setData(rowIdx, j + colIdx, tvalue); } } if(hasHashValue) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 5856f9c7d..365304c5b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -37,7 +37,7 @@ #include #include #include - +#include // Plugin includes #include "translation_manager_main_window.h" #include "translation_manager_constants.h" From ec3fcf5ef370c7b77bd39f617c024876afb63416 Mon Sep 17 00:00:00 2001 From: cemycc Date: Thu, 18 Aug 2011 00:58:26 +0300 Subject: [PATCH 086/215] Changed: #1307 Making some little modifcations in code. Open now saves the last directory --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_phrase.cpp | 31 +--- .../translation_manager/editor_worksheet.cpp | 161 +++++------------- .../translation_manager/editor_worksheet.h | 25 +-- .../translation_manager/ftp_selection.ui | 96 +++++------ .../translation_manager_main_window.cpp | 46 +++-- 5 files changed, 134 insertions(+), 225 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp index 696bd41b1..c1633c3be 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp @@ -75,29 +75,6 @@ void CEditorPhrase::newUndoCommandAdded() current_stack->push(new CUndoPhraseNewCommand(text_edit)); } -/* void CTextEdit::keyPressEvent(QKeyEvent *event) -{ - QString chars = event->text(); - int index = textCursor().position(); - - switch(event->key()) - { - case Qt::Key_Backspace: - if (index > 0) - m_undoStack->push(new CUndoPhraseRemoveCommand(index--, 1, this)); - break; - case Qt::Key_Delete: - if (index < toPlainText().length()) - m_undoStack->push(new CUndoPhraseRemoveCommand(index, 1, this)); - break; - default: - if (!chars.isEmpty()) - m_undoStack->push(new CUndoPhraseInsertCommand(index, chars, this)); - break; - } - -} */ - void CEditorPhrase::docContentsChanged() { setWindowModified(true); @@ -110,13 +87,7 @@ void CEditorPhrase::activateWindow() void CEditorPhrase::save() { - QFile file(current_file); - file.open(QIODevice::WriteOnly | QIODevice::Text); - QTextStream out(&file); - out.setCodec("UTF-8"); - out.setGenerateByteOrderMark(true); - out<toPlainText(); - setCurrentFile(current_file); + saveAs(current_file); } void CEditorPhrase::saveAs(QString filename) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index 6e71a9290..c957e6506 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -34,8 +34,6 @@ using namespace std; namespace TranslationManager { - - void CEditorWorksheet::open(QString filename) { STRING_MANAGER::TWorksheet wk_file; @@ -134,54 +132,7 @@ void CEditorWorksheet::activateWindow() void CEditorWorksheet::save() { - STRING_MANAGER::TWorksheet wk_file; - loadExcelSheet(current_file.toStdString(), wk_file, true); - uint rowIdx; - uint colIdx = 0; - bool hasHashValue = false; - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - hasHashValue = true; - colIdx = 1; - } - for(int i = 0; i < table_editor->rowCount(); i++) - { - // maybe extra rows ? - if((unsigned)table_editor->rowCount() > (wk_file.size() - 1)) - { - rowIdx = wk_file.size(); - wk_file.resize(rowIdx + table_editor->rowCount() - wk_file.size() + 1); - } - for(int j = 0; j < table_editor->columnCount(); j++) - { - ucstring tvalue; - ucstring colname; - uint rowIdf; - QString tvalueQt = table_editor->item(i, j)->text(); - tvalue.fromUtf8(std::string(tvalueQt.toUtf8())); - colname = wk_file.getData(0, j + colIdx); - - rowIdf = uint(i + 1); - if(wk_file.findRow(j + colIdx, colname, rowIdf)) // search for the row - { - if(wk_file.getData(i + 1, j + colIdx) != tvalue) // verify the current value - { - wk_file.setData(i + 1, j + colIdx, tvalue); // change the value - } - } else { - wk_file.setData(i + 1, j + colIdx, tvalue); // insert the value - } - } - } - if(hasHashValue) - { - // rewrite the hash codes - makeHashCode(wk_file, true); - } - // write to file - ucstring s = prepareExcelSheet(wk_file); - NLMISC::CI18N::writeTextFile(current_file.toStdString(), s, false); - setCurrentFile(current_file); + saveAs(current_file); } void CEditorWorksheet::saveAs(QString filename) @@ -238,8 +189,8 @@ void CEditorWorksheet::deleteRow() { int selected_row = table_editor->currentRow(); QMessageBox msgBox; - msgBox.setText("The row will be deleted."); - msgBox.setInformativeText("Do you want to delete the selected row ?"); + msgBox.setText(tr("The row will be deleted.")); + msgBox.setInformativeText(tr("Do you want to delete the selected row ?")); msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); msgBox.setDefaultButton(QMessageBox::No); int ret = msgBox.exec(); @@ -286,30 +237,15 @@ void CEditorWorksheet::extractBotNames(list filters, string level_design for (; it != last; ++it) { - QList search_results = table_editor->findItems(tr(it->first.c_str()), Qt::MatchExactly); + QList search_results = table_editor->findItems(QString(it->first.c_str()), Qt::MatchExactly); if(search_results.size() == 0) { - const int currentRow = table_editor->rowCount(); - table_editor->setRowCount(currentRow + 1); - QTableWidgetItem *bot_name_row = new QTableWidgetItem(); - bot_name_row->setText(tr(it->first.c_str())); - bot_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 0, bot_name_row); - QTableWidgetItem *translation_name_row = new QTableWidgetItem(); - translation_name_row->setBackgroundColor(QColor("#F75D59")); - translation_name_row->setText(tr(it->first.c_str())); - table_editor ->setItem(currentRow , 1, translation_name_row); - QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); - sheet_name_row->setText(tr(it->second.SheetName.c_str())); - sheet_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 2, sheet_name_row); - if(!modified) modified = true; - CTableWidgetItemStore bot_name_row_s(bot_name_row, currentRow, 0); - new_items.push_back(bot_name_row_s); - CTableWidgetItemStore translation_name_row_s(translation_name_row, currentRow, 1); - new_items.push_back(translation_name_row_s); - CTableWidgetItemStore sheet_name_row_s(sheet_name_row, currentRow, 2); - new_items.push_back(sheet_name_row_s); + QList records; + records.push_back(tr(it->first.c_str())); + records.push_back(tr(it->first.c_str())); + records.push_back(tr(it->second.SheetName.c_str())); + insertTableRecords(records, new_items); + if(!modified) modified = true; } } ebn.cleanSimpleNames(); @@ -321,30 +257,15 @@ void CEditorWorksheet::extractBotNames(list filters, string level_design for (; it != last; ++it) { string gnName = "gn_" + ebn.cleanupName(*it); - QList search_results = table_editor->findItems(tr((*it).c_str()), Qt::MatchExactly); + QList search_results = table_editor->findItems(QString((*it).c_str()), Qt::MatchExactly); if(search_results.size() == 0) { - const int currentRow = table_editor->rowCount(); - table_editor->setRowCount(currentRow + 1); - QTableWidgetItem *bot_name_row = new QTableWidgetItem(); - bot_name_row->setText(tr((*it).c_str())); - bot_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 0, bot_name_row); - QTableWidgetItem *translation_name_row = new QTableWidgetItem(); - translation_name_row->setBackgroundColor(QColor("#F75D59")); - translation_name_row->setText(tr(gnName.c_str())); - table_editor ->setItem(currentRow , 1, translation_name_row); - QTableWidgetItem *sheet_name_row = new QTableWidgetItem(); - sheet_name_row->setText(" "); - sheet_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, 2, sheet_name_row); + QList records; + records.push_back(tr((*it).c_str())); + records.push_back(tr(gnName.c_str())); + records.push_back(" "); + insertTableRecords(records, new_items); if(!modified) modified = true; - CTableWidgetItemStore bot_name_row_s(bot_name_row, currentRow, 0); - new_items.push_back(bot_name_row_s); - CTableWidgetItemStore translation_name_row_s(translation_name_row, currentRow, 1); - new_items.push_back(translation_name_row_s); - CTableWidgetItemStore sheet_name_row_s(sheet_name_row, currentRow, 2); - new_items.push_back(sheet_name_row_s); } } ebn.cleanGenericNames(); @@ -399,8 +320,7 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis string keyName = allWords[i]; QList search_results = table_editor->findItems(tr(keyName.c_str()), Qt::MatchExactly); if(search_results.size() == 0) - { - + { int knPos = 0, nPos = 0; if(workSheet.getData(0, 0) == ucstring("*HASH_VALUE")) { @@ -409,25 +329,13 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis } else { knPos = keyColIndex; nPos = nameColIndex; - } - const int currentRow = table_editor->rowCount(); - table_editor->setRowCount(currentRow + 1); - // keyName row - QTableWidgetItem *key_name_row = new QTableWidgetItem(); - key_name_row->setText(tr(keyName.c_str())); - key_name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, knPos, key_name_row); - // nameColumn key - QTableWidgetItem *name_row = new QTableWidgetItem(); - name_row->setText(QString("") + tr(keyName.c_str())); - name_row->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, nPos, name_row); - if(!modified) modified = true; - CTableWidgetItemStore key_name_row_s(key_name_row, currentRow, knPos); - new_items.push_back(key_name_row_s); - CTableWidgetItemStore name_row_s(name_row, currentRow, nPos); - new_items.push_back(name_row_s); + } + QList records; + records.push_back(tr(keyName.c_str())); + records.push_back(QString("") + tr(keyName.c_str())); + insertTableRecords(records, new_items); + if(!modified) modified = true; } } current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); @@ -438,6 +346,23 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis } } +void CEditorWorksheet::insertTableRecords(QList records, QList new_items) +{ + const int currentRow = table_editor->rowCount(); + table_editor->setRowCount(currentRow + 1); + int n = 0; + Q_FOREACH(QString record, records) + { + QTableWidgetItem *rec = new QTableWidgetItem(); + rec->setBackgroundColor(QColor("#F75D59")); + table_editor ->setItem(currentRow, n, rec); + CTableWidgetItemStore rec_s(rec, currentRow, n); + new_items.push_back(rec_s); + n++; + } + +} + bool CEditorWorksheet::compareWorksheetFile(QString filename) { STRING_MANAGER::TWorksheet wk_file; @@ -503,7 +428,7 @@ void CEditorWorksheet::mergeWorksheetFile(QString filename) } } else { QErrorMessage error; - error.showMessage("This file is not a worksheet file."); + error.showMessage(tr("This file is not a worksheet file.")); error.exec(); } } @@ -513,8 +438,8 @@ void CEditorWorksheet::closeEvent(QCloseEvent *event) if(isWindowModified()) { QMessageBox msgBox; - msgBox.setText("The document has been modified."); - msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setText(tr("The document has been modified.")); + msgBox.setInformativeText(tr("Do you want to save your changes?")); msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Save); int ret = msgBox.exec(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 839601047..487a9eea8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -40,6 +40,18 @@ namespace TranslationManager { +struct CTableWidgetItemStore +{ +public: + CTableWidgetItemStore(QTableWidgetItem *item, int row, int column) : + m_item(item), + m_row(row), + m_column(column) { } + QTableWidgetItem *m_item; + int m_row; + int m_column; +}; + class CEditorWorksheet : public CEditor { Q_OBJECT @@ -57,6 +69,7 @@ public: bool compareWorksheetFile(QString filename); void extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig); void extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder); + void insertTableRecords(QList records, QList new_items); bool isBotNamesTable(); bool isSheetTable(QString type); void closeEvent(QCloseEvent *event); @@ -69,18 +82,6 @@ private Q_SLOTS: }; -struct CTableWidgetItemStore -{ -public: - CTableWidgetItemStore(QTableWidgetItem *item, int row, int column) : - m_item(item), - m_row(row), - m_column(column) { } - QTableWidgetItem *m_item; - int m_row; - int m_column; -}; - class CUndoWorksheetCommand : public QUndoCommand { public: diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui index b8b0bf447..444a7d97e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui @@ -6,7 +6,7 @@ 0 0 - 388 + 446 560 @@ -18,7 +18,7 @@ 10 10 - 371 + 421 541 @@ -29,9 +29,9 @@ 10 - 130 - 351 - 361 + 110 + 401 + 381 @@ -55,7 +55,7 @@ 10 40 - 331 + 381 311 @@ -69,49 +69,9 @@ - 11 - 29 - 351 - 101 - - - - - - - Ftp server - - - - - - - - - - Connect - - - - - - - - - - - :/translationManager/images/cdtoparent.png:/translationManager/images/cdtoparent.png - - - - - - - - - 0 + 10 500 - 371 + 401 33 @@ -132,6 +92,46 @@ + + + + 10 + 20 + 411 + 81 + + + + + + + Ftp server + + + + + + + + + + + + + + :/translationManager/images/cdtoparent.png:/translationManager/images/cdtoparent.png + + + + + + + Connect + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 365304c5b..ee509c8ad 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -37,7 +37,6 @@ #include #include #include -#include // Plugin includes #include "translation_manager_main_window.h" #include "translation_manager_constants.h" @@ -70,7 +69,7 @@ CMainWindow::CMainWindow(QWidget *parent) void CMainWindow::createToolbar() { // File menu - openAct = new QAction(QIcon(Core::Constants::ICON_OPEN), "&Open...", this); + openAct = new QAction(QIcon(Core::Constants::ICON_OPEN), "&Open file(s)...", this); _ui.toolBar->addAction(openAct); connect(openAct, SIGNAL(triggered()), this, SLOT(open())); saveAct = new QAction(QIcon(Core::Constants::ICON_SAVE), "&Save...", this); @@ -147,11 +146,11 @@ void CMainWindow::updateToolbar(QMdiSubWindow *window) if(QString(window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor { //setContextMenuPolicy(Qt::ActionsContextMenu); - QAction *insertRowAct = new QAction("Insert new row", this); + QAction *insertRowAct = new QAction(tr("Insert new row"), this); connect(insertRowAct, SIGNAL(triggered()), window, SLOT(insertRow())); //addAction(insertRowAct); windowMenu->addAction(insertRowAct); - QAction *deleteRowAct = new QAction("Delete row", this); + QAction *deleteRowAct = new QAction(tr("Delete row"), this); connect(deleteRowAct, SIGNAL(triggered()), window, SLOT(deleteRow())); //addAction(deleteRowAct); windowMenu->addAction(deleteRowAct); @@ -202,7 +201,14 @@ void CMainWindow::updateWindowsList() void CMainWindow::open() { - QString file_name = QFileDialog::getOpenFileName(this); + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("translationmanager"); + QString lastOpenLocation = settings->value("lastOpenLocation").toString(); + QString file_name = QFileDialog::getOpenFileName(this, tr("Open translation file"), lastOpenLocation, tr("Translation files (*txt)")); + QFileInfo* file_info = new QFileInfo(file_name); + settings->setValue("lastOpenLocation", file_info->absolutePath()); + settings->endGroup(); + if(!file_name.isEmpty()) { CEditor *editor = getEditorByWindowFilePath(file_name); @@ -303,14 +309,22 @@ void CMainWindow::initializeSettings(bool georges = false) if(initialize_settings["ligo"] == false) { - //------------------------------------------------------------------- - // init ligo config - string ligoPath = CPath::lookup("world_editor_classes.xml", true, true); - ligoConfig.readPrimitiveClass(ligoPath.c_str(), false); - NLLIGO::Register(); - NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &ligoConfig; - initialize_settings["ligo"] = true; + try + { + // Search path of file world_editor_classes.xml + std::string ligoPath = NLMISC::CPath::lookup("world_editor_classes.xml"); + // Init LIGO + ligoConfig.readPrimitiveClass(ligoPath.c_str(), true); + NLLIGO::Register(); + NLLIGO::CPrimitiveContext::instance().CurrentLigoConfig = &ligoConfig; + initialize_settings["ligo"] = true; + } + catch (NLMISC::Exception &e) + { + nlerror("Can't found path to world_editor_classes.xml"); + } } + } void CMainWindow::extractWords(QString typeq) @@ -510,7 +524,7 @@ bool CMainWindow::verifySettings() if(level_design_path.isNull() || primitives_path.isNull() || work_path.isNull()) { QErrorMessage error_settings; - error_settings.showMessage("Please write all the paths on the settings dialog."); + error_settings.showMessage(tr("Please write all the paths on the settings dialog.")); error_settings.exec(); count_errors = true; } @@ -574,11 +588,9 @@ bool CMainWindow::isWorksheetEditor(QString filename) { if(wk_file.ColCount > 1) return true; - else - return false; - } else { - return false; } + + return false; } bool CMainWindow::isPhraseEditor(QString filename) From f43774765d3e0ade14780ed391b35f425c78aab0 Mon Sep 17 00:00:00 2001 From: sfb Date: Fri, 19 Aug 2011 07:50:05 -0500 Subject: [PATCH 087/215] Changed: Turned off zone painter, fixed nelns and snowballs CMake builds for gcc 4.4.3 --- code/CMakeLists.txt | 4 +- .../src/plugins/CMakeLists.txt | 8 +- code/nelns/CMakeLists.txt | 28 +- .../admin_executor_service/log_report.cpp | 1161 +++++++++-------- .../nel_launcher_qt/CMakeLists.txt | 77 +- code/snowballs2/CMakeLists.txt | 170 +-- code/snowballs2/CMakePackaging.txt | 113 ++ 7 files changed, 765 insertions(+), 796 deletions(-) create mode 100644 code/snowballs2/CMakePackaging.txt diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 630ff4414..4d429e693 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -143,11 +143,11 @@ IF(WITH_RYZOM) ENDIF(WITH_RYZOM) IF(WITH_NELNS) -# ADD_SUBDIRECTORY(nelns) + ADD_SUBDIRECTORY(nelns) ENDIF(WITH_NELNS) IF(WITH_SNOWBALLS) -# ADD_SUBDIRECTORY(snowballs2) + ADD_SUBDIRECTORY(snowballs2) ENDIF(WITH_SNOWBALLS) IF(WITH_TOOLS) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt index 66cbdb188..3babfdab1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt @@ -1,14 +1,16 @@ ADD_SUBDIRECTORY(core) ADD_SUBDIRECTORY(example) ADD_SUBDIRECTORY(ovqt_sheet_builder) -ADD_SUBDIRECTORY(landscape_editor) +ADD_SUBDIRECTORY(landscape_editor) ADD_SUBDIRECTORY(log) ADD_SUBDIRECTORY(disp_sheet_id) ADD_SUBDIRECTORY(object_viewer) -ADD_SUBDIRECTORY(zone_painter) ADD_SUBDIRECTORY(georges_editor) +# Note: Temporarily disabled until development continues. +#ADD_SUBDIRECTORY(zone_painter) + # Ryzom Specific Plugins IF(WITH_RYZOM AND WITH_RYZOM_TOOLS) ADD_SUBDIRECTORY(mission_compiler) -ENDIF(WITH_RYZOM AND WITH_RYZOM_TOOLS) \ No newline at end of file +ENDIF(WITH_RYZOM AND WITH_RYZOM_TOOLS) diff --git a/code/nelns/CMakeLists.txt b/code/nelns/CMakeLists.txt index 9a8402fac..63916ba08 100644 --- a/code/nelns/CMakeLists.txt +++ b/code/nelns/CMakeLists.txt @@ -1,14 +1,14 @@ -FIND_PACKAGE(MySQL) -FIND_PACKAGE(CURL) - -IF(BUILD_SERVICES) - ADD_SUBDIRECTORY(admin_executor_service) - ADD_SUBDIRECTORY(admin_service) - ADD_SUBDIRECTORY(naming_service) - ADD_SUBDIRECTORY(login_service) - ADD_SUBDIRECTORY(welcome_service) -ENDIF(BUILD_SERVICES) - -IF(BUILD_LOGIN_SYSTEM) - ADD_SUBDIRECTORY(login_system) -ENDIF(BUILD_LOGIN_SYSTEM) +FIND_PACKAGE(MySQL) +FIND_PACKAGE(CURL) + +IF(WITH_NELNS_SERVER) + ADD_SUBDIRECTORY(admin_executor_service) + ADD_SUBDIRECTORY(admin_service) + ADD_SUBDIRECTORY(naming_service) + ADD_SUBDIRECTORY(login_service) + ADD_SUBDIRECTORY(welcome_service) +ENDIF(WITH_NELNS_SERVER) + +IF(WITH_NELNS_LOGIN_SYSTEM) + ADD_SUBDIRECTORY(login_system) +ENDIF(WITH_NELNS_LOGIN_SYSTEM) diff --git a/code/nelns/admin_executor_service/log_report.cpp b/code/nelns/admin_executor_service/log_report.cpp index eac5f83c3..7106fb9d1 100644 --- a/code/nelns/admin_executor_service/log_report.cpp +++ b/code/nelns/admin_executor_service/log_report.cpp @@ -1,580 +1,581 @@ -// NeLNS - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#include "log_report.h" -#include -#include "nel/misc/common.h" -#include "nel/misc/displayer.h" -#include "nel/misc/file.h" -#include "nel/misc/path.h" -#include "nel/misc/variable.h" - -using namespace NLMISC; -using namespace std; - - -CVariable LogPath( "LogReport","LogPath", "Path of the log files", ".", 0, true ); - -const uint MAX_LOG_LINE_SIZE = 1024; -//nlctassert(MAX_LOG_LINE_SIZE>0); - -enum TLogLineHeader { LHDate, LHTime, LHType, LHThread, LHService, LHCodeFile, LHCodeLine, LHSeparator, LH_NB_FIELDS }; - - -/// -bool isLogFile( const std::string& filename ) -{ - uint len = (uint)filename.size(); - return (len >= 4 ) && (filename.substr( len-4 ) == ".log"); -} - -/// -inline bool isNumberChar( char c ) -{ - return (c >= '0') && (c <= '9'); -} - -/// -void sortLogFiles( vector& filenames ) -{ - uint i; - for ( i=0; i!=filenames.size(); ++i ) - { - // Ensure that a log file without number comes *after* the ones with a number - string name = string(filenames[i]); - string::size_type dotpos = name.find_last_of('.'); - if ( (dotpos!=string::npos) && (dotpos > 2) ) - { - if ( ! (isNumberChar(name[dotpos-1]) && isNumberChar(name[dotpos-2]) && isNumberChar(name[dotpos-3])) ) - { - name = name.substr( 0, dotpos ) + "ZZZ" + name.substr( dotpos ); - filenames[i] = name.c_str(); - } - } - } - sort( filenames.begin(), filenames.end() ); - for ( i=0; i!=filenames.size(); ++i ) - { - // Set the original names back - string name = filenames[i]; - string::size_type tokenpos = name.find( "ZZZ." ); - if ( tokenpos != string::npos ) - { - name = name.substr( 0, tokenpos ) + name.substr( tokenpos + 3 ); - filenames[i] = name.c_str(); - } - } -} - -void CMakeLogTask::setLogPath(const std::string & logPath) -{ - _LogPaths.resize( 1 ); - _LogPaths[0] = logPath; -} - -void CMakeLogTask::setLogPaths(const std::vector& logPaths) -{ - _LogPaths = logPaths; -} - -void CMakeLogTask::setLogPathToDefault() -{ - setLogPath( LogPath.get() ); -} - -/* - * - */ -CMakeLogTask::~CMakeLogTask() -{ - if ( _Thread ) // implies && _OutputLogReport - { - if ( ! _Complete ) - { - pleaseStop(); - _Thread->wait(); - } - clear(); - } -} - - -/* - * - */ -void CMakeLogTask::start() -{ - if ( _Thread ) - { - if ( _Complete ) - clear(); - else - return; - } - _Stopping = false; - _Complete = false; - _Thread = NLMISC::IThread::create( this ); - _OutputLogReport = new CLogReport(); - _Thread->start(); -} - - -/* - * - */ -void CMakeLogTask::clear() -{ - if (_Thread) - { - delete _Thread; - _Thread = NULL; - } - if (_OutputLogReport) - { - delete _OutputLogReport; - _OutputLogReport = NULL; - } -} - -/* - * - */ -void CMakeLogTask::terminateTask() -{ - if (!_Thread) // _Thread _implies _OutputLogReport - return; - - pleaseStop(); - _Thread->wait(); - - clear(); -} - -// -bool isOfLogDotLogFamily( const std::string& filename ) -{ - return ((filename == "log.log") || - ((filename.size() == 10) && - (filename.substr( 0, 3 ) == "log") && - isNumberChar(filename[3]) && isNumberChar(filename[4]) && isNumberChar(filename[5]) && - (filename.substr( 6, 4 ) == ".log")) ); -} - - -enum TVersionTargetMode { TTMAll, TTMMatchAllV, TTMMatchExactV, TTMMatchGreaterV, TTMMatchLowerV } targetMode; -const uint CurrentVersion = std::numeric_limits::max(); - -// Return true and logVersion, or false if not a log with version -bool getLogVersion( const std::string& filename, uint& logVersion ) -{ - uint len = (uint)filename.size(); - if ( (len > 4) && (filename.substr( len-4 ) == ".log") ) - { - if ( filename.substr(0, 3) == "log" ) - { - if ( (len == 7) || - ((len == 10) && (isNumberChar(filename[3]) && isNumberChar(filename[4]) && isNumberChar(filename[5]))) ) - { - logVersion = CurrentVersion; - return true; - } - } - else if ( filename[0] == 'v' ) - { - string::size_type p = filename.find( "_", 1 ); - if ( p != string::npos ) - { - if ( (len == p + 8) || - ((len == p + 11) && (isNumberChar(filename[p+4]) && isNumberChar(filename[p+5]) && isNumberChar(filename[p+6]))) ) - { - NLMISC::fromString( filename.substr( 1, p-1 ), logVersion ); - return true; - } - } - } - } - return false; -} - -// Assumes filename is .log file -bool matchLogTarget( const std::string& filename, TVersionTargetMode targetMode, uint targetVersion ) -{ - if ( targetMode == TTMAll ) - return true; - - uint version; - - // Get version or exclude non-standard log files - if ( ! getLogVersion( filename, version ) ) - return false; - - // Exclude non-matching version - switch ( targetMode ) - { - case TTMMatchExactV: - return (version == targetVersion); // break; - case TTMMatchGreaterV: - return (version >= targetVersion); // break; - case TTMMatchLowerV: - return (version <= targetVersion); // break; - default: // TTMMatchAllV - return true; - } -} - -/* - * - */ -void CMakeLogTask::run() -{ - // Parse log target - uint targetVersion = CurrentVersion; - uint lts = (uint)_LogTarget.size(); - if ( _LogTarget.empty() || (_LogTarget == "v") ) - { - targetMode = TTMMatchExactV; - } - else if ( _LogTarget == "v*" ) - { - targetMode = TTMMatchAllV; - } - else if ( _LogTarget == "*" ) - { - targetMode = TTMAll; - } - else if ( (lts > 1) && (_LogTarget[0] == 'v') ) - { - uint additionalChars = 1; - if ( _LogTarget[lts-1] == '+' ) - targetMode = TTMMatchGreaterV; - else if ( _LogTarget[lts-1] == '-' ) - targetMode = TTMMatchLowerV; - else - { - targetMode = TTMMatchExactV; - additionalChars = 0; - } - - NLMISC::fromString( _LogTarget.substr( 1, lts-additionalChars-1 ), targetVersion ); - } - else - { - nlwarning( "Invalid log target argument: %s", _LogTarget.c_str() ); - _Complete = true; - return; - } - - // Get log files and sort them - vector filenames; - vector filenamesOfPath; - for ( vector::const_iterator ilf=_LogPaths.begin(); ilf!=_LogPaths.end(); ++ilf ) - { - string path = (*ilf); - if ( (! path.empty()) && (path[path.size()-1]!='/') ) - path += "/"; - filenamesOfPath.clear(); - CPath::getPathContent( path, false, false, true, filenamesOfPath, NULL, true ); - vector::iterator ilf2 = partition( filenamesOfPath.begin(), filenamesOfPath.end(), isLogFile ); - filenamesOfPath.erase( ilf2, filenamesOfPath.end() ); - sortLogFiles( filenamesOfPath ); - filenames.insert( filenames.end(), filenamesOfPath.begin(), filenamesOfPath.end() ); - } - - // Analyse log files - _OutputLogReport->reset(); - uint nbLines = 0; - char line [MAX_LOG_LINE_SIZE]; - - uint nbSkippedFiles = 0; - for ( vector::const_iterator ilf=filenames.begin(); ilf!=filenames.end(); ++ilf ) - { - string shortname = CFile::getFilename( *ilf ); - - // Filter log files based on filename before opening them - if ( ! matchLogTarget( shortname, targetMode, targetVersion ) ) - { - ++nbSkippedFiles; - continue; - } - - nlinfo( "Processing %s (%u/%u)", (*ilf).c_str(), ilf-filenames.begin(), filenames.size() ); - CIFile logfile; - if ( logfile.open( *ilf, true ) ) - { - _OutputLogReport->setProgress( (uint)(ilf-filenames.begin()), (uint)filenames.size() ); - while ( ! logfile.eof() ) - { - logfile.getline( line, MAX_LOG_LINE_SIZE ); - line[MAX_LOG_LINE_SIZE-1] = '\0'; // force valid end of line - _OutputLogReport->pushLine( line ); - ++nbLines; - - if ( isStopping() ) - return; - } - } - } - nlinfo( "%u lines processed", nbLines ); - if ( nbSkippedFiles != 0 ) - nlinfo( "%u log files skipped, not matching target %s", nbSkippedFiles, _LogTarget.c_str() ); - _Complete = true; -} - - -/* - * Add a log line to the report tree - */ -void CLogReport::pushLine( const std::string& line, NLMISC::CLog::TLogType onlyType, bool countOtherTypes ) -{ - // Ignore session title - if ( (line.size() > 14) && (line.substr( 0, 14 ) == "Log Starting [") ) - return; - - // Decode standard log line - vector lineTokens; - explode( line, string(" "), lineTokens ); - - if ( lineTokens.size() < LH_NB_FIELDS ) - return; - - // Filter log type - if ( onlyType != CLog::LOG_UNKNOWN ) - { - if ( lineTokens[LHType] != IDisplayer::logTypeToString( onlyType ) ) - { - if ( countOtherTypes ) - storeLine( lineTokens, true ); - return; - } - } - - // Store - storeLine( lineTokens, false ); -} - - -/* - * - */ -void CLogReportNode::storeLine( const std::vector& lineTokens, bool mainCountOnly ) -{ - // Get service name from "[machine/]serviceName-serviceId" - string service = lineTokens[LHService]; - string::size_type p = service.find( '/' ); - if ( p != string::npos ) - service = service.substr( p+1 ); - p = service.find( '-' ); - if ( p != string::npos ) - service = service.substr( 0, p ); - - // Store to appropriate child - CLogReportLeaf *child = getChild( service ); - if ( ! child ) - child = addChild( service ); - child->storeLine( lineTokens, mainCountOnly ); -} - - -/* - * - */ -void CLogReportLeaf::storeLine( const std::vector& lineTokens, bool mainCountOnly ) -{ - if ( ! mainCountOnly ) - { - // Build key from "codeFile codeLine" - string key = lineTokens[LHCodeFile] + ":" + lineTokens[LHCodeLine]; - _LogLineInfo[key].addAnOccurence( lineTokens ); - } - ++_Counts[lineTokens[LHType]]; - ++_TotalLines; -} - - -/* - * - */ -void CLogLineInfo::addAnOccurence( const std::vector& lineTokens ) -{ - if ( NbOccurences == 0 ) - { - for ( uint i=LH_NB_FIELDS; ireport( targetLog, true ); - } - else - { - targetLog->displayNL( "Nothing found for service %s", service.c_str() ); - } -} - - -/* - * Get results for a service (all distinct lines, sorted by occurence) - */ -void CLogReportLeaf::report( NLMISC::CLog *targetLog, bool ) -{ - // Sort it - typedef multimap< uint, pair< string, const CLogLineInfo * >, std::greater > CSortedByOccurenceLogLineInfoMap; - CSortedByOccurenceLogLineInfoMap sortedByOccurence; - for ( CLogLineInfoMap::const_iterator it=_LogLineInfo.begin(); it!=_LogLineInfo.end(); ++it ) - { - const string &key = (*it).first; - const CLogLineInfo& info = (*it).second; - sortedByOccurence.insert( make_pair( info.NbOccurences, make_pair( key, &info ) ) ); - } - - // Display it - for ( CSortedByOccurenceLogLineInfoMap::const_iterator iso=sortedByOccurence.begin(); iso!=sortedByOccurence.end(); ++iso ) - { - const string &key = (*iso).second.first; - const CLogLineInfo& info = *((*iso).second.second); - targetLog->displayRawNL( "%s %6u %s : %s", _Service.c_str(), info.NbOccurences, key.c_str(), info.SampleLogText.c_str() ); - } -} - - -/* - * Return the number of lines displayed - */ -uint CLogReportLeaf::reportPart( uint beginIndex, uint maxNbLines, NLMISC::CLog *targetLog ) -{ - uint i = 0; - CLogLineInfoMap::const_iterator it; - for ( it=_LogLineInfo.begin(); it!=_LogLineInfo.end(); ++it ) - { - if ( i >= beginIndex ) - { - if ( i >= maxNbLines ) - return i - beginIndex; - - const string &key = (*it).first; - const CLogLineInfo& info = (*it).second; - targetLog->displayRawNL( "%s %6u %s : %s", _Service.c_str(), info.NbOccurences, key.c_str(), info.SampleLogText.c_str() ); - } - ++i; - } - return i - beginIndex; -} - - -/* - * Get summary of results - */ -void CLogReportNode::report( NLMISC::CLog *targetLog, bool displayDetailsPerService ) -{ - uint nb1Sum=0, nb2Sum=0; - for ( std::vector::const_iterator it=_Children.begin(); it!=_Children.end(); ++it ) - { - CLogReportLeaf *pt = (*it); - - // Get distinct warnings - uint nb1 = pt->getNbDistinctLines(); - nb1Sum += nb1; - - // Get total warnings, info... but filter out lines with no header - uint sumTotalLinesNotUnknown = 0; - uint nbTotalLines [CLog::LOG_UNKNOWN]; - for ( uint i=CLog::LOG_ERROR; i!=CLog::LOG_UNKNOWN; ++i ) - { - nbTotalLines[i] = pt->getNbTotalLines( (CLog::TLogType)i ); - sumTotalLinesNotUnknown += nbTotalLines[i]; - } - if ( sumTotalLinesNotUnknown != 0 ) - { - targetLog->displayRawNL( "%s: \t%u distinct WRN, %u total WRN, %u INF, %u DBG, %u STT, %u AST, %u ERR, %u TOTAL", - pt->service().c_str(), nb1, nbTotalLines[CLog::LOG_WARNING], - nbTotalLines[CLog::LOG_INFO], nbTotalLines[CLog::LOG_DEBUG], - nbTotalLines[CLog::LOG_STAT], nbTotalLines[CLog::LOG_ASSERT], - nbTotalLines[CLog::LOG_ERROR], pt->getNbTotalLines( CLog::LOG_UNKNOWN ) ); - nb2Sum += nbTotalLines[CLog::LOG_WARNING]; - } - } - targetLog->displayRawNL( "=> %u distinct, %u total WRN (%u pages)", nb1Sum, nb2Sum, nb1Sum / NB_LINES_PER_PAGE + 1 ); - - if ( displayDetailsPerService ) - { - for ( std::vector::const_iterator it=_Children.begin(); it!=_Children.end(); ++it ) - { - (*it)->report( targetLog, true ); - } - } -} - - -/* - * Get partial results (pageNum>=1) - */ -void CLogReportNode::reportPage( uint pageNum, NLMISC::CLog *targetLog ) -{ - if ( _Children.empty() ) - { - targetLog->displayRawNL( "[END OF LOG]" ); - return; - } - - uint beginIndex = pageNum * NB_LINES_PER_PAGE; - uint lineCounter = 0, prevLineCounter; - for ( std::vector::const_iterator it=_Children.begin(); it!=_Children.end(); ++it ) - { - CLogReportLeaf *pt = (*it); - prevLineCounter = lineCounter; - lineCounter += pt->getNbDistinctLines(); - if ( lineCounter >= beginIndex ) - { - uint remainingLines = pageNum - (lineCounter - beginIndex ); - pt->reportPart( beginIndex - prevLineCounter, remainingLines, targetLog ); // - while ( remainingLines != 0 ) - { - ++it; - if ( it == _Children.end() ) - { - targetLog->displayRawNL( "[END OF LOG]" ); - return; - } - pt = (*it); - remainingLines -= pt->reportPart( 0, remainingLines, targetLog ); - } - return; - } - } -} +// NeLNS - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "log_report.h" +#include +#include +#include "nel/misc/common.h" +#include "nel/misc/displayer.h" +#include "nel/misc/file.h" +#include "nel/misc/path.h" +#include "nel/misc/variable.h" + +using namespace NLMISC; +using namespace std; + + +CVariable LogPath( "LogReport","LogPath", "Path of the log files", ".", 0, true ); + +const uint MAX_LOG_LINE_SIZE = 1024; +//nlctassert(MAX_LOG_LINE_SIZE>0); + +enum TLogLineHeader { LHDate, LHTime, LHType, LHThread, LHService, LHCodeFile, LHCodeLine, LHSeparator, LH_NB_FIELDS }; + + +/// +bool isLogFile( const std::string& filename ) +{ + uint len = (uint)filename.size(); + return (len >= 4 ) && (filename.substr( len-4 ) == ".log"); +} + +/// +inline bool isNumberChar( char c ) +{ + return (c >= '0') && (c <= '9'); +} + +/// +void sortLogFiles( vector& filenames ) +{ + uint i; + for ( i=0; i!=filenames.size(); ++i ) + { + // Ensure that a log file without number comes *after* the ones with a number + string name = string(filenames[i]); + string::size_type dotpos = name.find_last_of('.'); + if ( (dotpos!=string::npos) && (dotpos > 2) ) + { + if ( ! (isNumberChar(name[dotpos-1]) && isNumberChar(name[dotpos-2]) && isNumberChar(name[dotpos-3])) ) + { + name = name.substr( 0, dotpos ) + "ZZZ" + name.substr( dotpos ); + filenames[i] = name.c_str(); + } + } + } + sort( filenames.begin(), filenames.end() ); + for ( i=0; i!=filenames.size(); ++i ) + { + // Set the original names back + string name = filenames[i]; + string::size_type tokenpos = name.find( "ZZZ." ); + if ( tokenpos != string::npos ) + { + name = name.substr( 0, tokenpos ) + name.substr( tokenpos + 3 ); + filenames[i] = name.c_str(); + } + } +} + +void CMakeLogTask::setLogPath(const std::string & logPath) +{ + _LogPaths.resize( 1 ); + _LogPaths[0] = logPath; +} + +void CMakeLogTask::setLogPaths(const std::vector& logPaths) +{ + _LogPaths = logPaths; +} + +void CMakeLogTask::setLogPathToDefault() +{ + setLogPath( LogPath.get() ); +} + +/* + * + */ +CMakeLogTask::~CMakeLogTask() +{ + if ( _Thread ) // implies && _OutputLogReport + { + if ( ! _Complete ) + { + pleaseStop(); + _Thread->wait(); + } + clear(); + } +} + + +/* + * + */ +void CMakeLogTask::start() +{ + if ( _Thread ) + { + if ( _Complete ) + clear(); + else + return; + } + _Stopping = false; + _Complete = false; + _Thread = NLMISC::IThread::create( this ); + _OutputLogReport = new CLogReport(); + _Thread->start(); +} + + +/* + * + */ +void CMakeLogTask::clear() +{ + if (_Thread) + { + delete _Thread; + _Thread = NULL; + } + if (_OutputLogReport) + { + delete _OutputLogReport; + _OutputLogReport = NULL; + } +} + +/* + * + */ +void CMakeLogTask::terminateTask() +{ + if (!_Thread) // _Thread _implies _OutputLogReport + return; + + pleaseStop(); + _Thread->wait(); + + clear(); +} + +// +bool isOfLogDotLogFamily( const std::string& filename ) +{ + return ((filename == "log.log") || + ((filename.size() == 10) && + (filename.substr( 0, 3 ) == "log") && + isNumberChar(filename[3]) && isNumberChar(filename[4]) && isNumberChar(filename[5]) && + (filename.substr( 6, 4 ) == ".log")) ); +} + + +enum TVersionTargetMode { TTMAll, TTMMatchAllV, TTMMatchExactV, TTMMatchGreaterV, TTMMatchLowerV } targetMode; +const uint CurrentVersion = std::numeric_limits::max(); + +// Return true and logVersion, or false if not a log with version +bool getLogVersion( const std::string& filename, uint& logVersion ) +{ + uint len = (uint)filename.size(); + if ( (len > 4) && (filename.substr( len-4 ) == ".log") ) + { + if ( filename.substr(0, 3) == "log" ) + { + if ( (len == 7) || + ((len == 10) && (isNumberChar(filename[3]) && isNumberChar(filename[4]) && isNumberChar(filename[5]))) ) + { + logVersion = CurrentVersion; + return true; + } + } + else if ( filename[0] == 'v' ) + { + string::size_type p = filename.find( "_", 1 ); + if ( p != string::npos ) + { + if ( (len == p + 8) || + ((len == p + 11) && (isNumberChar(filename[p+4]) && isNumberChar(filename[p+5]) && isNumberChar(filename[p+6]))) ) + { + NLMISC::fromString( filename.substr( 1, p-1 ), logVersion ); + return true; + } + } + } + } + return false; +} + +// Assumes filename is .log file +bool matchLogTarget( const std::string& filename, TVersionTargetMode targetMode, uint targetVersion ) +{ + if ( targetMode == TTMAll ) + return true; + + uint version; + + // Get version or exclude non-standard log files + if ( ! getLogVersion( filename, version ) ) + return false; + + // Exclude non-matching version + switch ( targetMode ) + { + case TTMMatchExactV: + return (version == targetVersion); // break; + case TTMMatchGreaterV: + return (version >= targetVersion); // break; + case TTMMatchLowerV: + return (version <= targetVersion); // break; + default: // TTMMatchAllV + return true; + } +} + +/* + * + */ +void CMakeLogTask::run() +{ + // Parse log target + uint targetVersion = CurrentVersion; + uint lts = (uint)_LogTarget.size(); + if ( _LogTarget.empty() || (_LogTarget == "v") ) + { + targetMode = TTMMatchExactV; + } + else if ( _LogTarget == "v*" ) + { + targetMode = TTMMatchAllV; + } + else if ( _LogTarget == "*" ) + { + targetMode = TTMAll; + } + else if ( (lts > 1) && (_LogTarget[0] == 'v') ) + { + uint additionalChars = 1; + if ( _LogTarget[lts-1] == '+' ) + targetMode = TTMMatchGreaterV; + else if ( _LogTarget[lts-1] == '-' ) + targetMode = TTMMatchLowerV; + else + { + targetMode = TTMMatchExactV; + additionalChars = 0; + } + + NLMISC::fromString( _LogTarget.substr( 1, lts-additionalChars-1 ), targetVersion ); + } + else + { + nlwarning( "Invalid log target argument: %s", _LogTarget.c_str() ); + _Complete = true; + return; + } + + // Get log files and sort them + vector filenames; + vector filenamesOfPath; + for ( vector::const_iterator ilf=_LogPaths.begin(); ilf!=_LogPaths.end(); ++ilf ) + { + string path = (*ilf); + if ( (! path.empty()) && (path[path.size()-1]!='/') ) + path += "/"; + filenamesOfPath.clear(); + CPath::getPathContent( path, false, false, true, filenamesOfPath, NULL, true ); + vector::iterator ilf2 = partition( filenamesOfPath.begin(), filenamesOfPath.end(), isLogFile ); + filenamesOfPath.erase( ilf2, filenamesOfPath.end() ); + sortLogFiles( filenamesOfPath ); + filenames.insert( filenames.end(), filenamesOfPath.begin(), filenamesOfPath.end() ); + } + + // Analyse log files + _OutputLogReport->reset(); + uint nbLines = 0; + char line [MAX_LOG_LINE_SIZE]; + + uint nbSkippedFiles = 0; + for ( vector::const_iterator ilf=filenames.begin(); ilf!=filenames.end(); ++ilf ) + { + string shortname = CFile::getFilename( *ilf ); + + // Filter log files based on filename before opening them + if ( ! matchLogTarget( shortname, targetMode, targetVersion ) ) + { + ++nbSkippedFiles; + continue; + } + + nlinfo( "Processing %s (%u/%u)", (*ilf).c_str(), ilf-filenames.begin(), filenames.size() ); + CIFile logfile; + if ( logfile.open( *ilf, true ) ) + { + _OutputLogReport->setProgress( (uint)(ilf-filenames.begin()), (uint)filenames.size() ); + while ( ! logfile.eof() ) + { + logfile.getline( line, MAX_LOG_LINE_SIZE ); + line[MAX_LOG_LINE_SIZE-1] = '\0'; // force valid end of line + _OutputLogReport->pushLine( line ); + ++nbLines; + + if ( isStopping() ) + return; + } + } + } + nlinfo( "%u lines processed", nbLines ); + if ( nbSkippedFiles != 0 ) + nlinfo( "%u log files skipped, not matching target %s", nbSkippedFiles, _LogTarget.c_str() ); + _Complete = true; +} + + +/* + * Add a log line to the report tree + */ +void CLogReport::pushLine( const std::string& line, NLMISC::CLog::TLogType onlyType, bool countOtherTypes ) +{ + // Ignore session title + if ( (line.size() > 14) && (line.substr( 0, 14 ) == "Log Starting [") ) + return; + + // Decode standard log line + vector lineTokens; + explode( line, string(" "), lineTokens ); + + if ( lineTokens.size() < LH_NB_FIELDS ) + return; + + // Filter log type + if ( onlyType != CLog::LOG_UNKNOWN ) + { + if ( lineTokens[LHType] != IDisplayer::logTypeToString( onlyType ) ) + { + if ( countOtherTypes ) + storeLine( lineTokens, true ); + return; + } + } + + // Store + storeLine( lineTokens, false ); +} + + +/* + * + */ +void CLogReportNode::storeLine( const std::vector& lineTokens, bool mainCountOnly ) +{ + // Get service name from "[machine/]serviceName-serviceId" + string service = lineTokens[LHService]; + string::size_type p = service.find( '/' ); + if ( p != string::npos ) + service = service.substr( p+1 ); + p = service.find( '-' ); + if ( p != string::npos ) + service = service.substr( 0, p ); + + // Store to appropriate child + CLogReportLeaf *child = getChild( service ); + if ( ! child ) + child = addChild( service ); + child->storeLine( lineTokens, mainCountOnly ); +} + + +/* + * + */ +void CLogReportLeaf::storeLine( const std::vector& lineTokens, bool mainCountOnly ) +{ + if ( ! mainCountOnly ) + { + // Build key from "codeFile codeLine" + string key = lineTokens[LHCodeFile] + ":" + lineTokens[LHCodeLine]; + _LogLineInfo[key].addAnOccurence( lineTokens ); + } + ++_Counts[lineTokens[LHType]]; + ++_TotalLines; +} + + +/* + * + */ +void CLogLineInfo::addAnOccurence( const std::vector& lineTokens ) +{ + if ( NbOccurences == 0 ) + { + for ( uint i=LH_NB_FIELDS; ireport( targetLog, true ); + } + else + { + targetLog->displayNL( "Nothing found for service %s", service.c_str() ); + } +} + + +/* + * Get results for a service (all distinct lines, sorted by occurence) + */ +void CLogReportLeaf::report( NLMISC::CLog *targetLog, bool ) +{ + // Sort it + typedef multimap< uint, pair< string, const CLogLineInfo * >, std::greater > CSortedByOccurenceLogLineInfoMap; + CSortedByOccurenceLogLineInfoMap sortedByOccurence; + for ( CLogLineInfoMap::const_iterator it=_LogLineInfo.begin(); it!=_LogLineInfo.end(); ++it ) + { + const string &key = (*it).first; + const CLogLineInfo& info = (*it).second; + sortedByOccurence.insert( make_pair( info.NbOccurences, make_pair( key, &info ) ) ); + } + + // Display it + for ( CSortedByOccurenceLogLineInfoMap::const_iterator iso=sortedByOccurence.begin(); iso!=sortedByOccurence.end(); ++iso ) + { + const string &key = (*iso).second.first; + const CLogLineInfo& info = *((*iso).second.second); + targetLog->displayRawNL( "%s %6u %s : %s", _Service.c_str(), info.NbOccurences, key.c_str(), info.SampleLogText.c_str() ); + } +} + + +/* + * Return the number of lines displayed + */ +uint CLogReportLeaf::reportPart( uint beginIndex, uint maxNbLines, NLMISC::CLog *targetLog ) +{ + uint i = 0; + CLogLineInfoMap::const_iterator it; + for ( it=_LogLineInfo.begin(); it!=_LogLineInfo.end(); ++it ) + { + if ( i >= beginIndex ) + { + if ( i >= maxNbLines ) + return i - beginIndex; + + const string &key = (*it).first; + const CLogLineInfo& info = (*it).second; + targetLog->displayRawNL( "%s %6u %s : %s", _Service.c_str(), info.NbOccurences, key.c_str(), info.SampleLogText.c_str() ); + } + ++i; + } + return i - beginIndex; +} + + +/* + * Get summary of results + */ +void CLogReportNode::report( NLMISC::CLog *targetLog, bool displayDetailsPerService ) +{ + uint nb1Sum=0, nb2Sum=0; + for ( std::vector::const_iterator it=_Children.begin(); it!=_Children.end(); ++it ) + { + CLogReportLeaf *pt = (*it); + + // Get distinct warnings + uint nb1 = pt->getNbDistinctLines(); + nb1Sum += nb1; + + // Get total warnings, info... but filter out lines with no header + uint sumTotalLinesNotUnknown = 0; + uint nbTotalLines [CLog::LOG_UNKNOWN]; + for ( uint i=CLog::LOG_ERROR; i!=CLog::LOG_UNKNOWN; ++i ) + { + nbTotalLines[i] = pt->getNbTotalLines( (CLog::TLogType)i ); + sumTotalLinesNotUnknown += nbTotalLines[i]; + } + if ( sumTotalLinesNotUnknown != 0 ) + { + targetLog->displayRawNL( "%s: \t%u distinct WRN, %u total WRN, %u INF, %u DBG, %u STT, %u AST, %u ERR, %u TOTAL", + pt->service().c_str(), nb1, nbTotalLines[CLog::LOG_WARNING], + nbTotalLines[CLog::LOG_INFO], nbTotalLines[CLog::LOG_DEBUG], + nbTotalLines[CLog::LOG_STAT], nbTotalLines[CLog::LOG_ASSERT], + nbTotalLines[CLog::LOG_ERROR], pt->getNbTotalLines( CLog::LOG_UNKNOWN ) ); + nb2Sum += nbTotalLines[CLog::LOG_WARNING]; + } + } + targetLog->displayRawNL( "=> %u distinct, %u total WRN (%u pages)", nb1Sum, nb2Sum, nb1Sum / NB_LINES_PER_PAGE + 1 ); + + if ( displayDetailsPerService ) + { + for ( std::vector::const_iterator it=_Children.begin(); it!=_Children.end(); ++it ) + { + (*it)->report( targetLog, true ); + } + } +} + + +/* + * Get partial results (pageNum>=1) + */ +void CLogReportNode::reportPage( uint pageNum, NLMISC::CLog *targetLog ) +{ + if ( _Children.empty() ) + { + targetLog->displayRawNL( "[END OF LOG]" ); + return; + } + + uint beginIndex = pageNum * NB_LINES_PER_PAGE; + uint lineCounter = 0, prevLineCounter; + for ( std::vector::const_iterator it=_Children.begin(); it!=_Children.end(); ++it ) + { + CLogReportLeaf *pt = (*it); + prevLineCounter = lineCounter; + lineCounter += pt->getNbDistinctLines(); + if ( lineCounter >= beginIndex ) + { + uint remainingLines = pageNum - (lineCounter - beginIndex ); + pt->reportPart( beginIndex - prevLineCounter, remainingLines, targetLog ); // + while ( remainingLines != 0 ) + { + ++it; + if ( it == _Children.end() ) + { + targetLog->displayRawNL( "[END OF LOG]" ); + return; + } + pt = (*it); + remainingLines -= pt->reportPart( 0, remainingLines, targetLog ); + } + return; + } + } +} diff --git a/code/nelns/login_system/nel_launcher_qt/CMakeLists.txt b/code/nelns/login_system/nel_launcher_qt/CMakeLists.txt index bf2096b9a..1183a7b74 100644 --- a/code/nelns/login_system/nel_launcher_qt/CMakeLists.txt +++ b/code/nelns/login_system/nel_launcher_qt/CMakeLists.txt @@ -1,37 +1,40 @@ -INCLUDE_DIRECTORIES( ${CMAKE_BINARY_DIR}/login_system/nel_launcher_qt/ ) -INCLUDE_DIRECTORIES( ${CMAKE_SOURCE_DIR}/login_system/nel_launcher_qt/ ) -INCLUDE( ${QT_USE_FILE} ) - -FILE(GLOB NEL_LAUNCHER_SRC *.cpp) -SET(NEL_LAUNCHER_HDR nel_launcher_dlg.h) -SET(NEL_LAUNCHER_UIS nel_launcher_dlg.ui) -SET(NEL_LAUNCHER_RCS ) - -SET( QT_USE_QT3SUPPORT TRUE) -SET( QT_USE_QTXML TRUE) - -QT4_ADD_RESOURCES( NEL_LAUNCHER_RC_SRCS ${NEL_LAUNCHER_RCS} ) -QT4_WRAP_UI( NEL_LAUNCHER_UI_HDRS ${NEL_LAUNCHER_UIS} ) -QT4_WRAP_CPP( NEL_LAUNCHER_MOC_SRCS ${NEL_LAUNCHER_HDR}) - -ADD_EXECUTABLE(nel_launcher_qt WIN32 ${NEL_LAUNCHER_SRC} ${NEL_LAUNCHER_MOC_SRCS} ${NEL_LAUNCHER_RC_SRCS} ${NEL_LAUNCHER_UI_HDRS}) - -INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${QT_INCLUDES}) - -TARGET_LINK_LIBRARIES(nel_launcher_qt - ${LIBXML2_LIBRARIES} - ${QT_LIBRARIES} - nelmisc - nelnet) - -NL_DEFAULT_PROPS(nel_launcher_qt "NelNS, Launcher: NeL Launcher Qt") -NL_ADD_RUNTIME_FLAGS(nel_launcher_qt) - -ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} ${QT_DEFINITIONS}) - -INSTALL(TARGETS nel_launcher_qt RUNTIME DESTINATION bin COMPONENT launcher) -IF(WIN32) - INSTALL(FILES nel_launcher.cfg DESTINATION bin COMPONENT launcher) -ELSE(WIN32) - INSTALL(FILES nel_launcher.cfg DESTINATION etc/nel/nelns COMPONENT launcher) -ENDIF(WIN32) \ No newline at end of file +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBXML2_INCLUDE_DIR} + ${QT_INCLUDES}) + +INCLUDE( ${QT_USE_FILE} ) + +FILE(GLOB NEL_LAUNCHER_SRC *.cpp) +SET(NEL_LAUNCHER_HDR nel_launcher_dlg.h) +SET(NEL_LAUNCHER_UIS nel_launcher_dlg.ui) +SET(NEL_LAUNCHER_RCS ) + +SET( QT_USE_QT3SUPPORT TRUE) +SET( QT_USE_QTXML TRUE) + +QT4_ADD_RESOURCES( NEL_LAUNCHER_RC_SRCS ${NEL_LAUNCHER_RCS} ) +QT4_WRAP_UI( NEL_LAUNCHER_UI_HDRS ${NEL_LAUNCHER_UIS} ) +QT4_WRAP_CPP( NEL_LAUNCHER_MOC_SRCS ${NEL_LAUNCHER_HDR}) + +ADD_EXECUTABLE(nel_launcher_qt WIN32 ${NEL_LAUNCHER_SRC} ${NEL_LAUNCHER_MOC_SRCS} ${NEL_LAUNCHER_RC_SRCS} ${NEL_LAUNCHER_UI_HDRS}) + +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${QT_INCLUDES}) + +TARGET_LINK_LIBRARIES(nel_launcher_qt + ${LIBXML2_LIBRARIES} + ${QT_LIBRARIES} + nelmisc + nelnet) + +NL_DEFAULT_PROPS(nel_launcher_qt "NelNS, Launcher: NeL Launcher Qt") +NL_ADD_RUNTIME_FLAGS(nel_launcher_qt) + +ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} ${QT_DEFINITIONS}) + +INSTALL(TARGETS nel_launcher_qt RUNTIME DESTINATION bin COMPONENT launcher) +IF(WIN32) + INSTALL(FILES nel_launcher.cfg DESTINATION bin COMPONENT launcher) +ELSE(WIN32) + INSTALL(FILES nel_launcher.cfg DESTINATION etc/nel/nelns COMPONENT launcher) +ENDIF(WIN32) diff --git a/code/snowballs2/CMakeLists.txt b/code/snowballs2/CMakeLists.txt index 0f158fb88..0007259ee 100644 --- a/code/snowballs2/CMakeLists.txt +++ b/code/snowballs2/CMakeLists.txt @@ -1,177 +1,27 @@ -#-----------------------------------------------------------------------------^M -# Set CMake 2.6 Policies. -IF(COMMAND cmake_policy) - cmake_policy(SET CMP0003 NEW) -ENDIF(COMMAND cmake_policy) - - -#----------------------------------------------------------------------------- -# Set default config options -# -NL_SETUP_DEFAULT_OPTIONS() -NL_SETUP_PREFIX_PATHS() - -#----------------------------------------------------------------------------- -# Override default options -OPTION(BUILD_CLIENT "Build the Snowballs Client" ON) -OPTION(ENABLE_SOUND "Enable sound in the Snowballs Client" OFF) - -OPTION(BUILD_SERVER "Build the Snowballs Servers" ON) SET(SNOWBALLS_CONFIG_FILE "${NL_ETC_PREFIX}/snowballs" CACHE FILEPATH "Snowballs config file location") SET(SNOWBALLS_DATA_FILE "${NL_SHARE_PREFIX}/games/snowballs" CACHE FILEPATH "Snowballs data file location") SET(SNOWBALLS_LOG_FILE "${NL_LOG_PREFIX}/snowballs" CACHE FILEPATH "Snowballs log file location") -INSTALL(CODE "FILE(MAKE_DIRECTORY ${SNOWBALLS_LOG_FILE})") + +# Note: Not sure if this is still the best way to do this... +#INSTALL(CODE "FILE(MAKE_DIRECTORY ${SNOWBALLS_LOG_FILE})") #----------------------------------------------------------------------------- #Platform specifics -FIND_PACKAGE(Threads REQUIRED) -FIND_PACKAGE(LibXml2 REQUIRED) -FIND_PACKAGE(PNG REQUIRED) - -NL_SETUP_BUILD() - -FIND_PACKAGE(NeL COMPONENTS nelmisc nelgeorges nel3d nelnet nelpacs REQUIRED) -FIND_PACKAGE(NeLNS COMPONENTS naming_service login_service welcome_service nel_launcher_qt) - ### # An example of finding NeL 3D and CEGUI Renderer. ### -IF(BUILD_CLIENT) - FIND_PACKAGE(FreeType) - FIND_PACKAGE(Jpeg) - - IF(ENABLE_SOUND) - FIND_PACKAGE(NeL COMPONENTS nelsound) - ENDIF(ENABLE_SOUND) -ENDIF(BUILD_CLIENT) - -IF(WIN32) - INCLUDE(${CMAKE_ROOT}/Modules/Platform/Windows-cl.cmake) - SET(WINSOCK2_LIB ws2_32.lib) -ENDIF(WIN32) - -NL_SETUP_BUILD_FLAGS() - ADD_DEFINITIONS(-DSNOWBALLS_CONFIG="${SNOWBALLS_CONFIG_FILE}/" -DSNOWBALLS_STATE="${SNOWBALLS_LOG_FILE}/" -DSNOWBALLS_LOGS="${SNOWBALLS_LOG_FILE}/") -IF(BUILD_CLIENT) +IF(WITH_SNOWBALLS_CLIENT) ADD_SUBDIRECTORY(client) -ENDIF(BUILD_CLIENT) +ENDIF(WITH_SNOWBALLS_CLIENT) -IF(BUILD_SERVER) +IF(WITH_SNOWBALLS_SERVER) ADD_SUBDIRECTORY(server) -ENDIF(BUILD_SERVER) +ENDIF(WITH_SNOWBALLS_SERVER) -# packaging information -SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "NeL MMORPG Framework - Snowballs Demo") -SET(CPACK_PACKAGE_VENDOR "NeL") -SET(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README) -SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/COPYING) -SET(CPACK_PACKAGE_VERSION_MAJOR "${SB_VERSION_MAJOR}") -SET(CPACK_PACKAGE_VERSION_MINOR "${SB_VERSION_MINOR}") -SET(CPACK_PACKAGE_VERSION_PATCH "${SB_VERSION_PATCH}") -SET(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR};Snowballs;ALL;/") -SET(CPACK_PACKAGE_EXECUTABLES "snowballs-${SB_VERSION}" "snowballs") +IF(WITH_SNOWBALLS_PACKAGE) + INCLUDE(CMakePackaging.txt) +ENDIF(WITH_SNOWBALLS_PACKAGE) -# NSIS Specific Packing Setup -SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "Snowballs") -SET(CPACK_NSIS_MODIFY_PATH "ON") -SET(CPACK_NSIS_MUI_ICON ${CMAKE_SOURCE_DIR}/resources/nevraxpill.ico) -SET(CPACK_NSIS_MUI_UNIICON ${CMAKE_SOURCE_DIR}/resources/nevraxpill.ico) -SET(CPACK_PACKAGE_ICON ${CMAKE_SOURCE_DIR}/resources\\\\nel.bmp) -SET(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} Snowballs") -SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\dev.ryzom.com\\\\projects\\\\nel") -SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\dev.ryzom.com\\\\projects\\\\nel") -SET(CPACK_NSIS_CONTACT "matt.raykowski@gmail.com") - -## Source Packages -SET(CPACK_PACKAGE_FILE_NAME "snowballs-${SB_VERSION}") -SET(CPACK_SOURCE_PACKAGE_FILE_NAME "snowballs-${SB_VERSION}") -IF(WIN32) - SET(CPACK_GENERATOR "NSIS") - SET(CPACK_SOURCE_GENERATOR "ZIP") -ELSE(WIN32) - SET(CPACK_GENERATOR "TGZ") - SET(CPACK_SOURCE_GENERATOR "TGZ") -ENDIF(WIN32) -set(CPACK_SOURCE_IGNORE_FILES - "~$" - "\\\\.cvsignore$" - "^${CMAKE_SOURCE_DIR}.*/CVS/" - "^${CMAKE_SOURCE_DIR}.*/\\\\.svn/" - "^${CMAKE_SOURCE_DIR}/debian/" - "^${CMAKE_SOURCE_DIR}/old/") -IF(WIN32) - IF(NOT CMAKE_BUILD_TYPE STREQUAL "Release") - # Install "Debug" specific stuff here. - SET(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) - message(status "install debug libraries.") - ELSE(NOT CMAKE_BUILD_TYPE STREQUAL "Release") - # Install "Release" Specific stuff here. - SET(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) - ENDIF(NOT CMAKE_BUILD_TYPE STREQUAL "Release") - - IF(BUILD_CLIENT) - # Needed to find dependencies for nel_launcher_qt. - FIND_PACKAGE(Qt4 REQUIRED) - - # Install Qt dependencies for the launcher. - INCLUDE(${QT_USE_FILE}) - INSTALL(FILES - "${QT_LIBRARY_DIR}/QtGuid4.dll" - "${QT_LIBRARY_DIR}/QtXmld4.dll" - "${QT_LIBRARY_DIR}/QtCored4.dll" - DESTINATION bin) - - # Install the Qt Launcher. - INSTALL(FILES ${NELNS_NEL_LAUNCHER_BIN} DESTINATION bin COMPONENT snowballsclient) - - # Install the sound/video drivers - INSTALL(FILES ${NELDRVOPENGL_DLL} ${NELDRVDIRECT3D_DLL} DESTINATION bin COMPONENT snowballsclient) - ENDIF(BUILD_CLIENT) - - IF(BUILD_SERVER) - INSTALL(FILES - ${NELNS_NAMING_SERVICE_BIN} - ${NELNS_NAMING_SERVICE_CFG} - ${NELNS_LOGIN_SERVICE_BIN} - ${NELNS_LOGIN_SERVICE_CFG} - ${NELNS_WELCOME_SERVICE_BIN} - ${NELNS_WELCOME_SERVICE_CFG} - DESTINATION sbin - COMPONENT services) - ENDIF(BUILD_SERVER) - - INCLUDE(InstallRequiredSystemLibraries) -ENDIF(WIN32) - -INCLUDE(CPack) - -cpack_add_install_type(Full DISPLAY_NAME "Full Install") -cpack_add_component_group(Client - EXPANDED - DESCRPTION "Snowballs Client") -cpack_add_component_group(Server - EXPANDED - DESCRPTION "Snowballs Services") -cpack_add_component(snowballsclient - DISPLAY_NAME "Snowballs Client Runtime" - DESCRIPTION "Hello World Application" - GROUP Application - INSTALL_TYPES Full) -cpack_add_component(Unspecified - DISPLAY_NAME "Application Runtime Libraries" - DESCRIPTION "Hello World Application Libraries" - GROUP Client - INSTALL_TYPES Full) -cpack_add_component(snowballsdata - DISPLAY_NAME "Snowballs Application Data" - DESCRIPTION "Snowballs Application Data" - GROUP Client - INSTALL_TYPES Full) -cpack_add_component(services - DISPLAY_NAME "Snowballs Services" - DESCRIPTION "Snowballs Services" - GROUP Server - INSTALL_TYPES Full) \ No newline at end of file diff --git a/code/snowballs2/CMakePackaging.txt b/code/snowballs2/CMakePackaging.txt new file mode 100644 index 000000000..eb564ba3b --- /dev/null +++ b/code/snowballs2/CMakePackaging.txt @@ -0,0 +1,113 @@ +# packaging information +SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "NeL MMORPG Framework - Snowballs Demo") +SET(CPACK_PACKAGE_VENDOR "NeL") +SET(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_SOURCE_DIR}/README) +SET(CPACK_RESOURCE_FILE_LICENSE ${CMAKE_SOURCE_DIR}/COPYING) +SET(CPACK_PACKAGE_VERSION_MAJOR "${SB_VERSION_MAJOR}") +SET(CPACK_PACKAGE_VERSION_MINOR "${SB_VERSION_MINOR}") +SET(CPACK_PACKAGE_VERSION_PATCH "${SB_VERSION_PATCH}") +SET(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR};Snowballs;ALL;/") +SET(CPACK_PACKAGE_EXECUTABLES "snowballs-${SB_VERSION}" "snowballs") + +# NSIS Specific Packing Setup +SET(CPACK_PACKAGE_INSTALL_REGISTRY_KEY "Snowballs") +SET(CPACK_NSIS_MODIFY_PATH "ON") +SET(CPACK_NSIS_MUI_ICON ${CMAKE_SOURCE_DIR}/resources/nevraxpill.ico) +SET(CPACK_NSIS_MUI_UNIICON ${CMAKE_SOURCE_DIR}/resources/nevraxpill.ico) +SET(CPACK_PACKAGE_ICON ${CMAKE_SOURCE_DIR}/resources\\\\nel.bmp) +SET(CPACK_NSIS_DISPLAY_NAME "${CPACK_PACKAGE_INSTALL_DIRECTORY} Snowballs") +SET(CPACK_NSIS_HELP_LINK "http:\\\\\\\\dev.ryzom.com\\\\projects\\\\nel") +SET(CPACK_NSIS_URL_INFO_ABOUT "http:\\\\\\\\dev.ryzom.com\\\\projects\\\\nel") +SET(CPACK_NSIS_CONTACT "matt.raykowski@gmail.com") + +## Source Packages +SET(CPACK_PACKAGE_FILE_NAME "snowballs-${SB_VERSION}") +SET(CPACK_SOURCE_PACKAGE_FILE_NAME "snowballs-${SB_VERSION}") +IF(WIN32) + SET(CPACK_GENERATOR "NSIS") + SET(CPACK_SOURCE_GENERATOR "ZIP") +ELSE(WIN32) + SET(CPACK_GENERATOR "TGZ") + SET(CPACK_SOURCE_GENERATOR "TGZ") +ENDIF(WIN32) +set(CPACK_SOURCE_IGNORE_FILES + "~$" + "\\\\.cvsignore$" + "^${CMAKE_SOURCE_DIR}.*/CVS/" + "^${CMAKE_SOURCE_DIR}.*/\\\\.svn/" + "^${CMAKE_SOURCE_DIR}/debian/" + "^${CMAKE_SOURCE_DIR}/old/") +IF(WIN32) + IF(NOT CMAKE_BUILD_TYPE STREQUAL "Release") + # Install "Debug" specific stuff here. + SET(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) + message(status "install debug libraries.") + ELSE(NOT CMAKE_BUILD_TYPE STREQUAL "Release") + # Install "Release" Specific stuff here. + SET(CMAKE_INSTALL_DEBUG_LIBRARIES TRUE) + ENDIF(NOT CMAKE_BUILD_TYPE STREQUAL "Release") + + IF(BUILD_CLIENT) + # Needed to find dependencies for nel_launcher_qt. + FIND_PACKAGE(Qt4 REQUIRED) + + # Install Qt dependencies for the launcher. + INCLUDE(${QT_USE_FILE}) + INSTALL(FILES + "${QT_LIBRARY_DIR}/QtGuid4.dll" + "${QT_LIBRARY_DIR}/QtXmld4.dll" + "${QT_LIBRARY_DIR}/QtCored4.dll" + DESTINATION bin) + + # Install the Qt Launcher. + INSTALL(FILES ${NELNS_NEL_LAUNCHER_BIN} DESTINATION bin COMPONENT snowballsclient) + + # Install the sound/video drivers + INSTALL(FILES ${NELDRVOPENGL_DLL} ${NELDRVDIRECT3D_DLL} DESTINATION bin COMPONENT snowballsclient) + ENDIF(BUILD_CLIENT) + + IF(BUILD_SERVER) + INSTALL(FILES + ${NELNS_NAMING_SERVICE_BIN} + ${NELNS_NAMING_SERVICE_CFG} + ${NELNS_LOGIN_SERVICE_BIN} + ${NELNS_LOGIN_SERVICE_CFG} + ${NELNS_WELCOME_SERVICE_BIN} + ${NELNS_WELCOME_SERVICE_CFG} + DESTINATION sbin + COMPONENT services) + ENDIF(BUILD_SERVER) + + INCLUDE(InstallRequiredSystemLibraries) +ENDIF(WIN32) + +INCLUDE(CPack) + +cpack_add_install_type(Full DISPLAY_NAME "Full Install") +cpack_add_component_group(Client + EXPANDED + DESCRPTION "Snowballs Client") +cpack_add_component_group(Server + EXPANDED + DESCRPTION "Snowballs Services") +cpack_add_component(snowballsclient + DISPLAY_NAME "Snowballs Client Runtime" + DESCRIPTION "Hello World Application" + GROUP Application + INSTALL_TYPES Full) +cpack_add_component(Unspecified + DISPLAY_NAME "Application Runtime Libraries" + DESCRIPTION "Hello World Application Libraries" + GROUP Client + INSTALL_TYPES Full) +cpack_add_component(snowballsdata + DISPLAY_NAME "Snowballs Application Data" + DESCRIPTION "Snowballs Application Data" + GROUP Client + INSTALL_TYPES Full) +cpack_add_component(services + DISPLAY_NAME "Snowballs Services" + DESCRIPTION "Snowballs Services" + GROUP Server + INSTALL_TYPES Full) + From fa5b280a37231016cdb1be55f6399c628576b6a0 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 19 Aug 2011 14:54:34 +0200 Subject: [PATCH 088/215] Changed: #1323 Cmake with snowballs (patch provided by dfighter) --- code/snowballs2/client/src/snowballs_client.cpp | 4 ++++ code/snowballs2/server/chat/src/main.cpp | 3 +++ code/snowballs2/server/collision/src/collision_service.cpp | 4 ++++ code/snowballs2/server/frontend/src/CMakeLists.txt | 2 +- code/snowballs2/server/frontend/src/main.cpp | 4 ++++ code/snowballs2/server/position/src/main.cpp | 3 +++ 6 files changed, 19 insertions(+), 1 deletion(-) diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 3aa4d5540..79ccef7e6 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -70,6 +70,10 @@ #include "internationalization.h" #include "game_time.h" +#ifdef NL_OS_WINDOWS +#include +#endif + using namespace std; using namespace NLMISC; using namespace NL3D; diff --git a/code/snowballs2/server/chat/src/main.cpp b/code/snowballs2/server/chat/src/main.cpp index 9a1a3d057..37f58eb92 100644 --- a/code/snowballs2/server/chat/src/main.cpp +++ b/code/snowballs2/server/chat/src/main.cpp @@ -36,6 +36,9 @@ // We're using the NeL Service framework, and layer 5 #include +#ifdef NL_OS_WINDOWS +#include +#endif using namespace std; using namespace NLMISC; diff --git a/code/snowballs2/server/collision/src/collision_service.cpp b/code/snowballs2/server/collision/src/collision_service.cpp index fc48d02c3..ffde0c725 100644 --- a/code/snowballs2/server/collision/src/collision_service.cpp +++ b/code/snowballs2/server/collision/src/collision_service.cpp @@ -17,6 +17,10 @@ #include "collision_service.h" #include +#ifdef NL_OS_WINDOWS +#include +#endif + using namespace SBSERVICE; using namespace NLMISC; using namespace NLNET; diff --git a/code/snowballs2/server/frontend/src/CMakeLists.txt b/code/snowballs2/server/frontend/src/CMakeLists.txt index 086ecfcd1..d24c6743e 100644 --- a/code/snowballs2/server/frontend/src/CMakeLists.txt +++ b/code/snowballs2/server/frontend/src/CMakeLists.txt @@ -1,6 +1,6 @@ FILE(GLOB SRC *.cpp *.h) -ADD_EXECUTABLE(snowballs_frontend_service ${SRC}) +ADD_EXECUTABLE(snowballs_frontend_service WIN32 ${SRC}) INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) diff --git a/code/snowballs2/server/frontend/src/main.cpp b/code/snowballs2/server/frontend/src/main.cpp index ff036fe51..74ccdc50f 100644 --- a/code/snowballs2/server/frontend/src/main.cpp +++ b/code/snowballs2/server/frontend/src/main.cpp @@ -38,6 +38,10 @@ #include #include +#ifdef NL_OS_WINDOWS +#include +#endif + using namespace NLMISC; using namespace NLNET; using namespace std; diff --git a/code/snowballs2/server/position/src/main.cpp b/code/snowballs2/server/position/src/main.cpp index b908d1e73..44a94d560 100644 --- a/code/snowballs2/server/position/src/main.cpp +++ b/code/snowballs2/server/position/src/main.cpp @@ -41,6 +41,9 @@ #include "physics.h" +#ifdef NL_OS_WINDOWS +#include +#endif using namespace NLMISC; using namespace NLNET; From b5b21ba16958ded1ebec18ef658d539d7536adc6 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 19 Aug 2011 15:48:33 +0200 Subject: [PATCH 089/215] Changed: #825 Remove all warnings when compiling Ryzom --- code/snowballs2/client/src/snowballs_client.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/snowballs2/client/src/snowballs_client.cpp b/code/snowballs2/client/src/snowballs_client.cpp index 79ccef7e6..bde24ac83 100644 --- a/code/snowballs2/client/src/snowballs_client.cpp +++ b/code/snowballs2/client/src/snowballs_client.cpp @@ -606,7 +606,7 @@ void loopLogin() string LSHost(ConfigFile->getVar("LSHost").asString()); Login = ConfigFile->getVar("Login").asString(); string Password = ConfigFile->getVar("Password").asString(); - CHashKeyMD5 hk = getMD5((uint8 *)Password.c_str(), Password.size()); + CHashKeyMD5 hk = getMD5((uint8 *)Password.c_str(), (uint32)Password.size()); string CPassword = hk.toString(); nlinfo("The crypted password is %s", CPassword.c_str()); string Application = ConfigFile->getVar("ClientApplication").asString(); @@ -1077,8 +1077,8 @@ sint main(int argc, char **argv) // extract the 2 first param (argv[1] and argv[2]) it must be cookie and addr string cmd = cmdline; - int pos1 = cmd.find_first_not_of (' '); - int pos2; + string::size_type pos1 = cmd.find_first_not_of (' '); + string::size_type pos2; if (pos1 != string::npos) { pos2 = cmd.find (' ', pos1); From 45eed6724219ce64aaf8cf9149fe835421aa5fb6 Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 19 Aug 2011 16:58:18 +0200 Subject: [PATCH 090/215] Changed: #1177 VS 2010 does not work under CMake --- code/CMakeModules/FindExternal.cmake | 2 +- code/CMakeModules/FindWindowsSDK.cmake | 6 +++++ code/CMakeModules/nel.cmake | 32 ++++++++++++++++++++------ 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/code/CMakeModules/FindExternal.cmake b/code/CMakeModules/FindExternal.cmake index fae4af92d..1ca01bd99 100644 --- a/code/CMakeModules/FindExternal.cmake +++ b/code/CMakeModules/FindExternal.cmake @@ -41,7 +41,7 @@ IF(EXTERNAL_PATH) ELSE(TARGET_X64) SET(EXTERNAL_BINARY_PATH "${EXTERNAL_PATH}/bin") ENDIF(TARGET_X64) - + # Using 32 or 64 bits libraries IF(TARGET_X64) SET(EXTERNAL_LIBRARY_PATH "${EXTERNAL_PATH}/lib64") diff --git a/code/CMakeModules/FindWindowsSDK.cmake b/code/CMakeModules/FindWindowsSDK.cmake index 30e507f85..25903be14 100644 --- a/code/CMakeModules/FindWindowsSDK.cmake +++ b/code/CMakeModules/FindWindowsSDK.cmake @@ -33,6 +33,12 @@ FIND_PATH(WINSDK_INCLUDE_DIR Windows.h ${WINSDKCURRENT_DIR}/Include ) +FIND_PROGRAM(WINSDK_SIGNTOOL signtool + PATHS + ${WINSDK71_DIR}/Bin + ${WINSDKCURRENT_DIR}/Bin +) + IF(WINSDK_INCLUDE_DIR) SET(WINSDK_FOUND TRUE) ELSE(WINSDK_INCLUDE_DIR) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 14a78d721..1524ebf46 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -37,9 +37,7 @@ ENDMACRO(NL_TARGET_DRIVER) # Argument: ### MACRO(NL_DEFAULT_PROPS name label) - IF(NOT MSVC10) - SET_TARGET_PROPERTIES(${name} PROPERTIES PROJECT_LABEL ${label}) - ENDIF(NOT MSVC10) + SET_TARGET_PROPERTIES(${name} PROPERTIES PROJECT_LABEL ${label}) GET_TARGET_PROPERTY(type ${name} TYPE) IF(${type} STREQUAL SHARED_LIBRARY) # Set versions only if target is a shared library @@ -49,6 +47,15 @@ MACRO(NL_DEFAULT_PROPS name label) SET_TARGET_PROPERTIES(${name} PROPERTIES INSTALL_NAME_DIR ${NL_LIB_PREFIX}) ENDIF(NL_LIB_PREFIX) ENDIF(${type} STREQUAL SHARED_LIBRARY) + + IF(${type} STREQUAL EXECUTABLE AND WIN32) + SET_TARGET_PROPERTIES(${name} PROPERTIES + VERSION ${NL_VERSION} + SOVERSION ${NL_VERSION_MAJOR} + COMPILE_FLAGS "/GA" + LINK_FLAGS "/VERSION:${NL_VERSION}") + ENDIF(${type} STREQUAL EXECUTABLE AND WIN32) + IF(WITH_STLPORT AND WIN32) SET_TARGET_PROPERTIES(${name} PROPERTIES COMPILE_FLAGS "/X") ENDIF(WITH_STLPORT AND WIN32) @@ -308,25 +315,32 @@ MACRO(NL_SETUP_BUILD) ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8) # ADD_DEFINITIONS(-DHAVE_IA64) # ENDIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86") - + IF(WIN32) IF(MSVC10) # /Ox is working with VC++ 2010, but custom optimizations don't exist SET(SPEED_OPTIMIZATIONS "/Ox /GF /GS-") # without inlining it's unusable, use custom optimizations again SET(MIN_OPTIMIZATIONS "/Od /Ob1") - ELSE(MSVC10) + ELSEIF(MSVC90) # don't use a /O[012x] flag if you want custom optimizations SET(SPEED_OPTIMIZATIONS "/Ob2 /Oi /Ot /Oy /GT /GF /GS-") # without inlining it's unusable, use custom optimizations again SET(MIN_OPTIMIZATIONS "/Ob1") + ELSEIF(MSVC80) + # don't use a /O[012x] flag if you want custom optimizations + SET(SPEED_OPTIMIZATIONS "/Ox /GF /GS-") + # without inlining it's unusable, use custom optimizations again + SET(MIN_OPTIMIZATIONS "/Od /Ob1") + ELSE(MSVC10) + MESSAGE(FATAL_ERROR "Can't determine compiler version ${MSVC_VERSION}") ENDIF(MSVC10) - SET(PLATFORM_CFLAGS "/D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DWIN32 /D_WINDOWS /W3 /Zi /Zm1000 /MP /Gy-") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DWIN32 /D_WINDOWS /W3 /Zi /Zm1000 /MP /Gy-") # Common link flags SET(PLATFORM_LINKFLAGS "-DEBUG") - + IF(TARGET_X64) # Fix a bug with Intellisense SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} /D_WIN64") @@ -504,6 +518,10 @@ MACRO(SETUP_EXTERNAL) INCLUDE(${CMAKE_ROOT}/Modules/Platform/Windows-cl.cmake) IF(MSVC10) + IF(NOT MSVC10_REDIST_DIR) + # If you have VC++ 2010 Express, put x64/Microsoft.VC100.CRT/*.dll in ${EXTERNAL_PATH}/redist + SET(MSVC10_REDIST_DIR "${EXTERNAL_PATH}/redist") + ENDIF(NOT MSVC10_REDIST_DIR) GET_FILENAME_COMPONENT(VC_ROOT_DIR "[HKEY_CURRENT_USER\\Software\\Microsoft\\VisualStudio\\10.0_Config;InstallDir]" ABSOLUTE) # VC_ROOT_DIR is set to "registry" when a key is not found IF(VC_ROOT_DIR MATCHES "registry") From c5445ad8979bed191506449ddc944261a097f5f6 Mon Sep 17 00:00:00 2001 From: sfb Date: Sat, 20 Aug 2011 11:54:55 -0500 Subject: [PATCH 091/215] Changed: #1323 Applied Molator's QtMain patch. --- code/nelns/login_system/nel_launcher_qt/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/code/nelns/login_system/nel_launcher_qt/CMakeLists.txt b/code/nelns/login_system/nel_launcher_qt/CMakeLists.txt index 1183a7b74..3bd503eef 100644 --- a/code/nelns/login_system/nel_launcher_qt/CMakeLists.txt +++ b/code/nelns/login_system/nel_launcher_qt/CMakeLists.txt @@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR} ${QT_INCLUDES}) TARGET_LINK_LIBRARIES(nel_launcher_qt ${LIBXML2_LIBRARIES} ${QT_LIBRARIES} + ${QT_QTMAIN_LIBRARY} nelmisc nelnet) From 2a9293b72ac3807468d1deae17a87848677d963f Mon Sep 17 00:00:00 2001 From: cemycc Date: Mon, 22 Aug 2011 14:42:51 +0300 Subject: [PATCH 092/215] Changed: #1307 Added documentation and minor fixes --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/editor_worksheet.cpp | 20 ++-- .../translation_manager/ftp_selection.cpp | 6 +- .../translation_manager/source_selection.cpp | 1 + .../translation_manager_main_window.cpp | 107 ++++++++++-------- 4 files changed, 75 insertions(+), 59 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index c957e6506..c8a101e0f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -59,7 +59,7 @@ void CEditorWorksheet::open(QString filename) } else { QTableWidgetItem *col = new QTableWidgetItem(); ucstring col_name = wk_file.getData(0, i); - col->setText(tr(col_name.toString().c_str())); + col->setText(QString(col_name.toString().c_str())); if(hasHashValue) { table_editor->setHorizontalHeaderItem(i - 1, col); @@ -241,9 +241,9 @@ void CEditorWorksheet::extractBotNames(list filters, string level_design if(search_results.size() == 0) { QList records; - records.push_back(tr(it->first.c_str())); - records.push_back(tr(it->first.c_str())); - records.push_back(tr(it->second.SheetName.c_str())); + records.push_back(QString(it->first.c_str())); + records.push_back(QString(it->first.c_str())); + records.push_back(QString(it->second.SheetName.c_str())); insertTableRecords(records, new_items); if(!modified) modified = true; } @@ -261,8 +261,8 @@ void CEditorWorksheet::extractBotNames(list filters, string level_design if(search_results.size() == 0) { QList records; - records.push_back(tr((*it).c_str())); - records.push_back(tr(gnName.c_str())); + records.push_back(QString((*it).c_str())); + records.push_back(QString(gnName.c_str())); records.push_back(" "); insertTableRecords(records, new_items); if(!modified) modified = true; @@ -318,7 +318,7 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis for(i = 0; i < allWords.size(); i++) { string keyName = allWords[i]; - QList search_results = table_editor->findItems(tr(keyName.c_str()), Qt::MatchExactly); + QList search_results = table_editor->findItems(QString(keyName.c_str()), Qt::MatchExactly); if(search_results.size() == 0) { int knPos = 0, nPos = 0; @@ -332,8 +332,8 @@ void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordLis } QList records; - records.push_back(tr(keyName.c_str())); - records.push_back(QString("") + tr(keyName.c_str())); + records.push_back(QString(keyName.c_str())); + records.push_back(QString("") + QString(keyName.c_str())); insertTableRecords(records, new_items); if(!modified) modified = true; } @@ -412,7 +412,7 @@ void CEditorWorksheet::mergeWorksheetFile(QString filename) { // search with the first column ucstring rowId = wk_file.getData(i,colIndex); - QList search_results = table_editor->findItems(tr(rowId.toString().c_str()), Qt::MatchExactly); + QList search_results = table_editor->findItems(QString(rowId.toString().c_str()), Qt::MatchExactly); if(search_results.size() == 0) { const int lastRow = table_editor->rowCount(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp index b3f637b80..41a8072e2 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp @@ -27,6 +27,7 @@ namespace TranslationManager status = false; } + // Connection with the FTP Server. We retrieve the file list. void CFtpSelection::ConnectButtonClicked() { conn = new QFtp(this); @@ -51,6 +52,7 @@ namespace TranslationManager } } + // Get the user action. void CFtpSelection::FtpCommandFinished(int, bool error) { #ifndef QT_NO_CURSOR @@ -98,7 +100,7 @@ namespace TranslationManager } } } - + // Make the file list with directories and files void CFtpSelection::AddToList(const QUrlInfo &urlInfo) { QTreeWidgetItem *item = new QTreeWidgetItem; @@ -138,6 +140,7 @@ namespace TranslationManager _ui.doneButton->setEnabled(true); } + // Exit from a directory void CFtpSelection::cdToParent() { #ifndef QT_NO_CURSOR @@ -155,6 +158,7 @@ namespace TranslationManager conn->list(); } + // Done action void CFtpSelection::DoneButtonClicked() { QString fileName = _ui.fileList->currentItem()->text(0); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp index 18c49f3e3..b4733c7b9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp @@ -17,6 +17,7 @@ CSourceDialog::CSourceDialog(QWidget *parent): QDialog(parent) _ui.listWidget->setSortingEnabled(false); } +// Insert options in the source dialog. Options like: from FTP Server, from Local directory etc. void CSourceDialog::setSourceOptions(map options) { map::iterator it; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index ee509c8ad..8f9a213bf 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -51,8 +51,7 @@ CMainWindow::CMainWindow(QWidget *parent) { _ui.setupUi(this); - _ui.mdiArea->closeAllSubWindows(); - connect(_ui.mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow*)),this, SLOT(activeSubWindowChanged())); + _ui.mdiArea->closeAllSubWindows(); windowMapper = new QSignalMapper(this); connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); @@ -66,6 +65,7 @@ CMainWindow::CMainWindow(QWidget *parent) } +// Functions that will insert the plugin buttons void CMainWindow::createToolbar() { // File menu @@ -96,27 +96,27 @@ void CMainWindow::createToolbar() QAction *extractItemWordsAct = wordsExtractionMenu->addAction("&Extract item words..."); extractItemWordsAct->setStatusTip(tr("Extract item words")); connect(extractItemWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractItemWordsAct, tr(Constants::WK_ITEM)); + wordsExtractionMapper->setMapping(extractItemWordsAct, QString(Constants::WK_ITEM)); // extract creature words QAction *extractCreatureWordsAct = wordsExtractionMenu->addAction("&Extract creature words..."); extractCreatureWordsAct->setStatusTip(tr("Extract creature words")); connect(extractCreatureWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractCreatureWordsAct, tr(Constants::WK_CREATURE)); + wordsExtractionMapper->setMapping(extractCreatureWordsAct, QString(Constants::WK_CREATURE)); // extract sbrick words QAction *extractSbrickWordsAct = wordsExtractionMenu->addAction("&Extract sbrick words..."); extractSbrickWordsAct->setStatusTip(tr("Extract sbrick words")); connect(extractSbrickWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractSbrickWordsAct, tr(Constants::WK_SBRICK)); + wordsExtractionMapper->setMapping(extractSbrickWordsAct, QString(Constants::WK_SBRICK)); // extract sphrase words QAction *extractSphraseWordsAct = wordsExtractionMenu->addAction("&Extract sphrase words..."); extractSphraseWordsAct->setStatusTip(tr("Extract sphrase words")); connect(extractSphraseWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractSphraseWordsAct, tr(Constants::WK_SPHRASE)); + wordsExtractionMapper->setMapping(extractSphraseWordsAct, QString(Constants::WK_SPHRASE)); // extract place and region names QAction *extractPlaceNamesAct = wordsExtractionMenu->addAction("&Extract place names..."); extractPlaceNamesAct->setStatusTip(tr("Extract place names from primitives")); connect(extractPlaceNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractPlaceNamesAct, tr(Constants::WK_PLACE)); + wordsExtractionMapper->setMapping(extractPlaceNamesAct, QString(Constants::WK_PLACE)); // Merge options // ----------------------------- QAction *mergeSingleFileAct = wordsExtractionMenu->addAction("&Merge worksheet file..."); @@ -140,24 +140,23 @@ void CMainWindow::createToolbar() _ui.toolBar->addAction(redoAction); } +// Update the toolbar if the editor is worksheet void CMainWindow::updateToolbar(QMdiSubWindow *window) { if(_ui.mdiArea->subWindowList().size() > 0) if(QString(window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor { - //setContextMenuPolicy(Qt::ActionsContextMenu); QAction *insertRowAct = new QAction(tr("Insert new row"), this); connect(insertRowAct, SIGNAL(triggered()), window, SLOT(insertRow())); - //addAction(insertRowAct); windowMenu->addAction(insertRowAct); QAction *deleteRowAct = new QAction(tr("Delete row"), this); connect(deleteRowAct, SIGNAL(triggered()), window, SLOT(deleteRow())); - //addAction(deleteRowAct); windowMenu->addAction(deleteRowAct); } } +// Set the active subwindow void CMainWindow::setActiveSubWindow(QWidget* window) { if (!window) @@ -168,13 +167,11 @@ void CMainWindow::setActiveSubWindow(QWidget* window) _ui.mdiArea->setActiveSubWindow(cwindow); } -void CMainWindow::activeSubWindowChanged() -{ - -} - +// Functions for updating the windows list void CMainWindow::updateWindowsList() { + if(_ui.mdiArea->activeSubWindow()) + { windowMenu->clear(); QMdiSubWindow *current_window = _ui.mdiArea->activeSubWindow(); QList subWindows = _ui.mdiArea->subWindowList(); @@ -186,9 +183,9 @@ void CMainWindow::updateWindowsList() QString window_file = QFileInfo(subWindows.at(i)->windowFilePath()).fileName(); QString action_text; if (i < 9) { - action_text = tr("&%1 %2").arg(i + 1).arg(window_file); + action_text = QString("&%1 %2").arg(i + 1).arg(window_file); } else { - action_text = tr("%1 %2").arg(i + 1).arg(window_file); + action_text = QString("%1 %2").arg(i + 1).arg(window_file); } QAction *action = new QAction(action_text, this); action->setCheckable(true); @@ -196,9 +193,12 @@ void CMainWindow::updateWindowsList() connect(action, SIGNAL(triggered()), windowMapper, SLOT(map())); windowMenu->addAction(action); windowMapper->setMapping(action, subWindows.at(i)); - } + } + + } } +// Open signal void CMainWindow::open() { QSettings *settings = Core::ICore::instance()->settings(); @@ -243,6 +243,7 @@ void CMainWindow::open() } +// Open a work file. You can set the directory for work file in the settings dialog void CMainWindow::openWorkFile(QString file) { QFileInfo* file_path = new QFileInfo(QString("%1/%2").arg(work_path).arg(file)); @@ -262,6 +263,7 @@ void CMainWindow::openWorkFile(QString file) } +// Save signal void CMainWindow::save() { if(_ui.mdiArea->subWindowList().size() > 0) @@ -277,6 +279,7 @@ void CMainWindow::save() } } +// Save as signal void CMainWindow::saveAs() { QString file_name; @@ -298,6 +301,7 @@ void CMainWindow::saveAs() } } +// This function is needed by extraction. void CMainWindow::initializeSettings(bool georges = false) { if(georges == true && initialize_settings["georges"] == false) @@ -327,6 +331,7 @@ void CMainWindow::initializeSettings(bool georges = false) } +// Extracting words void CMainWindow::extractWords(QString typeq) { if(verifySettings() == true) @@ -396,6 +401,7 @@ void CMainWindow::extractWords(QString typeq) } +// Extract bot names from primitives void CMainWindow::extractBotNames() { if(verifySettings() == true) @@ -425,6 +431,7 @@ void CMainWindow::extractBotNames() } } +// Merge the content for 2 worksheet files void CMainWindow::mergeSingleFile() { CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); @@ -485,7 +492,7 @@ void CMainWindow::mergeSingleFile() current_window->mergeWorksheetFile(file_name); } else { QErrorMessage error; - error.showMessage(QString("The file: %1 has different columns from the current file in editor.").arg(file_name)); + error.showMessage(tr("The file: %1 has different columns from the current file in editor.").arg(file_name)); error.exec(); } if(dialog->selected_item == ftp_item) @@ -493,13 +500,14 @@ void CMainWindow::mergeSingleFile() if(!ftp_dialog->file->remove()) { QErrorMessage error; - error.showMessage(QString("Please remove the file from ftp server manually. The file is located on the same directory with OVQT application.")); + error.showMessage(tr("Please remove the file from ftp server manually. The file is located on the same directory with OVQT application.")); error.exec(); } } } +// Read the settings from QSettings void CMainWindow::readSettings() { QSettings *settings = Core::ICore::instance()->settings(); @@ -517,6 +525,7 @@ void CMainWindow::readSettings() settings->endGroup(); } +// Verify the settings bool CMainWindow::verifySettings() { bool count_errors = false; @@ -533,6 +542,36 @@ bool CMainWindow::verifySettings() } +bool CCoreListener::closeMainWindow() const +{ + bool okToClose = true; + Q_FOREACH(QMdiSubWindow *subWindow, m_MainWindow->_ui.mdiArea->subWindowList()) + { + CEditor *currentEditor = qobject_cast(subWindow); + if(subWindow->isWindowModified()) + { + QMessageBox msgBox; + msgBox.setText(tr("The document has been modified ( %1 ).").arg(currentEditor->windowFilePath())); + msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + if(ret == QMessageBox::Save) + { + currentEditor->save(); + } + else if(ret == QMessageBox::Cancel) + { + okToClose = false; + break; + } + } + } + return okToClose; +} + + +// Helper functions CEditor *CMainWindow::getEditorByWindowFilePath(const QString &fileName) { Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) @@ -604,34 +643,6 @@ bool CMainWindow::isPhraseEditor(QString filename) } } -bool CCoreListener::closeMainWindow() const -{ - bool okToClose = true; - Q_FOREACH(QMdiSubWindow *subWindow, m_MainWindow->_ui.mdiArea->subWindowList()) - { - CEditor *currentEditor = qobject_cast(subWindow); - if(subWindow->isWindowModified()) - { - QMessageBox msgBox; - msgBox.setText(tr("The document has been modified ( %1 ).").arg(currentEditor->windowFilePath())); - msgBox.setInformativeText("Do you want to save your changes?"); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - int ret = msgBox.exec(); - if(ret == QMessageBox::Save) - { - currentEditor->save(); - } - else if(ret == QMessageBox::Cancel) - { - okToClose = false; - break; - } - } - } - return okToClose; -} - } /* namespace Plugin */ From 16b3e28b7a2bae6a9c961f45183c87c1511ac59f Mon Sep 17 00:00:00 2001 From: aquiles Date: Thu, 25 Aug 2011 23:25:33 +0200 Subject: [PATCH 093/215] Changed: #1306 added basic editing possibillities for normal and parent values (no default values and no saving of changed values yet); added possibillity to expand/collapse whole tree by header click --- .../src/plugins/georges_editor/CMakeLists.txt | 3 +- .../georges_editor/expandable_headerview.cpp | 143 +++++++++ .../georges_editor/expandable_headerview.h | 49 +++ .../plugins/georges_editor/formdelegate.cpp | 278 ++++++++++++++++++ .../src/plugins/georges_editor/formdelegate.h | 41 +++ .../georges_editor/georges_editor_plugin.cpp | 2 +- .../georges_treeview_dialog.cpp | 30 +- .../georges_editor/georges_treeview_dialog.h | 3 + .../georges_editor/georgesform_model.cpp | 72 +++-- .../georges_editor/georgesform_model.h | 5 +- .../georgesform_proxy_model.cpp | 17 +- 11 files changed, 595 insertions(+), 48 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/expandable_headerview.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/expandable_headerview.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formdelegate.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formdelegate.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/CMakeLists.txt index 7c668ec8a..b29f32152 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/CMakeLists.txt @@ -12,7 +12,8 @@ SET(OVQT_PLUG_GEORGES_EDITOR_HDR georges_editor_plugin.h georges_editor_form.h georges_dirtree_dialog.h georges_filesystem_model.h - georges_treeview_dialog.h) + georges_treeview_dialog.h + expandable_headerview.h) SET(OVQT_PLUG_GEORGES_EDITOR_UIS georges_editor_form.ui georges_dirtree_form.ui diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/expandable_headerview.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/expandable_headerview.cpp new file mode 100644 index 000000000..7ae482824 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/expandable_headerview.cpp @@ -0,0 +1,143 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Project includes +#include "expandable_headerview.h" + +// Qt includes +#include +#include +#include + +namespace Plugin +{ + + ExpandableHeaderView::ExpandableHeaderView(Qt::Orientation orientation, QWidget * parent) + : QHeaderView(orientation, parent), + m_expanded(true), + m_inDecoration(false) + { + } + + void ExpandableHeaderView::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const + { + painter->save(); + QHeaderView::paintSection(painter, rect, logicalIndex); + painter->restore(); + + if (logicalIndex == 0) + { + QRect sectionRect = this->orientation() == Qt::Horizontal ? + QRect(this->sectionPosition(logicalIndex), 0, + this->sectionSize(logicalIndex), this->height()): + QRect(0, this->sectionPosition(logicalIndex), + this->width(), this->sectionSize(logicalIndex)); + + QStyleOptionHeader opt; + initStyleOption(&opt); + opt.iconAlignment = Qt::AlignVCenter; + + QVariant variant = this->model()->headerData(logicalIndex, this->orientation(), + Qt::DecorationRole); + opt.icon = qvariant_cast(variant); + if (opt.icon.isNull()) + { + opt.icon = qvariant_cast(variant); + } + QRect headerLabelRect = this->style()->subElementRect(QStyle::SE_HeaderLabel, &opt, this); + + QPixmap pixmap + = opt.icon.pixmap(this->style()->pixelMetric(QStyle::PM_SmallIconSize), + (opt.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled); + QRect aligned = this->style()->alignedRect(opt.direction, QFlag(opt.iconAlignment), + pixmap.size(), headerLabelRect); + QRect inter = aligned.intersected(headerLabelRect); + + QStyleOption option; + option.rect = QRect(inter.x()-2,inter.y(),inter.width(),inter.height()); + if (m_expanded) + option.state = QStyle::State_Children | QStyle::State_Open; + else + option.state = QStyle::State_Children; + if (m_inDecoration) + option.state |= QStyle::State_MouseOver; + QApplication::style()->drawPrimitive(QStyle::PE_IndicatorBranch, &option, painter); + } + } + + void ExpandableHeaderView::mousePressEvent(QMouseEvent *e) + { + int section = logicalIndexAt(e->x()); + + if (section == 0 && m_inDecoration) { + if (m_expanded) + m_expanded = false; + else + m_expanded = true; + this->QHeaderView::mousePressEvent(e); + Q_EMIT headerClicked(section); + } + } + + void ExpandableHeaderView::mouseMoveEvent(QMouseEvent *e) + { + int section = this->logicalIndexAt(e->x()); + + if (section != 0) + return; + + bool tmp = m_inDecoration; + if (isPointInDecoration(section, e->pos())) + m_inDecoration = true; + else + m_inDecoration = false; + + if (m_inDecoration != tmp) + updateSection(0); + } + + bool ExpandableHeaderView::isPointInDecoration(int section, QPoint pos)const + { + QRect sectionRect = this->orientation() == Qt::Horizontal ? + QRect(this->sectionPosition(section), 0, + this->sectionSize(section), this->height()): + QRect(0, this->sectionPosition(section), + this->width(), this->sectionSize(section)); + QStyleOptionHeader opt; + this->initStyleOption(&opt); + opt.iconAlignment = Qt::AlignVCenter; + QVariant variant = this->model()->headerData(section, this->orientation(), + Qt::DecorationRole); + opt.icon = qvariant_cast(variant); + if (opt.icon.isNull()) + { + opt.icon = qvariant_cast(variant); + } + QRect headerLabelRect = this->style()->subElementRect(QStyle::SE_HeaderLabel, &opt, this); + // from qcommonstyle.cpp + if (opt.icon.isNull()) + { + return false; + } + QPixmap pixmap + = opt.icon.pixmap(this->style()->pixelMetric(QStyle::PM_SmallIconSize), + (opt.state & QStyle::State_Enabled) ? QIcon::Normal : QIcon::Disabled); + QRect aligned = this->style()->alignedRect(opt.direction, QFlag(opt.iconAlignment), + pixmap.size(), headerLabelRect); + QRect inter = aligned.intersected(headerLabelRect); + return inter.contains(pos); + } +} diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/expandable_headerview.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/expandable_headerview.h new file mode 100644 index 000000000..596bf5ba1 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/expandable_headerview.h @@ -0,0 +1,49 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef EXPANDABLE_HEADERVIEW_H +#define EXPANDABLE_HEADERVIEW_H + +// Qt includes +#include + +namespace Plugin +{ + class ExpandableHeaderView : public QHeaderView + { + Q_OBJECT + public: + ExpandableHeaderView(Qt::Orientation orientation, QWidget * parent = 0); + + bool* expanded() { return &m_expanded; } + + protected: + void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; + bool isPointInDecoration(int section, QPoint pos)const; + void mousePressEvent(QMouseEvent *event); + void mouseMoveEvent(QMouseEvent *event); + + private: + bool m_expanded; + bool m_inDecoration; + +Q_SIGNALS: + void headerClicked(int); + }; + +} /* namespace NLQT */ + +#endif // EXPANDABLE_HEADERVIEW_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formdelegate.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formdelegate.cpp new file mode 100644 index 000000000..4d04bc6b5 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formdelegate.cpp @@ -0,0 +1,278 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#include "formdelegate.h" + +// NeL includes +#include +#include +#include + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Project includes +#include "georgesform_model.h" +#include "georgesform_proxy_model.h" +#include "formitem.h" + +namespace Plugin +{ + + FormDelegate::FormDelegate(QObject *parent) + : QStyledItemDelegate(parent) + { + } + + QWidget *FormDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem & option , + const QModelIndex &index) const + { + const CGeorgesFormProxyModel * mp = dynamic_cast(index.model()); + const CGeorgesFormModel * m = dynamic_cast(mp->sourceModel()); + CFormItem *item = static_cast(mp->mapToSource(index).internalPointer()); + QString value = item->data(1).toString(); + + if (value.isEmpty() || !mp || !m) + return 0; + + CFormItem* curItem = m->getItem(mp->mapToSource(index)); + NLGEORGES::UFormElm *curElm = curItem->getFormElm(); + if (!curElm) { + // TODO: create new Element + return 0; + } + const NLGEORGES::UType *type = curElm->getType(); + if(type) + { + int numDefinitions = type->getNumDefinition(); + + if (numDefinitions) + { + std::string l, v; + QString label,value; + + QComboBox *editor = new QComboBox(parent); + for (int i = 0; i < numDefinitions; i++) + { + type->getDefinition(i,l,v); + label = l.c_str(); + value = v.c_str(); + editor->addItem(label); + } + return editor; + } + else + { + switch (type->getType()) + { + case NLGEORGES::UType::UnsignedInt: + case NLGEORGES::UType::SignedInt: + { + QSpinBox *editor = new QSpinBox(parent); + + //QString min = QString(type->getMin().c_str()); + //QString max = QString(type->getMax().c_str()); + //QString inc = QString(type->getIncrement().c_str()); + //nldebug(QString("min %1 max %2 inc %3").arg(min).arg(max).arg(inc).toStdString().c_str()); + + // TODO: use saved min/max values + editor->setMinimum(-99999); + editor->setMaximum(99999); + editor->setSingleStep(1); + return editor; + } + case NLGEORGES::UType::Double: + { + QDoubleSpinBox *editor = new QDoubleSpinBox(parent); + + //QString min = QString(type->getMin().c_str()); + //QString max = QString(type->getMax().c_str()); + //QString inc = QString(type->getIncrement().c_str()); + //nldebug(QString("min %1 max %2 inc %3").arg(min).arg(max).arg(inc).toStdString().c_str()); + + // TODO: use saved min/max values + editor->setMinimum(-99999); + editor->setMaximum(99999); + editor->setSingleStep(0.1); + editor->setDecimals(1); + return editor; + } + case NLGEORGES::UType::Color: + { + return new QColorDialog(); + } + default: // UType::String + { + QLineEdit *editor = new QLineEdit(parent); + return editor; + } + } + } + } + return 0; + } + + void FormDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const + { + const CGeorgesFormProxyModel * mp = dynamic_cast(index.model()); + const CGeorgesFormModel * m = dynamic_cast(mp->sourceModel()); + + const NLGEORGES::UType *type = m->getItem(mp->mapToSource(index))->getFormElm()->getType(); + int numDefinitions = type->getNumDefinition(); + QString value = index.model()->data(index, Qt::DisplayRole).toString(); + + if (numDefinitions) + { + QComboBox *cb = static_cast(editor); + cb->setCurrentIndex(cb->findText(value)); + } + else + { + switch (type->getType()) + { + case NLGEORGES::UType::UnsignedInt: + case NLGEORGES::UType::SignedInt: + { + QSpinBox *spinBox = static_cast(editor); + spinBox->setValue((int)value.toDouble()); + break; + } + case NLGEORGES::UType::Double: + { + QDoubleSpinBox *spinBox = static_cast(editor); + spinBox->setValue(value.toDouble()); + break; + } + case NLGEORGES::UType::Color: + { + break; + } + default: + { + QLineEdit *textEdit = static_cast(editor); + textEdit->setText(value); + break; + } + } + } + } + + void FormDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const + { + const CGeorgesFormProxyModel * mp = dynamic_cast(index.model()); + const CGeorgesFormModel * m = dynamic_cast(mp->sourceModel()); + + const NLGEORGES::UType *type = m->getItem(mp->mapToSource(index))->getFormElm()->getType(); + int numDefinitions = type->getNumDefinition(); + + if (numDefinitions) + { + QComboBox *comboBox = static_cast(editor); + QString value = comboBox->currentText(); + QString oldValue = index.model()->data(index, Qt::DisplayRole).toString(); + if (value == oldValue) + { + // nothing's changed + } + else + { + nldebug(QString("setModelData from %1 to %2") + .arg(oldValue).arg(value).toStdString().c_str()); + model->setData(index, value, Qt::EditRole); + } + } + else + { + switch (type->getType()) + { + case NLGEORGES::UType::UnsignedInt: + case NLGEORGES::UType::SignedInt: + { + QSpinBox *spinBox = static_cast(editor); + int value = spinBox->value(); + QString oldValue = index.model()->data(index, Qt::DisplayRole).toString(); + if (QString("%1").arg(value) == oldValue) + { + // nothing's changed + } + else + { + nldebug(QString("setModelData from %1 to %2") + .arg(oldValue).arg(value).toStdString().c_str()); + model->setData(index, value, Qt::EditRole); + } + break; + } + case NLGEORGES::UType::Double: + { + QDoubleSpinBox *spinBox = static_cast(editor); + double value = spinBox->value(); + QString oldValue = index.model()->data(index, Qt::DisplayRole).toString(); + if (QString("%1").arg(value) == oldValue) + { + // nothing's changed + } + else + { + nldebug(QString("setModelData from %1 to %2") + .arg(oldValue).arg(value).toStdString().c_str()); + model->setData(index, value, Qt::EditRole); + } + break; + } + case NLGEORGES::UType::Color: + { + break; // TODO + } + default: // UType::String + { + QLineEdit *textEdit = static_cast(editor); + QString value = textEdit->text(); + QString oldValue = index.model()->data(index, Qt::DisplayRole).toString(); + if (value == oldValue) + { + // nothing's changed + } + else + { + nldebug(QString("setModelData from %1 to %2") + .arg(oldValue).arg(value).toStdString().c_str()); + model->setData(index, value, Qt::EditRole); + } + break; + } + } + } + } + + void FormDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index) const + { + QRect r = option.rect; + editor->setGeometry(r); + } +} /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formdelegate.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formdelegate.h new file mode 100644 index 000000000..a309da1fc --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/formdelegate.h @@ -0,0 +1,41 @@ +// Object Viewer Qt - Georges Editor Plugin - MMORPG Framework +// Copyright (C) 2011 Adrian Jaekel +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef FORMDELEGATE_H +#define FORMDELEGATE_H + +#include + +namespace Plugin +{ + + class FormDelegate : public QStyledItemDelegate + { + + public: + FormDelegate(QObject *parent = 0); + + 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; + }; + +} +#endif // FORMDELEGATE_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp index 9ea9ae5ff..425db7841 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp @@ -74,7 +74,7 @@ QString GeorgesEditorPlugin::name() const QString GeorgesEditorPlugin::version() const { - return "0.3"; + return "0.4"; } QString GeorgesEditorPlugin::vendor() const diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp index 09556a21a..bc5dd7487 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp @@ -32,7 +32,8 @@ #include "georgesform_model.h" #include "georgesform_proxy_model.h" #include "formitem.h" -//#include "formdelegate.h" +#include "formdelegate.h" +#include "expandable_headerview.h" using namespace NLMISC; using namespace NLGEORGES; @@ -41,7 +42,8 @@ namespace Plugin { CGeorgesTreeViewDialog::CGeorgesTreeViewDialog(QWidget *parent /*= 0*/) - : QDockWidget(parent) + : QDockWidget(parent), + m_header(0) { m_georges = new CGeorges; @@ -49,16 +51,18 @@ namespace Plugin m_modified = false; m_ui.setupUi(this); + m_header = new ExpandableHeaderView(Qt::Horizontal, m_ui.treeView); + m_ui.treeView->setHeader(m_header); m_ui.treeView->header()->setResizeMode(QHeaderView::ResizeToContents); + m_ui.treeView->header()->setStretchLastSection(true); m_ui.treeViewTabWidget->setTabEnabled (2,false); m_ui.checkBoxParent->setStyleSheet("background-color: rgba(0,255,0,30)"); m_ui.checkBoxDefaults->setStyleSheet("background-color: rgba(255,0,0,30)"); m_form = 0; - //FormDelegate *formdelegate = new FormDelegate(this); - //_ui.treeView->setItemDelegateForColumn(1, formdelegate); - + FormDelegate *formdelegate = new FormDelegate(this); + m_ui.treeView->setItemDelegateForColumn(1, formdelegate); connect(m_ui.treeView, SIGNAL(doubleClicked (QModelIndex)), this, SLOT(doubleClicked (QModelIndex))); @@ -66,14 +70,22 @@ namespace Plugin this, SLOT(filterRows())); connect(m_ui.checkBoxDefaults, SIGNAL(toggled(bool)), this, SLOT(filterRows())); + connect(m_header, SIGNAL(headerClicked(int)), + this, SLOT(headerClicked(int))); } CGeorgesTreeViewDialog::~CGeorgesTreeViewDialog() { - //delete _ui.treeView->itemDelegateForColumn(1); delete m_form; - //QSettings settings("RyzomCore", "GeorgesQt"); - //settings.setValue("dirViewGeometry", saveGeometry()); + } + + void CGeorgesTreeViewDialog::headerClicked(int section) + { + if (section == 0) + if (*(m_header->expanded())) + m_ui.treeView->expandAll(); + else + m_ui.treeView->collapseAll(); } void CGeorgesTreeViewDialog::setForm(const CForm *form) @@ -170,7 +182,7 @@ namespace Plugin { loadedForm = m_form->getFilename().c_str(); - CGeorgesFormModel *model = new CGeorgesFormModel(root,deps,comments,parents); + CGeorgesFormModel *model = new CGeorgesFormModel(root,deps,comments,parents,m_header->expanded()); CGeorgesFormProxyModel *proxyModel = new CGeorgesFormProxyModel(); proxyModel->setSourceModel(model); m_ui.treeView->setModel(proxyModel); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h index 9d4f02e5d..e4c5dcdf4 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h @@ -18,6 +18,7 @@ #define GEORGES_TREEVIEWER_DIALOG_H #include "ui_georges_treeview_form.h" +#include "expandable_headerview.h" // Qt includes #include @@ -77,9 +78,11 @@ namespace Plugin private Q_SLOTS: void doubleClicked ( const QModelIndex & index ); void filterRows(); + void headerClicked(int); private: Ui::CGeorgesTreeViewDialog m_ui; + ExpandableHeaderView *m_header; UForm *m_form; CGeorges *m_georges; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp index 0ec8f785a..3ae798dea 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp @@ -30,10 +30,13 @@ #include #include #include +#include +#include +#include +#include // project includes #include "formitem.h" -//#include "modules.h" using namespace NLGEORGES; @@ -41,7 +44,7 @@ namespace Plugin { CGeorgesFormModel::CGeorgesFormModel(UFormElm *rootElm, QMap< QString, QStringList> deps, - QString comment, QStringList parents, QObject *parent) : QAbstractItemModel(parent) + QString comment, QStringList parents, bool *expanded, QObject *parent) : QAbstractItemModel(parent) { QList rootData; rootData << "Value" << "Data" << "Extra";// << "Type"; @@ -51,6 +54,7 @@ namespace Plugin m_comments = comment; m_parents = parents; m_parentRows = new QList; + m_expanded = expanded; setupModelData(); } @@ -231,23 +235,23 @@ namespace Plugin /******************************************************************************/ - //bool CGeorgesFormModel::setData(const QModelIndex &index, const QVariant &value, - // int role) - //{ + bool CGeorgesFormModel::setData(const QModelIndex &index, const QVariant &value, + int role) + { - // if (role != Qt::EditRole) - // return false; + if (role != Qt::EditRole) + return false; - // CFormItem *item = getItem(index); - // bool result = item->setData(index.column(), value); + CFormItem *item = getItem(index); + bool result = item->setData(index.column(), value); // // TODO: ugly hack for updating icon too // if (result) // Q_EMIT dataChanged(index, this->index(index.row(),index.column()+1,index.parent())); - // setupModelData(); - // return result; - //} + //setupModelData(); + return result; + } /******************************************************************************/ @@ -258,11 +262,8 @@ namespace Plugin Qt::ItemFlags returnValue = Qt::ItemIsSelectable | Qt::ItemIsEnabled; - //if(index.column() == 1) - // returnValue |= Qt::ItemIsEditable; - // TODO? - // col 2 should go here too but i dont want to do another delegate - // so for now i just connected the dblClick in the dialog + if(index.column() == 1) + returnValue |= Qt::ItemIsEditable; return returnValue; @@ -273,11 +274,42 @@ namespace Plugin QVariant CGeorgesFormModel::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - return m_rootItem->data(section); + if (orientation == Qt::Horizontal) + { + if (role == Qt::DisplayRole) + return m_rootItem->data(section); + if (role == Qt::TextAlignmentRole) + return Qt::AlignLeft; + if (section == 0 && role == Qt::DecorationRole) + { + // transparent pixmap as we paint it ourself with tree brach + // if we extend the HeaderView::paintSection for the CE_HeaderLabel + // we could drop this + QPixmap pixmap = QPixmap( + QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize), + QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize)); + // Create new picture for transparent + QPixmap transparent(pixmap.size()); + // Do transparency + transparent.fill(Qt::transparent); + QPainter p(&transparent); + p.setCompositionMode(QPainter::CompositionMode_Source); + p.drawPixmap(0, 0, pixmap); + p.setCompositionMode(QPainter::CompositionMode_DestinationIn); + // Set transparency level to 150 (possible values are 0-255) + // The alpha channel of a color specifies the transparency effect, + // 0 represents a fully transparent color, while 255 represents + // a fully opaque color. + p.fillRect(transparent.rect(), QColor(0, 0, 0, 0)); + p.end(); + + // Set original picture's reference to new transparent one + pixmap = transparent; + return pixmap; + } + } return QVariant(); - } /******************************************************************************/ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.h index 7668f9e7f..0d8ce6e69 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.h @@ -39,11 +39,11 @@ namespace Plugin public: CGeorgesFormModel(NLGEORGES::UFormElm *root, QMap< QString, QStringList> deps, - QString comment, QStringList parents, QObject *parent = 0); + QString comment, QStringList parents, bool* expanded, QObject *parent = 0); ~CGeorgesFormModel(); QVariant data(const QModelIndex &index, int role) const; - //bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; @@ -71,6 +71,7 @@ namespace Plugin bool m_showParents; bool m_showDefaults; + bool *m_expanded; };/* class CGeorgesFormModel */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.cpp index 0950e1b04..ae3720a57 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_proxy_model.cpp @@ -14,17 +14,14 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -#include "georgesform_proxy_model.h" -#include "georgesform_model.h" - // NeL includes #include #include // project includes #include "formitem.h" - -#include +#include "georgesform_proxy_model.h" +#include "georgesform_model.h" namespace Plugin { @@ -32,20 +29,11 @@ namespace Plugin bool CGeorgesFormProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { - //nlinfo("CGeorgesFormProxyModel::filterAcceptsRow"); - // column doesnt matter for item CGeorgesFormModel *smodel = dynamic_cast(sourceModel()); QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent); CFormItem *item = smodel->getItem(index); - //qDebug() << smodel->showParents() << (item->valueFrom() == NLGEORGES::UFormElm::NodeParentForm); - //nlinfo("%s %d %d %d %d", item->data(index.column()).toString().toStdString().c_str(), - // item->valueFrom(), - // item->nodeFrom(), - // smodel->showParents(), - // (item->valueFrom() == NLGEORGES::UFormElm::NodeParentForm)); - // if elm not existing it must be some kind of default or type value if(!item->getFormElm()) { @@ -86,7 +74,6 @@ namespace Plugin bool CGeorgesFormProxyModel::filterAcceptsColumn(int sourceRow, const QModelIndex &sourceParent) const { - //nlinfo("CGeorgesFormProxyModel::filterAcceptsColumn"); return QSortFilterProxyModel::filterAcceptsColumn(sourceRow, sourceParent); } } /* namespace Plugin */ From cbeb115244005c66aa152ebfa67ea7570b66b318 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Fri, 2 Sep 2011 01:29:19 +0300 Subject: [PATCH 094/215] Changed: #1193 Fix typos in comments/code. --- .../src/plugins/object_viewer/bin_op_dialog.h | 2 +- .../src/plugins/object_viewer/entity.h | 2 +- .../object_viewer/graphics_viewport.cpp | 6 +- .../plugins/object_viewer/graphics_viewport.h | 2 +- .../src/plugins/object_viewer/hoverpoints.cpp | 2 +- .../object_viewer/morph_mesh_dialog.cpp | 2 +- .../plugins/object_viewer/object_viewer.cpp | 2 +- .../src/plugins/object_viewer/object_viewer.h | 2 +- .../object_viewer/particle_tree_model.cpp | 4 +- .../particle_workspace_dialog.cpp | 4 +- .../object_viewer/particle_workspace_dialog.h | 2 +- .../src/plugins/object_viewer/sound_system.h | 8 +- .../vegetable_appearance_page.cpp | 2 +- .../plugins/object_viewer/workspace_form.ui | 96 +++++++++---------- 14 files changed, 68 insertions(+), 68 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/bin_op_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/bin_op_dialog.h index 061edafd4..3180e5d84 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/bin_op_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/bin_op_dialog.h @@ -97,7 +97,7 @@ public: _AttrbDlg[k]->init(); } - static const char * const operators[] = + static const char *const operators[] = { QT_TR_NOOP("Select Arg1"), QT_TR_NOOP("Select Arg2"), diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/entity.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/entity.h index 88338f214..a322363be 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/entity.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/entity.h @@ -141,7 +141,7 @@ public: float getAnimLength(std::string name); /// Get slot infomation - void setSlotInfo(uint num, CSlotInfo& slotInfo) + void setSlotInfo(uint num, CSlotInfo &slotInfo) { _SlotInfo[num] = slotInfo; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/graphics_viewport.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/graphics_viewport.cpp index 93bc1043e..d8e36e785 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/graphics_viewport.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/graphics_viewport.cpp @@ -135,7 +135,7 @@ bool CGraphicsViewport::winEvent(MSG *message, long *result) { winProc proc = (winProc)driver->getWindowProc(); - // TODO: shouldn't it return false like the others? + // TODO: shouldn't it return false like the others? // see macEvent() and x11Event() below return proc(driver, message->hwnd, message->message, message->wParam, message->lParam); } @@ -163,7 +163,7 @@ bool CGraphicsViewport::macEvent(EventHandlerCallRef caller, EventRef event) } } - // return false to let Qt handle the event as well, + // return false to let Qt handle the event as well, // else the widget would never get focus return false; } @@ -184,7 +184,7 @@ bool CGraphicsViewport::x11Event(XEvent *event) } } - // return false to let Qt handle the event as well, + // return false to let Qt handle the event as well, // else the widget would never get focus // TODO: test me please, i have no linux at hand (rti) return false; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/graphics_viewport.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/graphics_viewport.h index d810d8c01..0c01fa1ad 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/graphics_viewport.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/graphics_viewport.h @@ -59,7 +59,7 @@ public: CGraphicsViewport(QWidget *parent); virtual ~CGraphicsViewport(); - virtual QPaintEngine* paintEngine() const + virtual QPaintEngine *paintEngine() const { return NULL; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/hoverpoints.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/hoverpoints.cpp index fe0751082..a2556c08d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/hoverpoints.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/hoverpoints.cpp @@ -161,7 +161,7 @@ bool HoverPoints::eventFilter(QObject *object, QEvent *event) case QEvent::TouchBegin: case QEvent::TouchUpdate: { - const QTouchEvent *const touchEvent = static_cast(event); + const QTouchEvent *const touchEvent = static_cast(event); const QList points = touchEvent->touchPoints(); const qreal pointSize = qMax(m_pointSize.width(), m_pointSize.height()); Q_FOREACH (const QTouchEvent::TouchPoint &touchPoint, points) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/morph_mesh_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/morph_mesh_dialog.cpp index d125277e4..de88d14c8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/morph_mesh_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/morph_mesh_dialog.cpp @@ -91,7 +91,7 @@ QString CMorphMeshDialog::getShapeDescStr(uint shapeIndex, sint numVerts) const } else { - QString error = qobject_cast(QObject::parent())->getShapeErrorString(numVerts); + QString error = qobject_cast(QObject::parent())->getShapeErrorString(numVerts); QString result = _CM->getShape(shapeIndex).c_str() + QString(" (%1)").arg(error); return result; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer.cpp index dc248e563..4b0509018 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer.cpp @@ -435,7 +435,7 @@ void CObjectViewer::setCurrentObject(const std::string &name) nlinfo("set current entity %s", _CurrentInstance.c_str()); } -CEntity& CObjectViewer::getEntity(const std::string &name) +CEntity &CObjectViewer::getEntity(const std::string &name) { if ( _Entities.count(name) == 0) nlerror("Entity %s not found", name.c_str()); EIT eit = _Entities.find (name); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer.h index dc96f5cdf..5e978d7e0 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer.h @@ -152,7 +152,7 @@ public: /// Get entity from the scene /// @return ref Entity - CEntity& getEntity(const std::string &name); + CEntity &getEntity(const std::string &name); /// Get full list instances from the scene /// @param listObj - ref of return list instances diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_tree_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_tree_model.cpp index 77286ed32..2daebd6e4 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_tree_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_tree_model.cpp @@ -404,7 +404,7 @@ bool CParticleTreeModel::insertRows(NL3D::CPSLocated *loc, int position, const Q bool CParticleTreeModel::insertRow(NL3D::CPSLocated *loc, uint32 index, int position, const QModelIndex &parent) { beginInsertRows(parent, position, position); - createItemFromLocatedInstance(loc, index, static_cast(parent.internalPointer())); + createItemFromLocatedInstance(loc, index, static_cast(parent.internalPointer())); endInsertRows(); return true; } @@ -424,7 +424,7 @@ bool CParticleTreeModel::removeRows(int position, const QModelIndex &parent) removeRows(0, parent.child(position, 0)); beginRemoveRows(parent, position, position); - static_cast(parent.internalPointer())->deleteChild(position); + static_cast(parent.internalPointer())->deleteChild(position); endRemoveRows(); return false; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.cpp index 858922af1..cb49def05 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.cpp @@ -46,7 +46,7 @@ namespace NLQT { -static const char * const LocatedBindable[] = +static const char *const LocatedBindable[] = { QT_TR_NOOP("Point"), QT_TR_NOOP("LookAt"), @@ -220,7 +220,7 @@ void CParticleWorkspaceDialog::touchPSState(CParticleTreeItem *item) } } -void CParticleWorkspaceDialog::clickedItem(const QModelIndex & index) +void CParticleWorkspaceDialog::clickedItem(const QModelIndex &index) { if (_currentItem != 0) _treeModel->getOwnerNode(_currentItem)->getPSPointer()->setCurrentEditedElement(NULL); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.h index 3a317adc7..c7aa0e52b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.h @@ -59,7 +59,7 @@ Q_SIGNALS: void changeActiveNode(); private Q_SLOTS: - void clickedItem(const QModelIndex & index); + void clickedItem(const QModelIndex &index); void customContextMenu(); void setActiveNode(); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.h index 4add4b9db..79ea68a46 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.h @@ -61,13 +61,13 @@ public: } /// Sets the path which contains samples - void setSamplePath(std::string& path) + void setSamplePath(std::string &path) { _SamplePath = NLMISC::CPath::standardizePath(path, true); } /// Sets the path which contains packed sheet - void setPackedSheetPath(std::string& path) + void setPackedSheetPath(std::string &path) { _PackedSheetPath = NLMISC::CPath::standardizePath(path, true); } @@ -88,13 +88,13 @@ public: NLSOUND::USource *create(const std::string &soundName); /// Load the sound animation with the specified name - void loadAnimation(std::string& name) + void loadAnimation(std::string &name) { _AnimManager->loadAnimation(name); } /// Start playing a sound animation. - void playAnimation(std::string& name, float lastTime, float curTime, NLSOUND::CSoundContext &context); + void playAnimation(std::string &name, float lastTime, float curTime, NLSOUND::CSoundContext &context); // Update the sound animations. //static void updateAnimations(float lastTime, float curTime) { _AnimManager->update(lastTime, curTime); }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/vegetable_appearance_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/vegetable_appearance_page.cpp index 9c49871b3..0366bc11f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/vegetable_appearance_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/vegetable_appearance_page.cpp @@ -60,7 +60,7 @@ CVegetableApperancePage::CVegetableApperancePage(QWidget *parent) connect(_ui.removePushButton, SIGNAL(clicked()), this, SLOT(removeColor())); connect(_ui.getListPushButton, SIGNAL(clicked()), this, SLOT(getFromListColors())); - connect(_ui.listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(browseColor(QListWidgetItem*))); + connect(_ui.listWidget, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(browseColor(QListWidgetItem *))); setEnabled(false); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/workspace_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/workspace_form.ui index 28705f097..b8bec17b7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/workspace_form.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/workspace_form.ui @@ -6,8 +6,8 @@ 0 0 - 337 - 232 + 235 + 293 @@ -130,7 +130,51 @@ - + + + + false + + + + 0 + 0 + + + + + 55 + 0 + + + + + 16777215 + 16777215 + + + + Unload + + + + :/icons/ic_nel_particle_system_close.png:/icons/ic_nel_particle_system_close.png + + + + 32 + 32 + + + + Qt::ToolButtonTextUnderIcon + + + true + + + + false @@ -177,7 +221,7 @@ - + false @@ -221,50 +265,6 @@ - - - - false - - - - 0 - 0 - - - - - 55 - 0 - - - - - 16777215 - 16777215 - - - - Unload - - - - :/icons/ic_nel_particle_system_close.png:/icons/ic_nel_particle_system_close.png - - - - 32 - 32 - - - - Qt::ToolButtonTextUnderIcon - - - true - - - From 52a6e8010cd9ca625c870e426a39c76e65370719 Mon Sep 17 00:00:00 2001 From: aquiles Date: Sun, 4 Sep 2011 18:48:37 +0200 Subject: [PATCH 095/215] Changed: #1306 some focus (in terms of widget focus) related additions, to provide expected usabillity (Tab/Click Focus etc.) --- .../georges_editor/georges_editor_form.cpp | 108 ++++++++++++------ .../georges_editor/georges_editor_form.h | 16 +-- .../georges_treeview_dialog.cpp | 67 +++-------- .../georges_editor/georges_treeview_dialog.h | 5 +- .../georges_editor/georges_treeview_form.ui | 6 + .../georges_editor/georgesform_model.cpp | 4 +- 6 files changed, 113 insertions(+), 93 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp index c623d440b..f67be6471 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp @@ -39,8 +39,8 @@ namespace Plugin GeorgesEditorForm::GeorgesEditorForm(QWidget *parent) : QMainWindow(parent), m_georgesDirTreeDialog(0), - m_emptyDock(0), - m_mainDock(0) + m_mainDock(0), + m_lastActiveDock(0) { m_ui.setupUi(this); @@ -64,34 +64,26 @@ namespace Plugin m_undoStack = new QUndoStack(this); Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); - _openAction = menuManager->action(Core::Constants::OPEN); + m_openAction = menuManager->action(Core::Constants::OPEN); - /*_openAction = new QAction(tr("&Open..."), this); - _openAction->setIcon(QIcon(Core::Constants::ICON_OPEN)); - _openAction->setShortcut(QKeySequence::Open); - _openAction->setStatusTip(tr("Open an existing file")); - connect(_openAction, SIGNAL(triggered()), this, SLOT(open()));*/ + m_newAction = new QAction(tr("&New..."), this); + m_newAction->setIcon(QIcon(Core::Constants::ICON_NEW)); + m_newAction->setShortcut(QKeySequence::New); + m_newAction->setStatusTip(tr("Create a new file")); + connect(m_newAction, SIGNAL(triggered()), this, SLOT(newFile())); - _newAction = new QAction(tr("&New..."), this); - _newAction->setIcon(QIcon(Core::Constants::ICON_NEW)); - _newAction->setShortcut(QKeySequence::New); - _newAction->setStatusTip(tr("Create a new file")); - connect(_newAction, SIGNAL(triggered()), this, SLOT(newFile())); + m_saveAction = new QAction(tr("&Save..."), this); + m_saveAction->setIcon(QIcon(Core::Constants::ICON_SAVE)); + m_saveAction->setShortcut(QKeySequence::Save); + m_saveAction->setStatusTip(tr("Save the current file")); + connect(m_saveAction, SIGNAL(triggered()), this, SLOT(save())); - _saveAction = new QAction(tr("&Save..."), this); - _saveAction->setIcon(QIcon(Core::Constants::ICON_SAVE)); - _saveAction->setShortcut(QKeySequence::Save); - _saveAction->setStatusTip(tr("Save the current file")); - connect(_saveAction, SIGNAL(triggered()), this, SLOT(save())); + m_fileToolBar = addToolBar(tr("&File")); + m_fileToolBar->addAction(m_openAction); + m_fileToolBar->addAction(m_newAction); + m_fileToolBar->addAction(m_saveAction); - _fileToolBar = addToolBar(tr("&File")); - _fileToolBar->addAction(_openAction); - _fileToolBar->addAction(_newAction); - _fileToolBar->addAction(_saveAction); - - //_openAction->setEnabled(false); - //_newAction->setEnabled(false); - //_saveAction->setEnabled(false); + m_saveAction->setEnabled(false); readSettings(); @@ -104,6 +96,8 @@ namespace Plugin this, SLOT(settingsChanged())); connect(m_georgesDirTreeDialog, SIGNAL(selectedForm(const QString)), this, SLOT(loadFile(const QString))); + connect(qApp, SIGNAL(focusChanged(QWidget*, QWidget*)), + this, SLOT(focusChanged(QWidget*, QWidget*))); } GeorgesEditorForm::~GeorgesEditorForm() @@ -196,25 +190,34 @@ namespace Plugin if (!m_dockedWidgets.size()) { - m_dockedWidgets.append(new CGeorgesTreeViewDialog(m_mainDock)); + CGeorgesTreeViewDialog *dock = new CGeorgesTreeViewDialog(m_mainDock); + m_lastActiveDock = dock; + m_dockedWidgets.append(dock); + m_mainDock->addDockWidget(Qt::RightDockWidgetArea, m_dockedWidgets.last()); connect(m_dockedWidgets.last(), SIGNAL(closing()), this, SLOT(closingTreeView())); + connect(m_dockedWidgets.last(), SIGNAL(visibilityChanged(bool)), + m_dockedWidgets.last(), SLOT(checkVisibility(bool))); } else { - Q_FOREACH(QDockWidget *wgt, m_dockedWidgets) + Q_FOREACH(CGeorgesTreeViewDialog *wgt, m_dockedWidgets) { - if (info.fileName() == wgt->windowTitle()) + if (info.fileName() == wgt->loadedForm) { wgt->raise(); return; } } - m_dockedWidgets.append(new CGeorgesTreeViewDialog(m_mainDock)); + CGeorgesTreeViewDialog *dock = new CGeorgesTreeViewDialog(m_mainDock); + m_dockedWidgets.append(dock); + connect(m_dockedWidgets.last(), SIGNAL(closing()), this, SLOT(closingTreeView())); + connect(m_dockedWidgets.last(), SIGNAL(visibilityChanged(bool)), + m_dockedWidgets.last(), SLOT(checkVisibility(bool))); Q_ASSERT(m_dockedWidgets.size() > 1); m_mainDock->tabifyDockWidget(m_dockedWidgets.at(m_dockedWidgets.size() - 2), m_dockedWidgets.last()); } @@ -224,6 +227,8 @@ namespace Plugin m_dockedWidgets.last()->setForm(form); m_dockedWidgets.last()->loadFormIntoDialog(form); QApplication::processEvents(); + connect(m_dockedWidgets.last(), SIGNAL(modified()), + this, SLOT(setModified())); m_dockedWidgets.last()->raise(); connect(m_dockedWidgets.last(), SIGNAL(changeFile(QString)), m_georgesDirTreeDialog, SLOT(changeFile(QString))); @@ -236,10 +241,47 @@ namespace Plugin void GeorgesEditorForm::closingTreeView() { - int i = m_dockedWidgets.size(); + //qDebug() << "closingTreeView"; m_dockedWidgets.removeAll(qobject_cast(sender())); - i = m_dockedWidgets.size(); - int j = i; + if (qobject_cast(sender()) == m_lastActiveDock) + m_lastActiveDock = 0; } + void GeorgesEditorForm::setModified () + { + qDebug() << "setModified"; + if (m_lastActiveDock) + m_saveAction->setEnabled(m_lastActiveDock->isModified()); + else + m_saveAction->setEnabled(false); + } + + void GeorgesEditorForm::focusChanged ( QWidget * old, QWidget * now ) + { + if (now) + { + // ugly, UGLY hack for compensating QDockWidgets failure in focus API + if (now->objectName() == "treeView" || + now->objectName() == "checkBoxDefaults" || + now->objectName() == "checkBoxParent" || + now->objectName() == "commentEdit") + { + QWidget *dlg = 0; + QApplication::focusWidget()? + QApplication::focusWidget()->parentWidget()? + QApplication::focusWidget()->parentWidget()->parentWidget()? + QApplication::focusWidget()->parentWidget()->parentWidget()->parentWidget()? + QApplication::focusWidget()->parentWidget()->parentWidget()->parentWidget()->parentWidget()? + QApplication::focusWidget()->parentWidget()->parentWidget()->parentWidget()->parentWidget()->parentWidget()? + dlg=QApplication::focusWidget()->parentWidget()->parentWidget()->parentWidget()->parentWidget()->parentWidget():dlg=0:dlg=0:dlg=0:dlg=0:dlg=0:dlg=0; + CGeorgesTreeViewDialog *active = qobject_cast(dlg); + if(active) + { + //qDebug() << "focusChanged" << active->loadedForm; + m_lastActiveDock = active; + m_saveAction->setEnabled(active->isModified()); + } + } + } + } } /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h index 4ed91a5f6..6b270ca3d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.h @@ -40,11 +40,14 @@ public: public Q_SLOTS: void open(); + void loadFile(const QString fileName); void newFile(); void save(); void settingsChanged(); - void loadFile(const QString fileName); void closingTreeView(); + void setModified(); + + void focusChanged(QWidget *old, QWidget *now); private: void readSettings(); @@ -54,18 +57,17 @@ private: Ui::GeorgesEditorForm m_ui; CGeorgesDirTreeDialog *m_georgesDirTreeDialog; - QToolBar *_fileToolBar; - QAction *_openAction; - QAction *_newAction; - QAction *_saveAction; + QToolBar *m_fileToolBar; + QAction *m_openAction; + QAction *m_newAction; + QAction *m_saveAction; QString m_leveldesignPath; - QDockWidget *m_emptyDock; QMainWindow *m_mainDock; QList m_dockedWidgets; - QList m_tabBars; + CGeorgesTreeViewDialog *m_lastActiveDock; }; /* class GeorgesEditorForm */ } /* namespace Plugin */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp index bc5dd7487..13b0dab03 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.cpp @@ -20,6 +20,7 @@ #include #include #include +#include // NeL includes #include @@ -43,12 +44,12 @@ namespace Plugin CGeorgesTreeViewDialog::CGeorgesTreeViewDialog(QWidget *parent /*= 0*/) : QDockWidget(parent), - m_header(0) + m_header(0), + m_modified(false) { m_georges = new CGeorges; loadedForm = ""; - m_modified = false; m_ui.setupUi(this); m_header = new ExpandableHeaderView(Qt::Horizontal, m_ui.treeView); @@ -77,6 +78,7 @@ namespace Plugin CGeorgesTreeViewDialog::~CGeorgesTreeViewDialog() { delete m_form; + qDebug() << "DTOR"; } void CGeorgesTreeViewDialog::headerClicked(int section) @@ -193,8 +195,8 @@ namespace Plugin filterRows(); // //_ui.treeView->setRowHidden(0,QModelIndex(),true); - // connect(model, SIGNAL(dataChanged(const QModelIndex, const QModelIndex)), - // this, SLOT(modifiedFile())); + connect(model, SIGNAL(dataChanged(const QModelIndex, const QModelIndex)), + this, SLOT(modifiedFile())); setWindowTitle(loadedForm); // //Modules::mainWin().getTabBar(); @@ -208,13 +210,12 @@ namespace Plugin void CGeorgesTreeViewDialog::modifiedFile( ) { - /*if (!_modified) + if (!m_modified) { - _modified = true; - setWindowTitle(windowTitle()+"*"); - Modules::mainWin().setWindowTitle(Modules::mainWin().windowTitle()+"*"); - Q_EMIT modified(_modified); - }*/ + m_modified = true; + setWindowTitle(windowTitle() + "*"); + } + Q_EMIT modified(); } void CGeorgesTreeViewDialog::write( ) @@ -366,52 +367,22 @@ namespace Plugin deleteLater(); } + void CGeorgesTreeViewDialog::checkVisibility(bool visible) { + // this prevents invisible docks from getting tab focus + qDebug() << "checkVisibility" << visible; + setEnabled(visible); + //if (visible) + Q_EMIT modified(); + } + void CGeorgesTreeViewDialog::filterRows() { - nlinfo("CGeorgesTreeViewDialog::filterRows"); CGeorgesFormProxyModel * mp = dynamic_cast(m_ui.treeView->model()); CGeorgesFormModel *m = dynamic_cast(mp->sourceModel()); if (m) { m->setShowParents(m_ui.checkBoxParent->isChecked()); m->setShowDefaults(m_ui.checkBoxDefaults->isChecked()); } - - //CGeorgesFormProxyModel * mp = dynamic_cast(_ui.treeView->model()); - //CGeorgesFormModel *m = dynamic_cast(mp->sourceModel()); - - //for (int i = 0; i < m->rowCount(); i++) - //{ - // const QModelIndex in = m->index(i,0); - // if (m->getItem(in)->nodeFrom() == UFormElm::NodeParentForm) - // { - // if (newState == Qt::Checked) - // { - // _ui.treeView->setRowHidden(in.row(),in.parent(),false); - // } - // else - // { - // _ui.treeView->setRowHidden(in.row(),in.parent(),true); - // } - // } - // else - // { // search childs // recursive? - // for (int j = 0; j < m->rowCount(in); j++) - // { - // const QModelIndex in2 = m->index(j,0,in); - // if (m->getItem(in2)->nodeFrom() == UFormElm::NodeParentForm) - // { - // if (newState == Qt::Checked) - // { - // _ui.treeView->setRowHidden(in2.row(),in,false); - // } - // else - // { - // _ui.treeView->setRowHidden(in2.row(),in,true); - // } - // } - // } - // } // end of search childs - //} } } /* namespace NLQT */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h index e4c5dcdf4..4992c9b23 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_dialog.h @@ -50,7 +50,7 @@ namespace Plugin CGeorgesTreeViewDialog(QWidget *parent = 0); ~CGeorgesTreeViewDialog(); - bool modified() {return m_modified;} + bool isModified() {return m_modified;} void setModified(bool m) {m_modified = m;} CForm* getFormByName(const QString); @@ -67,13 +67,14 @@ namespace Plugin Q_SIGNALS: void changeFile(QString); - void modified(bool); + void modified(); void closing(); public Q_SLOTS: void setForm(const CForm*); void loadFormIntoDialog(CForm *form = 0); void modifiedFile( ); + void checkVisibility(bool); private Q_SLOTS: void doubleClicked ( const QModelIndex & index ); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_form.ui index 71db74326..183b16118 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_form.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_treeview_form.ui @@ -22,6 +22,9 @@ 165 + + Qt::NoFocus + @@ -36,6 +39,9 @@ 0 + + Qt::NoFocus + Form diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp index 3ae798dea..f6b9c441b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georgesform_model.cpp @@ -245,9 +245,7 @@ namespace Plugin CFormItem *item = getItem(index); bool result = item->setData(index.column(), value); - // // TODO: ugly hack for updating icon too - // if (result) - // Q_EMIT dataChanged(index, this->index(index.row(),index.column()+1,index.parent())); + Q_EMIT dataChanged(index, index); //setupModelData(); return result; From 16383043d563a76ed40d57001f3546c4d1f27174 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 10 Sep 2011 18:43:21 +0200 Subject: [PATCH 096/215] Fixed: "relocation R_X86_64_32 against.." error when mixing static and shared libraries --- code/CMakeModules/nel.cmake | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 1524ebf46..19e0ea5c0 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -368,6 +368,11 @@ MACRO(NL_SETUP_BUILD) SET(PLATFORM_CFLAGS "-gdwarf-2 ${PLATFORM_CFLAGS}") ENDIF(APPLE) + # Fix "relocation R_X86_64_32 against.." error on x64 platforms + IF(TARGET_X64 AND WITH_STATIC AND NOT WITH_STATIC_DRIVERS) + SET(PLATFORM_CFLAGS "-fPIC ${PLATFORM_CFLAGS}") + ENDIF(TARGET_X64 AND WITH_STATIC AND NOT WITH_STATIC_DRIVERS) + SET(PLATFORM_CXXFLAGS ${PLATFORM_CFLAGS}) IF(NOT APPLE) From 60ba9387948b117b630a089b09ec6271692b2917 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 10 Sep 2011 18:45:54 +0200 Subject: [PATCH 097/215] Changed: Allow to use external under Linux and Mac OS X --- code/CMakeModules/FindExternal.cmake | 14 +++++++------- code/CMakeModules/nel.cmake | 16 +++++++++++----- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/code/CMakeModules/FindExternal.cmake b/code/CMakeModules/FindExternal.cmake index 1ca01bd99..5d6086bc6 100644 --- a/code/CMakeModules/FindExternal.cmake +++ b/code/CMakeModules/FindExternal.cmake @@ -8,7 +8,7 @@ # EXTERNAL_FOUND - True if the external libraries are available SET(EXTERNAL_TEMP_PATH ${CMAKE_CURRENT_SOURCE_DIR}/external ${CMAKE_CURRENT_SOURCE_DIR}/../external ${CMAKE_CURRENT_SOURCE_DIR}/3rdParty ${CMAKE_CURRENT_SOURCE_DIR}/../3rdParty ${EXTERNAL_PATH}) -SET(EXTERNAL_TEMP_FILE "include/zlib.h") +SET(EXTERNAL_TEMP_FILE "include/wwwconf.h") SET(EXTERNAL_NAME "external") # If using STLport preprend external_stlport @@ -36,18 +36,18 @@ IF(EXTERNAL_PATH) SET(EXTERNAL_INCLUDE_PATH "${EXTERNAL_PATH}/include") # Using 32 or 64 bits binaries - IF(TARGET_X64) + IF(TARGET_X64 AND WIN32) SET(EXTERNAL_BINARY_PATH "${EXTERNAL_PATH}/bin64") - ELSE(TARGET_X64) + ELSE(TARGET_X64 AND WIN32) SET(EXTERNAL_BINARY_PATH "${EXTERNAL_PATH}/bin") - ENDIF(TARGET_X64) + ENDIF(TARGET_X64 AND WIN32) # Using 32 or 64 bits libraries - IF(TARGET_X64) + IF(TARGET_X64 AND WIN32) SET(EXTERNAL_LIBRARY_PATH "${EXTERNAL_PATH}/lib64") - ELSE(TARGET_X64) + ELSE(TARGET_X64 AND WIN32) SET(EXTERNAL_LIBRARY_PATH "${EXTERNAL_PATH}/lib") - ENDIF(TARGET_X64) + ENDIF(TARGET_X64 AND WIN32) SET(CMAKE_INCLUDE_PATH "${EXTERNAL_INCLUDE_PATH};${CMAKE_INCLUDE_PATH}") # Stupid hack for FindOpenAL.cmake diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 19e0ea5c0..64e3e7275 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -178,6 +178,11 @@ MACRO(NL_SETUP_DEFAULT_OPTIONS) OPTION(WITH_STATIC "With static libraries." OFF) ENDIF(WIN32) OPTION(WITH_STATIC_DRIVERS "With static drivers." OFF) + IF(WIN32) + OPTION(WITH_EXTERNAL "With provided external." ON ) + ELSE(WIN32) + OPTION(WITH_EXTERNAL "With provided external." OFF) + ENDIF(WIN32) OPTION(WITH_STATIC_EXTERNAL "With static external libraries" OFF) ### @@ -203,9 +208,9 @@ MACRO(NL_SETUP_DEFAULT_OPTIONS) OPTION(BUILD_DASHBOARD "Build to the CDash dashboard" OFF) - OPTION(WITH_NEL "Build NeL (nearly always required)." ON ) - OPTION(WITH_NELNS "Build NeL Network Services." OFF) - OPTION(WITH_RYZOM "Build Ryzom Core." ON ) + OPTION(WITH_NEL "Build NeL (nearly always required)." ON ) + OPTION(WITH_NELNS "Build NeL Network Services." OFF) + OPTION(WITH_RYZOM "Build Ryzom Core." ON ) OPTION(WITH_SNOWBALLS "Build Snowballs." OFF) ENDMACRO(NL_SETUP_DEFAULT_OPTIONS) @@ -518,9 +523,11 @@ MACRO(RYZOM_SETUP_PREFIX_PATHS) ENDMACRO(RYZOM_SETUP_PREFIX_PATHS) MACRO(SETUP_EXTERNAL) - IF(WIN32) + IF(WITH_EXTERNAL) FIND_PACKAGE(External REQUIRED) + ENDIF(WITH_EXTERNAL) + IF(WIN32) INCLUDE(${CMAKE_ROOT}/Modules/Platform/Windows-cl.cmake) IF(MSVC10) IF(NOT MSVC10_REDIST_DIR) @@ -567,5 +574,4 @@ MACRO(SETUP_EXTERNAL) INCLUDE_DIRECTORIES(${VC_INCLUDE_DIR} ${WINSDK_INCLUDE_DIR}) ENDIF(WIN32) ENDIF(WITH_STLPORT) - ENDMACRO(SETUP_EXTERNAL) From 630683342339c4746038e8e9ba9cb68311d0535e Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 11 Sep 2011 14:44:37 +0200 Subject: [PATCH 098/215] Changed: New option WITH_INSTALL_LIBRARIES which allows user to install only final binaries and no development files (header, static libraries, etc...) --- code/CMakeModules/nel.cmake | 43 ++++++++++--------- code/nel/CMakeLists.txt | 35 ++++++++------- code/nel/src/3d/CMakeLists.txt | 5 ++- .../nel/src/3d/driver/direct3d/CMakeLists.txt | 10 +++-- code/nel/src/3d/driver/opengl/CMakeLists.txt | 10 +++-- code/nel/src/cegui/CMakeLists.txt | 4 +- code/nel/src/georges/CMakeLists.txt | 5 ++- code/nel/src/ligo/CMakeLists.txt | 5 ++- code/nel/src/logic/CMakeLists.txt | 4 +- code/nel/src/misc/CMakeLists.txt | 5 ++- code/nel/src/net/CMakeLists.txt | 5 ++- code/nel/src/pacs/CMakeLists.txt | 5 ++- code/nel/src/sound/CMakeLists.txt | 5 ++- code/nel/src/sound/driver/CMakeLists.txt | 4 +- .../src/sound/driver/dsound/CMakeLists.txt | 10 +++-- code/nel/src/sound/driver/fmod/CMakeLists.txt | 10 +++-- .../src/sound/driver/openal/CMakeLists.txt | 10 +++-- .../src/sound/driver/xaudio2/CMakeLists.txt | 10 +++-- code/ryzom/client/CMakeLists.txt | 4 +- code/ryzom/client/src/CMakeLists.txt | 8 ++-- .../client/src/client_sheets/CMakeLists.txt | 4 +- .../ryzom/client/src/seven_zip/CMakeLists.txt | 6 ++- .../common/src/game_share/CMakeLists.txt | 5 ++- 23 files changed, 130 insertions(+), 82 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 64e3e7275..5c5efc4a9 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -3,10 +3,10 @@ # Argument: name - the name of the .pc package, e.g. "nel-pacs.pc" ### MACRO(NL_GEN_PC name) - IF(NOT WIN32) + IF(NOT WIN32 AND WITH_INSTALL_LIBRARIES) CONFIGURE_FILE(${name}.in "${CMAKE_CURRENT_BINARY_DIR}/${name}") INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${name}" DESTINATION lib/pkgconfig) - ENDIF(NOT WIN32) + ENDIF(NOT WIN32 AND WITH_INSTALL_LIBRARIES) ENDMACRO(NL_GEN_PC) ### @@ -184,6 +184,7 @@ MACRO(NL_SETUP_DEFAULT_OPTIONS) OPTION(WITH_EXTERNAL "With provided external." OFF) ENDIF(WIN32) OPTION(WITH_STATIC_EXTERNAL "With static external libraries" OFF) + OPTION(WITH_INSTALL_LIBRARIES "Install development files." ON ) ### # GUI toolkits @@ -211,7 +212,7 @@ MACRO(NL_SETUP_DEFAULT_OPTIONS) OPTION(WITH_NEL "Build NeL (nearly always required)." ON ) OPTION(WITH_NELNS "Build NeL Network Services." OFF) OPTION(WITH_RYZOM "Build Ryzom Core." ON ) - OPTION(WITH_SNOWBALLS "Build Snowballs." OFF) + OPTION(WITH_SNOWBALLS "Build Snowballs." OFF) ENDMACRO(NL_SETUP_DEFAULT_OPTIONS) MACRO(NL_SETUP_NEL_DEFAULT_OPTIONS) @@ -327,18 +328,18 @@ MACRO(NL_SETUP_BUILD) SET(SPEED_OPTIMIZATIONS "/Ox /GF /GS-") # without inlining it's unusable, use custom optimizations again SET(MIN_OPTIMIZATIONS "/Od /Ob1") - ELSEIF(MSVC90) + ELSEIF(MSVC90) # don't use a /O[012x] flag if you want custom optimizations SET(SPEED_OPTIMIZATIONS "/Ob2 /Oi /Ot /Oy /GT /GF /GS-") # without inlining it's unusable, use custom optimizations again SET(MIN_OPTIMIZATIONS "/Ob1") - ELSEIF(MSVC80) + ELSEIF(MSVC80) # don't use a /O[012x] flag if you want custom optimizations SET(SPEED_OPTIMIZATIONS "/Ox /GF /GS-") # without inlining it's unusable, use custom optimizations again SET(MIN_OPTIMIZATIONS "/Od /Ob1") - ELSE(MSVC10) - MESSAGE(FATAL_ERROR "Can't determine compiler version ${MSVC_VERSION}") + ELSE(MSVC10) + MESSAGE(FATAL_ERROR "Can't determine compiler version ${MSVC_VERSION}") ENDIF(MSVC10) SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} /D_CRT_SECURE_NO_WARNINGS /D_CRT_NONSTDC_NO_WARNINGS /DWIN32 /D_WINDOWS /W3 /Zi /Zm1000 /MP /Gy-") @@ -480,43 +481,43 @@ MACRO(RYZOM_SETUP_PREFIX_PATHS) IF(WIN32) SET(RYZOM_ETC_PREFIX "." CACHE PATH "Installation path for configurations") ELSE(WIN32) - SET(RYZOM_ETC_PREFIX "${CMAKE_INSTALL_PREFIX}/etc/ryzom" CACHE PATH "Installation path for configurations") + SET(RYZOM_ETC_PREFIX "${RYZOM_PREFIX}/etc/ryzom" CACHE PATH "Installation path for configurations") ENDIF(WIN32) ENDIF(NOT RYZOM_ETC_PREFIX) ## Allow override of install_prefix/share path. IF(NOT RYZOM_SHARE_PREFIX) IF(WIN32) - SET(RYZOM_SHARE_PREFIX "." CACHE PATH "Installation path for data.") - ELSE(WIN32) - SET(RYZOM_SHARE_PREFIX "${CMAKE_INSTALL_PREFIX}/share/ryzom" CACHE PATH "Installation path for data.") - ENDIF(WIN32) + SET(RYZOM_SHARE_PREFIX "." CACHE PATH "Installation path for data.") + ELSE(WIN32) + SET(RYZOM_SHARE_PREFIX "${RYZOM_PREFIX}/share/ryzom" CACHE PATH "Installation path for data.") + ENDIF(WIN32) ENDIF(NOT RYZOM_SHARE_PREFIX) ## Allow override of install_prefix/sbin path. IF(NOT RYZOM_SBIN_PREFIX) - IF(WIN32) - SET(RYZOM_SBIN_PREFIX "." CACHE PATH "Installation path for admin tools and services.") - ELSE(WIN32) - SET(RYZOM_SBIN_PREFIX "${CMAKE_INSTALL_PREFIX}/sbin" CACHE PATH "Installation path for admin tools and services.") - ENDIF(WIN32) + IF(WIN32) + SET(RYZOM_SBIN_PREFIX "." CACHE PATH "Installation path for admin tools and services.") + ELSE(WIN32) + SET(RYZOM_SBIN_PREFIX "${RYZOM_PREFIX}/sbin" CACHE PATH "Installation path for admin tools and services.") + ENDIF(WIN32) ENDIF(NOT RYZOM_SBIN_PREFIX) ## Allow override of install_prefix/bin path. IF(NOT RYZOM_BIN_PREFIX) IF(WIN32) - SET(RYZOM_BIN_PREFIX "." CACHE PATH "Installation path for tools and applications.") + SET(RYZOM_BIN_PREFIX "." CACHE PATH "Installation path for tools and applications.") ELSE(WIN32) - SET(RYZOM_BIN_PREFIX "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Installation path for tools.") + SET(RYZOM_BIN_PREFIX "${RYZOM_PREFIX}/bin" CACHE PATH "Installation path for tools.") ENDIF(WIN32) ENDIF(NOT RYZOM_BIN_PREFIX) ## Allow override of install_prefix/games path. IF(NOT RYZOM_GAMES_PREFIX) IF(WIN32) - SET(RYZOM_GAMES_PREFIX "." CACHE PATH "Installation path for tools and applications.") + SET(RYZOM_GAMES_PREFIX "." CACHE PATH "Installation path for tools and applications.") ELSE(WIN32) - SET(RYZOM_GAMES_PREFIX "${CMAKE_INSTALL_PREFIX}/games" CACHE PATH "Installation path for client.") + SET(RYZOM_GAMES_PREFIX "${RYZOM_PREFIX}/games" CACHE PATH "Installation path for client.") ENDIF(WIN32) ENDIF(NOT RYZOM_GAMES_PREFIX) diff --git a/code/nel/CMakeLists.txt b/code/nel/CMakeLists.txt index ead484a2d..57f7edd44 100644 --- a/code/nel/CMakeLists.txt +++ b/code/nel/CMakeLists.txt @@ -54,24 +54,27 @@ IF(WITH_GTK) FIND_PACKAGE(GTK2) ENDIF(WITH_GTK) -IF(UNIX) - SET(prefix ${CMAKE_INSTALL_PREFIX}) - SET(exec_prefix ${CMAKE_INSTALL_PREFIX}/bin) - SET(libdir ${CMAKE_INSTALL_PREFIX}/lib) - SET(includedir ${CMAKE_INSTALL_PREFIX}/include) - SET(enable_ligo ${WITH_LIGO}) - SET(enable_logic ${WITH_LOGIC}) - SET(enable_georges ${WITH_GEORGES}) - SET(enable_net ${WITH_NET}) - SET(enable_3d ${WITH_3D}) - SET(enable_pacs ${WITH_PACS}) - SET(enable_sound ${WITH_SOUND}) - CONFIGURE_FILE(nel-config.in ${CMAKE_CURRENT_BINARY_DIR}/nel-config) +IF(WITH_INSTALL_LIBRARIES) + IF(UNIX) + SET(prefix ${CMAKE_INSTALL_PREFIX}) + SET(exec_prefix ${CMAKE_INSTALL_PREFIX}/bin) + SET(libdir ${CMAKE_INSTALL_PREFIX}/lib) + SET(includedir ${CMAKE_INSTALL_PREFIX}/include) + SET(enable_ligo ${WITH_LIGO}) + SET(enable_logic ${WITH_LOGIC}) + SET(enable_georges ${WITH_GEORGES}) + SET(enable_net ${WITH_NET}) + SET(enable_3d ${WITH_3D}) + SET(enable_pacs ${WITH_PACS}) + SET(enable_sound ${WITH_SOUND}) + CONFIGURE_FILE(nel-config.in ${CMAKE_CURRENT_BINARY_DIR}/nel-config) - INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/nel-config DESTINATION bin) -ENDIF(UNIX) + INSTALL(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/nel-config DESTINATION bin) + ENDIF(UNIX) + + ADD_SUBDIRECTORY(include) +ENDIF(WITH_INSTALL_LIBRARIES) -ADD_SUBDIRECTORY(include) ADD_SUBDIRECTORY(src) IF(WITH_NEL_SAMPLES) diff --git a/code/nel/src/3d/CMakeLists.txt b/code/nel/src/3d/CMakeLists.txt index 3888be211..45b38fcbd 100644 --- a/code/nel/src/3d/CMakeLists.txt +++ b/code/nel/src/3d/CMakeLists.txt @@ -706,6 +706,9 @@ IF(WITH_PCH) ENDIF(WITH_PCH) NL_GEN_PC(nel-3d.pc) -INSTALL(TARGETS nel3d LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) + +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nel3d LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) ADD_SUBDIRECTORY(driver) diff --git a/code/nel/src/3d/driver/direct3d/CMakeLists.txt b/code/nel/src/3d/driver/direct3d/CMakeLists.txt index 5011c3c45..cc61fa9bd 100644 --- a/code/nel/src/3d/driver/direct3d/CMakeLists.txt +++ b/code/nel/src/3d/driver/direct3d/CMakeLists.txt @@ -16,7 +16,9 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(nel_drv_direct3d_win ${CMAKE_CURRENT_SOURCE_DIR}/stddirect3d.h ${CMAKE_CURRENT_SOURCE_DIR}/stddirect3d.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS nel_drv_direct3d_win LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib RUNTIME DESTINATION bin COMPONENT drivers3d) -IF(WITH_MAXPLUGIN) - INSTALL(TARGETS nel_drv_direct3d_win RUNTIME DESTINATION maxplugin COMPONENT drivers3d) -ENDIF(WITH_MAXPLUGIN) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) + INSTALL(TARGETS nel_drv_direct3d_win LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib RUNTIME DESTINATION bin COMPONENT drivers3d) + IF(WITH_MAXPLUGIN) + INSTALL(TARGETS nel_drv_direct3d_win RUNTIME DESTINATION maxplugin COMPONENT drivers3d) + ENDIF(WITH_MAXPLUGIN) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) diff --git a/code/nel/src/3d/driver/opengl/CMakeLists.txt b/code/nel/src/3d/driver/opengl/CMakeLists.txt index 6fdecab71..d90c7e0b3 100644 --- a/code/nel/src/3d/driver/opengl/CMakeLists.txt +++ b/code/nel/src/3d/driver/opengl/CMakeLists.txt @@ -61,7 +61,9 @@ IF(NOT APPLE AND WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(${NLDRV_OGL_LIB} ${CMAKE_CURRENT_SOURCE_DIR}/stdopengl.h ${CMAKE_CURRENT_SOURCE_DIR}/stdopengl.cpp) ENDIF(NOT APPLE AND WITH_PCH) -INSTALL(TARGETS ${NLDRV_OGL_LIB} LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib RUNTIME DESTINATION bin COMPONENT drivers3d) -IF(WITH_MAXPLUGIN) - INSTALL(TARGETS ${NLDRV_OGL_LIB} RUNTIME DESTINATION maxplugin COMPONENT drivers3d) -ENDIF(WITH_MAXPLUGIN) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) + INSTALL(TARGETS ${NLDRV_OGL_LIB} LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib RUNTIME DESTINATION bin COMPONENT drivers3d) + IF(WITH_MAXPLUGIN) + INSTALL(TARGETS ${NLDRV_OGL_LIB} RUNTIME DESTINATION maxplugin COMPONENT drivers3d) + ENDIF(WITH_MAXPLUGIN) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) diff --git a/code/nel/src/cegui/CMakeLists.txt b/code/nel/src/cegui/CMakeLists.txt index 99fcfedf3..342e08780 100644 --- a/code/nel/src/cegui/CMakeLists.txt +++ b/code/nel/src/cegui/CMakeLists.txt @@ -11,4 +11,6 @@ NL_ADD_LIB_SUFFIX(nelceguirenderer) ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} -DNEL_CEGUIRENDERER_EXPORTS) -INSTALL(TARGETS nelceguirenderer RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nelceguirenderer RUNTIME DESTINATION bin LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/nel/src/georges/CMakeLists.txt b/code/nel/src/georges/CMakeLists.txt index d3c0199d7..6954bb87b 100644 --- a/code/nel/src/georges/CMakeLists.txt +++ b/code/nel/src/georges/CMakeLists.txt @@ -21,4 +21,7 @@ IF(WITH_PCH) ENDIF(WITH_PCH) NL_GEN_PC(nel-georges.pc) -INSTALL(TARGETS nelgeorges LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) + +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nelgeorges LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/nel/src/ligo/CMakeLists.txt b/code/nel/src/ligo/CMakeLists.txt index f600637b7..4d8d49f3d 100644 --- a/code/nel/src/ligo/CMakeLists.txt +++ b/code/nel/src/ligo/CMakeLists.txt @@ -19,4 +19,7 @@ IF(WITH_PCH) ENDIF(WITH_PCH) NL_GEN_PC(nel-ligo.pc) -INSTALL(TARGETS nelligo LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) + +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nelligo LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/nel/src/logic/CMakeLists.txt b/code/nel/src/logic/CMakeLists.txt index 107c57850..c84e8d18e 100644 --- a/code/nel/src/logic/CMakeLists.txt +++ b/code/nel/src/logic/CMakeLists.txt @@ -14,4 +14,6 @@ NL_ADD_LIB_SUFFIX(nellogic) ADD_DEFINITIONS(${LIBXML2_DEFINITIONS}) -INSTALL(TARGETS nellogic LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nellogic LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/nel/src/misc/CMakeLists.txt b/code/nel/src/misc/CMakeLists.txt index 1710e5b6b..38d291552 100644 --- a/code/nel/src/misc/CMakeLists.txt +++ b/code/nel/src/misc/CMakeLists.txt @@ -53,4 +53,7 @@ IF(WITH_PCH) ENDIF(WITH_PCH) NL_GEN_PC(nel-misc.pc) -INSTALL(TARGETS nelmisc LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) + +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nelmisc LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/nel/src/net/CMakeLists.txt b/code/nel/src/net/CMakeLists.txt index ea68efe32..c24a94215 100644 --- a/code/nel/src/net/CMakeLists.txt +++ b/code/nel/src/net/CMakeLists.txt @@ -24,4 +24,7 @@ IF(WITH_PCH) ENDIF(WITH_PCH) NL_GEN_PC(nel-net.pc) -INSTALL(TARGETS nelnet LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) + +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nelnet LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/nel/src/pacs/CMakeLists.txt b/code/nel/src/pacs/CMakeLists.txt index 0fa059d68..62a809407 100644 --- a/code/nel/src/pacs/CMakeLists.txt +++ b/code/nel/src/pacs/CMakeLists.txt @@ -19,4 +19,7 @@ IF(WITH_PCH) ENDIF(WITH_PCH) NL_GEN_PC(nel-pacs.pc) -INSTALL(TARGETS nelpacs LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) + +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nelpacs LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/nel/src/sound/CMakeLists.txt b/code/nel/src/sound/CMakeLists.txt index 9497357ee..21c1de1f8 100644 --- a/code/nel/src/sound/CMakeLists.txt +++ b/code/nel/src/sound/CMakeLists.txt @@ -19,6 +19,9 @@ IF(WITH_PCH) ENDIF(WITH_PCH) NL_GEN_PC(nel-sound.pc) -INSTALL(TARGETS nelsound LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) + +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nelsound LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) ADD_SUBDIRECTORY(driver) diff --git a/code/nel/src/sound/driver/CMakeLists.txt b/code/nel/src/sound/driver/CMakeLists.txt index c2ae80606..1a8391c41 100644 --- a/code/nel/src/sound/driver/CMakeLists.txt +++ b/code/nel/src/sound/driver/CMakeLists.txt @@ -23,7 +23,9 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(nelsnd_lowlevel ${CMAKE_CURRENT_SOURCE_DIR}/stdsound_lowlevel.h ${CMAKE_CURRENT_SOURCE_DIR}/stdsound_lowlevel.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS nelsnd_lowlevel LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS nelsnd_lowlevel LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) IF(WITH_DRIVER_OPENAL) ADD_SUBDIRECTORY(openal) diff --git a/code/nel/src/sound/driver/dsound/CMakeLists.txt b/code/nel/src/sound/driver/dsound/CMakeLists.txt index f2d13d785..c2fc934dc 100644 --- a/code/nel/src/sound/driver/dsound/CMakeLists.txt +++ b/code/nel/src/sound/driver/dsound/CMakeLists.txt @@ -13,7 +13,9 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(nel_drv_dsound_win ${CMAKE_CURRENT_SOURCE_DIR}/stddsound.h ${CMAKE_CURRENT_SOURCE_DIR}/stddsound.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS nel_drv_dsound_win RUNTIME DESTINATION bin LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib COMPONENT driverssound) -IF(WITH_MAXPLUGIN) - INSTALL(TARGETS nel_drv_dsound_win RUNTIME DESTINATION maxplugin COMPONENT driverssound) -ENDIF(WITH_MAXPLUGIN) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) + INSTALL(TARGETS nel_drv_dsound_win RUNTIME DESTINATION bin LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib COMPONENT driverssound) + IF(WITH_MAXPLUGIN) + INSTALL(TARGETS nel_drv_dsound_win RUNTIME DESTINATION maxplugin COMPONENT driverssound) + ENDIF(WITH_MAXPLUGIN) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) diff --git a/code/nel/src/sound/driver/fmod/CMakeLists.txt b/code/nel/src/sound/driver/fmod/CMakeLists.txt index 049d8b627..e641a9277 100644 --- a/code/nel/src/sound/driver/fmod/CMakeLists.txt +++ b/code/nel/src/sound/driver/fmod/CMakeLists.txt @@ -13,7 +13,9 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(nel_drv_fmod_win ${CMAKE_CURRENT_SOURCE_DIR}/stdfmod.h ${CMAKE_CURRENT_SOURCE_DIR}/stdfmod.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS nel_drv_fmod_win RUNTIME DESTINATION bin LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib COMPONENT driverssound) -IF(WITH_MAXPLUGIN) - INSTALL(TARGETS nel_drv_fmod_win RUNTIME DESTINATION maxplugin COMPONENT driverssound) -ENDIF(WITH_MAXPLUGIN) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) + INSTALL(TARGETS nel_drv_fmod_win RUNTIME DESTINATION bin LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib COMPONENT driverssound) + IF(WITH_MAXPLUGIN) + INSTALL(TARGETS nel_drv_fmod_win RUNTIME DESTINATION maxplugin COMPONENT driverssound) + ENDIF(WITH_MAXPLUGIN) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) diff --git a/code/nel/src/sound/driver/openal/CMakeLists.txt b/code/nel/src/sound/driver/openal/CMakeLists.txt index 53b0c8c2a..47ce5b24f 100644 --- a/code/nel/src/sound/driver/openal/CMakeLists.txt +++ b/code/nel/src/sound/driver/openal/CMakeLists.txt @@ -44,7 +44,9 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(${NLDRV_AL_LIB} ${CMAKE_CURRENT_SOURCE_DIR}/stdopenal.h ${CMAKE_CURRENT_SOURCE_DIR}/stdopenal.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS ${NLDRV_AL_LIB} RUNTIME DESTINATION bin LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib COMPONENT driverssound) -IF(WITH_MAXPLUGIN) - INSTALL(TARGETS ${NLDRV_AL_LIB} RUNTIME DESTINATION maxplugin COMPONENT driverssound) -ENDIF(WITH_MAXPLUGIN) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) + INSTALL(TARGETS ${NLDRV_AL_LIB} RUNTIME DESTINATION ${NL_DRIVER_PREFIX} LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib COMPONENT driverssound) + IF(WITH_MAXPLUGIN) + INSTALL(TARGETS ${NLDRV_AL_LIB} RUNTIME DESTINATION maxplugin COMPONENT driverssound) + ENDIF(WITH_MAXPLUGIN) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) diff --git a/code/nel/src/sound/driver/xaudio2/CMakeLists.txt b/code/nel/src/sound/driver/xaudio2/CMakeLists.txt index 5e45a9809..eb342a4df 100644 --- a/code/nel/src/sound/driver/xaudio2/CMakeLists.txt +++ b/code/nel/src/sound/driver/xaudio2/CMakeLists.txt @@ -40,7 +40,9 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(nel_drv_xaudio2_win ${CMAKE_CURRENT_SOURCE_DIR}/stdxaudio2.h ${CMAKE_CURRENT_SOURCE_DIR}/stdxaudio2.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS nel_drv_xaudio2_win RUNTIME DESTINATION bin LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib COMPONENT driverssound) -IF(WITH_MAXPLUGIN) - INSTALL(TARGETS nel_drv_xaudio2_win RUNTIME DESTINATION maxplugin COMPONENT driverssound) -ENDIF(WITH_MAXPLUGIN) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) + INSTALL(TARGETS nel_drv_xaudio2_win RUNTIME DESTINATION bin LIBRARY DESTINATION ${NL_DRIVER_PREFIX} ARCHIVE DESTINATION lib COMPONENT driverssound) + IF(WITH_MAXPLUGIN) + INSTALL(TARGETS nel_drv_xaudio2_win RUNTIME DESTINATION maxplugin COMPONENT driverssound) + ENDIF(WITH_MAXPLUGIN) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC_DRIVERS) OR NOT WITH_STATIC_DRIVERS) diff --git a/code/ryzom/client/CMakeLists.txt b/code/ryzom/client/CMakeLists.txt index db2ffce2a..36090be7b 100644 --- a/code/ryzom/client/CMakeLists.txt +++ b/code/ryzom/client/CMakeLists.txt @@ -1,6 +1,6 @@ ADD_SUBDIRECTORY(src) -ADD_SUBDIRECTORY(data) -ADD_SUBDIRECTORY(patcher) +#ADD_SUBDIRECTORY(data) +#ADD_SUBDIRECTORY(patcher) IF(UNIX AND NOT APPLE) ADD_SUBDIRECTORY(unix) diff --git a/code/ryzom/client/src/CMakeLists.txt b/code/ryzom/client/src/CMakeLists.txt index 84ea6a369..3971fb98f 100644 --- a/code/ryzom/client/src/CMakeLists.txt +++ b/code/ryzom/client/src/CMakeLists.txt @@ -94,12 +94,12 @@ TARGET_LINK_LIBRARIES(ryzom_client ) IF(NOT APPLE AND NOT WIN32) - TARGET_LINK_LIBRARIES(ryzom_client ${X11_LIBRARIES}) + TARGET_LINK_LIBRARIES(ryzom_client ${X11_LIBRARIES}) ENDIF(NOT APPLE AND NOT WIN32) IF(APPLE) - FIND_LIBRARY(FOUNDATION_LIBRARY Foundation) - TARGET_LINK_LIBRARIES(ryzom_client ${FOUNDATION_LIBRARY}) + FIND_LIBRARY(FOUNDATION_LIBRARY Foundation) + TARGET_LINK_LIBRARIES(ryzom_client ${FOUNDATION_LIBRARY}) ENDIF(APPLE) ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} ${CURL_DEFINITIONS} ${LUABIND_DEFINITIONS}) @@ -113,4 +113,4 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(ryzom_client ${CMAKE_CURRENT_SOURCE_DIR}/stdpch.h ${CMAKE_CURRENT_SOURCE_DIR}/stdpch.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS ryzom_client RUNTIME DESTINATION games COMPONENT client BUNDLE DESTINATION /Applications) +INSTALL(TARGETS ryzom_client RUNTIME DESTINATION ${RYZOM_GAMES_PREFIX} COMPONENT client BUNDLE DESTINATION /Applications) diff --git a/code/ryzom/client/src/client_sheets/CMakeLists.txt b/code/ryzom/client/src/client_sheets/CMakeLists.txt index cc02e3c64..4f21ecf9d 100644 --- a/code/ryzom/client/src/client_sheets/CMakeLists.txt +++ b/code/ryzom/client/src/client_sheets/CMakeLists.txt @@ -13,4 +13,6 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(ryzom_clientsheets ${CMAKE_CURRENT_SOURCE_DIR}/stdpch.h ${CMAKE_CURRENT_SOURCE_DIR}/stdpch.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS ryzom_clientsheets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS ryzom_clientsheets LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/ryzom/client/src/seven_zip/CMakeLists.txt b/code/ryzom/client/src/seven_zip/CMakeLists.txt index 80bd946a6..85212084e 100644 --- a/code/ryzom/client/src/seven_zip/CMakeLists.txt +++ b/code/ryzom/client/src/seven_zip/CMakeLists.txt @@ -19,5 +19,7 @@ NL_ADD_LIB_SUFFIX(ryzom_sevenzip) ADD_DEFINITIONS(-D_SZ_ONE_DIRECTORY) -INSTALL(TARGETS ryzom_sevenzip LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) -INSTALL(TARGETS 7zDec RUNTIME DESTINATION bin COMPONENT client) +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS ryzom_sevenzip LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) + INSTALL(TARGETS 7zDec RUNTIME DESTINATION bin COMPONENT client) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) diff --git a/code/ryzom/common/src/game_share/CMakeLists.txt b/code/ryzom/common/src/game_share/CMakeLists.txt index 7883ce03d..6342f468d 100644 --- a/code/ryzom/common/src/game_share/CMakeLists.txt +++ b/code/ryzom/common/src/game_share/CMakeLists.txt @@ -22,5 +22,6 @@ IF(WITH_PCH) ADD_NATIVE_PRECOMPILED_HEADER(ryzom_gameshare ${CMAKE_CURRENT_SOURCE_DIR}/stdpch.h ${CMAKE_CURRENT_SOURCE_DIR}/stdpch.cpp) ENDIF(WITH_PCH) -INSTALL(TARGETS ryzom_gameshare LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) - +IF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) + INSTALL(TARGETS ryzom_gameshare LIBRARY DESTINATION lib ARCHIVE DESTINATION lib COMPONENT libraries) +ENDIF((WITH_INSTALL_LIBRARIES AND WITH_STATIC) OR NOT WITH_STATIC) From 54bfe9aff42f75a073f667db6b92290e6e425c9c Mon Sep 17 00:00:00 2001 From: kervala Date: Sun, 11 Sep 2011 16:41:16 +0200 Subject: [PATCH 099/215] Changed: Replaced 32x32 pixmap by a 48x48 one --- code/ryzom/client/unix/ryzom.xpm | 1930 ++++++++++++++++++++---------- 1 file changed, 1311 insertions(+), 619 deletions(-) diff --git a/code/ryzom/client/unix/ryzom.xpm b/code/ryzom/client/unix/ryzom.xpm index 43748c2ce..5e6ef89df 100644 --- a/code/ryzom/client/unix/ryzom.xpm +++ b/code/ryzom/client/unix/ryzom.xpm @@ -1,621 +1,1313 @@ /* XPM */ -static char * ryzom_32x32_xpm[] = { -"32 32 586 2", +static char * ryzom_48x48_xpm[] = { +"48 48 1262 2", " c None", -". c #CACCCA", -"+ c #737766", -"@ c #747674", -"# c #717175", -"$ c #6E6E71", -"% c #8A9293", -"& c #888477", -"* c #63605B", -"= c #83856E", -"- c #888C7E", -"; c #666D60", -"> c #4A483C", -", c #433729", -"' c #8A816C", -") c #48453A", -"! c #3D3A32", -"~ c #6B6A61", -"{ c #8A8B8A", -"] c #989592", -"^ c #948F87", -"/ c #7E7861", -"( c #59513F", -"_ c #65604F", -": c #918F86", -"< c #5F5E45", -"[ c #4B5735", -"} c #3F462F", -"| c #443F31", -"1 c #49483C", -"2 c #7F7B6F", -"3 c #6E6049", -"4 c #696C6D", -"5 c #483C2F", -"6 c #403623", -"7 c #5B4C3B", -"8 c #514237", -"9 c #66553A", -"0 c #645B3E", -"a c #878B79", -"b c #93948B", -"c c #605C3B", -"d c #4D5032", -"e c #373117", -"f c #48512C", -"g c #52633A", -"h c #2B2818", -"i c #664E32", -"j c #37341B", -"k c #636156", -"l c #737769", -"m c #454935", -"n c #3F4430", -"o c #666637", -"p c #5E712F", -"q c #535435", -"r c #7D7E6A", -"s c #717267", -"t c #6E6B6A", -"u c #545B46", -"v c #6D715E", -"w c #767960", -"x c #575736", -"y c #42512E", -"z c #303828", -"A c #3E3C22", -"B c #4D4B1E", -"C c #323916", -"D c #565137", -"E c #4A3D26", -"F c #3E3E24", -"G c #746E37", -"H c #5F5B2F", -"I c #232A10", -"J c #1F2513", -"K c #5B5B3C", -"L c #807B63", -"M c #625C4B", -"N c #6B675B", -"O c #90968D", -"P c #7B7E7B", -"Q c #7C8380", -"R c #4B503E", -"S c #1D2212", -"T c #4D4F31", -"U c #494524", -"V c #7D9442", -"W c #4F6429", -"X c #57512D", -"Y c #4F3C1F", -"Z c #423F1D", -"` c #5F6A35", -" . c #3D481F", -".. c #404024", -"+. c #5F512B", -"@. c #3F381E", -"#. c #6E6C3F", -"$. c #717448", -"%. c #646C48", -"&. c #B4ADA3", -"*. c #E5E6E4", -"=. c #BEBEBE", -"-. c #444737", -";. c #2D311B", -">. c #222911", -",. c #1D220E", -"'. c #231E09", -"). c #3C3922", -"!. c #4C5324", -"~. c #4E4D27", -"{. c #3F3420", -"]. c #3C401F", -"^. c #4E552A", -"/. c #5C703B", -"(. c #778649", -"_. c #43442C", -":. c #736039", -"<. c #897648", -"[. c #847644", -"}. c #635F2D", -"|. c #525B31", -"1. c #647444", -"2. c #504E43", -"3. c #7F7774", -"4. c #8B8788", -"5. c #969591", -"6. c #4B542F", -"7. c #5C6041", -"8. c #5D513D", -"9. c #474638", -"0. c #4B442E", -"a. c #859F4A", -"b. c #373F1C", -"c. c #24260C", -"d. c #161407", -"e. c #2C2D14", -"f. c #3C4420", -"g. c #38401D", -"h. c #657B43", -"i. c #70804E", -"j. c #595C35", -"k. c #706B3A", -"l. c #66562B", -"m. c #3C4120", -"n. c #383B20", -"o. c #444B2E", -"p. c #75766C", -"q. c #DEDBD8", -"r. c #A49E98", -"s. c #7A726F", -"t. c #ADAFAA", -"u. c #9CA098", -"v. c #767164", -"w. c #402815", -"x. c #46362F", -"y. c #47341D", -"z. c #484A2B", -"A. c #576B2A", -"B. c #252210", -"C. c #3F3B17", -"D. c #4A2B12", -"E. c #442812", -"F. c #58582B", -"G. c #555F32", -"H. c #5C6433", -"I. c #48532E", -"J. c #636D42", -"K. c #574E30", -"L. c #524C2B", -"M. c #69623A", -"N. c #888661", -"O. c #CECDBF", -"P. c #E3DFDC", -"Q. c #C9C3C3", -"R. c #898481", -"S. c #5D574D", -"T. c #797271", -"U. c #8D8987", -"V. c #484638", -"W. c #534830", -"X. c #4A4C48", -"Y. c #5E4327", -"Z. c #252108", -"`. c #424030", -" + c #675F3D", -".+ c #32100A", -"++ c #2F180F", -"@+ c #887F5A", -"#+ c #817950", -"$+ c #615F39", -"%+ c #2C2D18", -"&+ c #2A2113", -"*+ c #695534", -"=+ c #947E52", -"-+ c #B8A37E", -";+ c #C3B29E", -">+ c #ACA09D", -",+ c #A09795", -"'+ c #BCBABA", -")+ c #857F7C", -"!+ c #544D3D", -"~+ c #30362B", -"{+ c #59564A", -"]+ c #A2A8A6", -"^+ c #62645A", -"/+ c #535453", -"(+ c #474534", -"_+ c #3D3C1F", -":+ c #575033", -"<+ c #776D51", -"[+ c #ACB2AA", -"}+ c #8A9080", -"|+ c #5A432F", -"1+ c #8B7B65", -"2+ c #746D50", -"3+ c #5F502C", -"4+ c #484224", -"5+ c #414122", -"6+ c #75673B", -"7+ c #9E8149", -"8+ c #BDB7A9", -"9+ c #E5E4E1", -"0+ c #EAE7E2", -"a+ c #9C9491", -"b+ c #ADABA6", -"c+ c #A0A3A0", -"d+ c #A8AFAE", -"e+ c #5C5F51", -"f+ c #5E5D32", -"g+ c #525431", -"h+ c #43382B", -"i+ c #453E34", -"j+ c #41412F", -"k+ c #5D6454", -"l+ c #464941", -"m+ c #60624D", -"n+ c #494644", -"o+ c #39382E", -"p+ c #444630", -"q+ c #584F2D", -"r+ c #444823", -"s+ c #30381B", -"t+ c #49562C", -"u+ c #7E8D54", -"v+ c #939472", -"w+ c #7F7F60", -"x+ c #7C8259", -"y+ c #373727", -"z+ c #3F3834", -"A+ c #85817E", -"B+ c #6D6D66", -"C+ c #666765", -"D+ c #352E27", -"E+ c #49493C", -"F+ c #969985", -"G+ c #A39D98", -"H+ c #928E81", -"I+ c #8C9892", -"J+ c #7E8B8A", -"K+ c #444130", -"L+ c #343021", -"M+ c #2D2D19", -"N+ c #28301C", -"O+ c #5E6257", -"P+ c #596044", -"Q+ c #3F4523", -"R+ c #384123", -"S+ c #65724F", -"T+ c #889176", -"U+ c #E6E6DF", -"V+ c #C2C0BD", -"W+ c #827B6D", -"X+ c #656551", -"Y+ c #3C3B28", -"Z+ c #524F43", -"`+ c #6A644D", -" @ c #655A42", -".@ c #7C735E", -"+@ c #9F9F8D", -"@@ c #B2B5B0", -"#@ c #C9D0CD", -"$@ c #8F9490", -"%@ c #3F4037", -"&@ c #4F504A", -"*@ c #292420", -"=@ c #2F2F19", -"-@ c #2C2710", -";@ c #252514", -">@ c #3F412A", -",@ c #5B6D68", -"'@ c #4D4C3D", -")@ c #282F1A", -"!@ c #526038", -"~@ c #49512D", -"{@ c #444B2D", -"]@ c #7F7B76", -"^@ c #5B5453", -"/@ c #535137", -"(@ c #525535", -"_@ c #565136", -":@ c #48432A", -"<@ c #483F23", -"[@ c #635636", -"}@ c #777664", -"|@ c #D5DBDA", -"1@ c #BECFD0", -"2@ c #A8BCC1", -"3@ c #525553", -"4@ c #4E4432", -"5@ c #4B402D", -"6@ c #403F26", -"7@ c #3B4A23", -"8@ c #323517", -"9@ c #454936", -"0@ c #30330E", -"a@ c #424513", -"b@ c #444F29", -"c@ c #434629", -"d@ c #746D5B", -"e@ c #585532", -"f@ c #4F552C", -"g@ c #677E43", -"h@ c #5A6E3D", -"i@ c #30361E", -"j@ c #424521", -"k@ c #5F5831", -"l@ c #3C3D1D", -"m@ c #414024", -"n@ c #3D391A", -"o@ c #413719", -"p@ c #5E593E", -"q@ c #929D9E", -"r@ c #77868D", -"s@ c #454C50", -"t@ c #2F2F2B", -"u@ c #4A3D1A", -"v@ c #3C451C", -"w@ c #313B19", -"x@ c #30401D", -"y@ c #23270F", -"z@ c #363617", -"A@ c #261605", -"B@ c #43360D", -"C@ c #5C5D29", -"D@ c #8F8D6B", -"E@ c #7F7563", -"F@ c #65512D", -"G@ c #433E23", -"H@ c #546137", -"I@ c #485332", -"J@ c #454D45", -"K@ c #60644A", -"L@ c #666C36", -"M@ c #57462C", -"N@ c #634E2A", -"O@ c #5D4A25", -"P@ c #4A3819", -"Q@ c #312513", -"R@ c #2B1F15", -"S@ c #1F2119", -"T@ c #16170F", -"U@ c #37391C", -"V@ c #373216", -"W@ c #313718", -"X@ c #1F2B11", -"Y@ c #111208", -"Z@ c #566631", -"`@ c #493915", -" # c #4A4314", -".# c #47501C", -"+# c #4A5A23", -"@# c #7B806A", -"## c #757260", -"$# c #3B3E23", -"%# c #3C4124", -"&# c #252314", -"*# c #3A413E", -"=# c #405B52", -"-# c #3E4331", -";# c #4C462A", -"># c #5E4C29", -",# c #654E26", -"'# c #634C2A", -")# c #583E1E", -"!# c #2C1D0E", -"~# c #3E2C17", -"{# c #42230F", -"]# c #443216", -"^# c #423817", -"/# c #1C200E", -"(# c #161605", -"_# c #394328", -":# c #232212", -"<# c #423E17", -"[# c #26370C", -"}# c #30371A", -"|# c #5A6B38", -"1# c #6E7364", -"2# c #5C6251", -"3# c #3D3E2D", -"4# c #4A473C", -"5# c #71664C", -"6# c #775E33", -"7# c #463921", -"8# c #574326", -"9# c #514528", -"0# c #524C2D", -"a# c #606139", -"b# c #4C502A", -"c# c #4B4923", -"d# c #3F351E", -"e# c #4F3F1E", -"f# c #53532B", -"g# c #202A13", -"h# c #2A2C16", -"i# c #2B2813", -"j# c #343525", -"k# c #32311A", -"l# c #3B3B1C", -"m# c #3F3214", -"n# c #3F3B19", -"o# c #333D1B", -"p# c #2B3215", -"q# c #272B27", -"r# c #636E75", -"s# c #766856", -"t# c #6D6B3A", -"u# c #5D6D38", -"v# c #565E35", -"w# c #56663A", -"x# c #504F2F", -"y# c #495331", -"z# c #404528", -"A# c #50542E", -"B# c #607239", -"C# c #4B5D2F", -"D# c #364A22", -"E# c #26301C", -"F# c #171C12", -"G# c #222417", -"H# c #353D24", -"I# c #666C6B", -"J# c #3A3216", -"K# c #4B4B2D", -"L# c #8D9384", -"M# c #8A9379", -"N# c #353C21", -"O# c #363820", -"P# c #3A3728", -"Q# c #434233", -"R# c #515034", -"S# c #4C5734", -"T# c #3C4929", -"U# c #353B26", -"V# c #3B3A24", -"W# c #575032", -"X# c #555C35", -"Y# c #495531", -"Z# c #4F6034", -"`# c #2F391F", -" $ c #2D3A20", -".$ c #353E30", -"+$ c #1C2417", -"@$ c #1D1D14", -"#$ c #2D311D", -"$$ c #333E2F", -"%$ c #585842", -"&$ c #34372B", -"*$ c #889191", -"=$ c #919E91", -"-$ c #4D5C34", -";$ c #565C4A", -">$ c #647171", -",$ c #595F52", -"'$ c #6E6E5F", -")$ c #403E34", -"!$ c #363125", -"~$ c #594C38", -"{$ c #655938", -"]$ c #3E4527", -"^$ c #343825", -"/$ c #404D2B", -"($ c #2D3521", -"_$ c #1A1B12", -":$ c #7D8988", -"<$ c #333936", -"[$ c #2B2D29", -"}$ c #262C1B", -"|$ c #3D3E34", -"1$ c #4F5A4C", -"2$ c #94A0A3", -"3$ c #A3B0B1", -"4$ c #454840", -"5$ c #3F3D2E", -"6$ c #6D6F64", -"7$ c #666150", -"8$ c #554F33", -"9$ c #666242", -"0$ c #5D553C", -"a$ c #4C503C", -"b$ c #545B41", -"c$ c #3B3D32", -"d$ c #8B928E", -"e$ c #25261B", -"f$ c #6D7877", -"g$ c #4F5D63", -"h$ c #343731", -"i$ c #555D58", -"j$ c #566259", -"k$ c #4E4B3E", -"l$ c #3F4232", -"m$ c #616A68", -"n$ c #252620", -"o$ c #383D30", -"p$ c #6A7366", -"q$ c #707569", -"r$ c #4F5443", -"s$ c #485038", -"t$ c #505641", -"u$ c #6F7861", -"v$ c #95A094", -"w$ c #8B958F", -"x$ c #676F67", -"y$ c #7E8582", -"z$ c #474B4A", -"A$ c #414137", -"B$ c #5F6B6A", -"C$ c #52626A", -"D$ c #393E3A", -"E$ c #5A6363", -"F$ c #484C44", -"G$ c #424A3E", -"H$ c #535C53", -"I$ c #474738", -"J$ c #5C5945", -"K$ c #7F8173", -"L$ c #939A95", -"M$ c #929C9C", -"N$ c #9EABAD", -"O$ c #A5B4B7", -"P$ c #89959A", -"Q$ c #3E443E", -"R$ c #3C463B", -"S$ c #485450", -"T$ c #53626A", -"U$ c #5C6B6C", -"V$ c #515F5E", -"W$ c #65767B", -"X$ c #899DA4", -"Y$ c #88999F", -"Z$ c #8E9A9B", -"`$ c #828B88", -" % c #696F66", -".% c #7F8A8A", -"+% c #7E8788", -"@% c #849196", -"#% c #899AA0", -"$% c #646F75", -"%% c #3C413F", -"&% c #252925", -"*% c #647074", -"=% c #36464E", -"-% c #505F67", -";% c #606F76", -">% c #55646E", -",% c #687B85", -"'% c #748890", -")% c #75878D", -"!% c #6F7F82", -"~% c #75858A", -"{% c #798D95", -"]% c #6C7E85", -"^% c #5E6F77", -"/% c #4D5A62", -"(% c #48555E", -"_% c #4B5C65", -":% c #50606A", -"<% c #5B6E7A", -"[% c #5D707B", -"}% c #5D6F79", -"|% c #5C6D77", -"1% c #5D6D78", -"2% c #5B6974", -"3% c #4F5C68", -"4% c #51606C", -"5% c #515E6A", -" ", -" ", -" . ", -" + @ # $ % & * = - ; > , ", -" ' ) ! ~ { ] ^ / ( _ : < [ } | ", -" 1 2 3 4 5 6 7 8 9 0 a b c d e f g ", -" h i j k l m n o p q r s t u v w x y z ", -" A B C D E F o G H I J K L M N O P Q R S ", -" T U V W X Y Z ` ...+.@.#.$.%.&.*.=.-.;.>.,.'. ", -" ).!.~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.8.9.0. ", -" a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z. ", -" A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y.Z. ", -" e.`. +.+++@+#+$+%+&+*+=+-+;+>+,+'+)+!+~+{+]+^+/+(+_+ ", -" :+<+[+}+|+1+2+3+4+5+6+7+8+9+0+a+b+c+d+e+f+g+h+i+j+k+l+ ", -" m+n+o+p+q+r+s+t+u+v+w+x+y+z+A+B+C+D+E+F+G+H+I+J+K+L+ ", -" M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @.@+@@@#@$@%@&@*@=@-@ ", -" ;@>@,@'@)@!@~@{@]@^@/@(@_@:@<@[@}@|@1@2@3@4@5@6@7@8@9@ ", -" 0@a@b@c@d@e@f@g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@ ", -" z@A@B@C@D@E@F@G@H@I@J@K@L@M@N@O@P@Q@R@S@T@U@V@W@X@Y@ ", -" Z@`@ #.#+#@###$#%#&#*#=#-#;#>#,#'#y.)#!#~#{#]#^#/#(# ", -" _#:#<#[#}#|#1#2#3#4#5#6#7#8#9#0#a#b#c#d#e#f#g#h#i#j# ", -" k#l#m#n#o#p#q#r#s#t#u#v#w#x#y#z#A#B#C#D#E#F#G#H#I# ", -" J#K#L#M#N#O#P#Q#R#S#T#U#V#W#X#Y#Z#`# $.$+$@$#$$$ ", -" %$&$*$=$-$;$>$,$'$)$!$~${$]$^$/$($_$:$<$[$}$ ", -" |$1$2$3$4$5$6$7$8$9$0$a$b$c$d$! e$f$g$h$i$ ", -" j$k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$z$A$B$C$ ", -" D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$ ", -" U$V$W$X$Y$Z$`$ %.%+%@%#%$%%%&%*% ", -" =%-%;%>%,%'%)%!%~%{%]%^%/% ", -" (%_%:%<%[%}%|%1%2% ", -" 3%4% 5% ", -" "}; +". c #AAB2A8", +"+ c #A6ABAF", +"@ c #79868A", +"# c #6A7B81", +"$ c #A6ABAA", +"% c #ADB0AF", +"& c #AAAFA9", +"* c #C8D2CE", +"= c #838679", +"- c #5F6159", +"; c #656766", +"> c #636365", +", c #504E50", +"' c #646464", +") c #7E8180", +"! c #9B988B", +"~ c #6D695B", +"{ c #636564", +"] c #535843", +"^ c #686A54", +"/ c #7E7D70", +"( c #717262", +"_ c #556056", +": c #47493B", +"< c #423E30", +"[ c #413629", +"} c #323229", +"| c #424039", +"1 c #5E5D56", +"2 c #656560", +"3 c #A8AAA7", +"4 c #989795", +"5 c #76726D", +"6 c #888074", +"7 c #807256", +"8 c #675B47", +"9 c #4B442E", +"0 c #808076", +"a c #8C8A7D", +"b c #817962", +"c c #4F4F36", +"d c #4A5535", +"e c #515E3E", +"f c #434736", +"g c #4C483F", +"h c #6E634E", +"i c #434233", +"j c #4A4943", +"k c #585347", +"l c #797367", +"m c #938B85", +"n c #908781", +"o c #746860", +"p c #605349", +"q c #897C6E", +"r c #958C81", +"s c #746953", +"t c #6C5E46", +"u c #665B43", +"v c #756852", +"w c #84847B", +"x c #AEB2AD", +"y c #737661", +"z c #49492E", +"A c #3A361F", +"B c #33371D", +"C c #34381F", +"D c #3D4028", +"E c #475131", +"F c #524F45", +"G c #7C7F76", +"H c #7D725E", +"I c #7E6847", +"J c #51534E", +"K c #4F5B5F", +"L c #3A3833", +"M c #332B18", +"N c #37331D", +"O c #51432D", +"P c #393326", +"Q c #423728", +"R c #55482E", +"S c #53482F", +"T c #565436", +"U c #727660", +"V c #888B7F", +"W c #88897C", +"X c #6E714C", +"Y c #6B724A", +"Z c #555B3A", +"` c #4B3D1D", +" . c #363119", +".. c #4B572D", +"+. c #536838", +"@. c #4C5740", +"#. c #4A3B24", +"$. c #795F3F", +"%. c #604D32", +"&. c #2E2D18", +"*. c #413C2D", +"=. c #808276", +"-. c #535C54", +";. c #474D43", +">. c #3C3E37", +",. c #353A30", +"'. c #807246", +"). c #4B5329", +"!. c #606F31", +"~. c #525730", +"{. c #73735E", +"]. c #8E9080", +"^. c #87887D", +"/. c #B7B6B6", +"(. c #858281", +"_. c #515941", +":. c #707C5F", +"<. c #656754", +"[. c #626A4B", +"}. c #645E3E", +"|. c #3C4023", +"1. c #44512E", +"2. c #38462C", +"3. c #21230E", +"4. c #29270D", +"5. c #382E14", +"6. c #39341B", +"7. c #524C33", +"8. c #53524A", +"9. c #645F55", +"0. c #48452C", +"a. c #464521", +"b. c #5D522D", +"c. c #716F3B", +"d. c #5C642A", +"e. c #51602B", +"f. c #37481B", +"g. c #33381E", +"h. c #5C5B3B", +"i. c #514D2F", +"j. c #4D462D", +"k. c #3F3A30", +"l. c #35352B", +"m. c #4C4C43", +"n. c #5B5B4A", +"o. c #91978B", +"p. c #9CA19B", +"q. c #B0B7AB", +"r. c #898D77", +"s. c #51593C", +"t. c #2F3B22", +"u. c #919162", +"v. c #464529", +"w. c #5F6031", +"x. c #545925", +"y. c #464E1F", +"z. c #373E23", +"A. c #585235", +"B. c #664F2F", +"C. c #3E3A23", +"D. c #595D35", +"E. c #666D38", +"F. c #75743D", +"G. c #636931", +"H. c #6E6B39", +"I. c #3D371C", +"J. c #222711", +"K. c #161A0E", +"L. c #353A24", +"M. c #6B7054", +"N. c #7D7F67", +"O. c #978C75", +"P. c #696450", +"Q. c #78786D", +"R. c #89887D", +"S. c #929591", +"T. c #52534E", +"U. c #5D5F5D", +"V. c #4B4E48", +"W. c #45493C", +"X. c #3F4533", +"Y. c #181C0D", +"Z. c #53512E", +"`. c #7D9042", +" + c #74903B", +".+ c #475525", +"++ c #4A5428", +"@+ c #534528", +"#+ c #513B1E", +"$+ c #554825", +"%+ c #494E26", +"&+ c #6E833A", +"*+ c #576B2A", +"=+ c #353E1D", +"-+ c #383D20", +";+ c #6F5B35", +">+ c #303017", +",+ c #2E2C17", +"'+ c #454426", +")+ c #737A49", +"!+ c #6D6F4B", +"~+ c #5A6140", +"{+ c #727260", +"]+ c #AFA799", +"^+ c #DDDFDA", +"/+ c #ECEDEC", +"(+ c #8F8F8D", +"_+ c #3C3E34", +":+ c #323623", +"<+ c #212415", +"[+ c #212712", +"}+ c #1C220F", +"|+ c #252310", +"1+ c #231E0C", +"2+ c #2F3317", +"3+ c #5C632E", +"4+ c #5E6C33", +"5+ c #465A22", +"6+ c #3D4022", +"7+ c #453921", +"8+ c #433E1D", +"9+ c #615C31", +"0+ c #3F471F", +"a+ c #545831", +"b+ c #586136", +"c+ c #29281A", +"d+ c #322F1B", +"e+ c #6B5836", +"f+ c #706134", +"g+ c #7F6B41", +"h+ c #83693F", +"i+ c #8F7846", +"j+ c #5C5B2B", +"k+ c #76804B", +"l+ c #6F7D47", +"m+ c #8C9178", +"n+ c #999188", +"o+ c #B0AAA8", +"p+ c #C7C6C6", +"q+ c #A29FA0", +"r+ c #9C9D95", +"s+ c #686E53", +"t+ c #414924", +"u+ c #373E1E", +"v+ c #303721", +"w+ c #343928", +"x+ c #212314", +"y+ c #363220", +"z+ c #2D3215", +"A+ c #333515", +"B+ c #5C4F2D", +"C+ c #5F5232", +"D+ c #2D2715", +"E+ c #31381A", +"F+ c #55592F", +"G+ c #4C552A", +"H+ c #677B43", +"I+ c #799250", +"J+ c #74864A", +"K+ c #656F46", +"L+ c #635637", +"M+ c #79683A", +"N+ c #968251", +"O+ c #665B33", +"P+ c #7D7840", +"Q+ c #616A32", +"R+ c #4C4F29", +"S+ c #4C562E", +"T+ c #556539", +"U+ c #595F44", +"V+ c #4D4D44", +"W+ c #6A6561", +"X+ c #95908F", +"Y+ c #74706F", +"Z+ c #797573", +"`+ c #595B4C", +" @ c #4E5532", +".@ c #5D603D", +"+@ c #797059", +"@@ c #655136", +"#@ c #5B5342", +"$@ c #504E3F", +"%@ c #3D461C", +"&@ c #21270E", +"*@ c #24280D", +"=@ c #141005", +"-@ c #191909", +";@ c #3D3A1C", +">@ c #454425", +",@ c #425024", +"'@ c #323C1B", +")@ c #506232", +"!@ c #7F9A5B", +"~@ c #75804F", +"{@ c #727F46", +"]@ c #635E36", +"^@ c #837443", +"/@ c #70562B", +"(@ c #505129", +"_@ c #454923", +":@ c #2E3019", +"<@ c #353920", +"[@ c #353B26", +"}@ c #404236", +"|@ c #98978C", +"1@ c #D6D4D2", +"2@ c #AFADA7", +"3@ c #8F8784", +"4@ c #7C7875", +"5@ c #A2A39D", +"6@ c #7E7D77", +"7@ c #868B7F", +"8@ c #A1A296", +"9@ c #5C5142", +"0@ c #553720", +"a@ c #543F33", +"b@ c #5F4A3A", +"c@ c #372A15", +"d@ c #657D32", +"e@ c #373D1B", +"f@ c #2C2B12", +"g@ c #343311", +"h@ c #3E3214", +"i@ c #392811", +"j@ c #3D3518", +"k@ c #393F1B", +"l@ c #2D3416", +"m@ c #5C713C", +"n@ c #678048", +"o@ c #47512F", +"p@ c #677346", +"q@ c #515132", +"r@ c #6B613A", +"s@ c #5D582D", +"t@ c #444224", +"u@ c #474B28", +"v@ c #5E5E37", +"w@ c #666B44", +"x@ c #999C81", +"y@ c #CDCBC5", +"z@ c #E5E1DE", +"A@ c #BEB9BA", +"B@ c #A9A3A0", +"C@ c #756D65", +"D@ c #877F79", +"E@ c #B2AFAA", +"F@ c #BEBEBA", +"G@ c #9FA19C", +"H@ c #443C2E", +"I@ c #302413", +"J@ c #301F0E", +"K@ c #27201A", +"L@ c #331D11", +"M@ c #432D13", +"N@ c #2D280F", +"O@ c #424F20", +"P@ c #1A1C0B", +"Q@ c #2F2E16", +"R@ c #54471F", +"S@ c #4F2E13", +"T@ c #1E0804", +"U@ c #542F14", +"V@ c #6F6534", +"W@ c #596332", +"X@ c #6F7444", +"Y@ c #645C32", +"Z@ c #526031", +"`@ c #434C2B", +" # c #4E5730", +".# c #48462A", +"+# c #5A5231", +"@# c #5C532E", +"## c #685A37", +"$# c #72673F", +"%# c #7F7C54", +"&# c #CAC8BA", +"*# c #DEDBD6", +"=# c #DBD7D3", +"-# c #D9D5D4", +";# c #C7C1C0", +"># c #857E7D", +",# c #59544B", +"'# c #544C44", +")# c #655F61", +"!# c #736D69", +"~# c #837E7E", +"{# c #595B4D", +"]# c #4D462F", +"^# c #7D7862", +"/# c #8D9594", +"(# c #4F3F2C", +"_# c #635237", +":# c #251E06", +"<# c #435120", +"[# c #262913", +"}# c #212012", +"|# c #3F3F27", +"1# c #6A5631", +"2# c #3D140C", +"3# c #1A0907", +"4# c #31180C", +"5# c #988561", +"6# c #7A804B", +"7# c #887C5D", +"8# c #596137", +"9# c #525633", +"0# c #242513", +"a# c #201A0F", +"b# c #453821", +"c# c #78613B", +"d# c #78603D", +"e# c #AB8E5D", +"f# c #A0865F", +"g# c #AB9575", +"h# c #C7BCB1", +"i# c #A49994", +"j# c #9E9492", +"k# c #A59E9D", +"l# c #A7A3A4", +"m# c #8A8280", +"n# c #7E7C72", +"o# c #403727", +"p# c #1C2119", +"q# c #55534F", +"r# c #555147", +"s# c #8C8F88", +"t# c #8E948D", +"u# c #788278", +"v# c #8C989C", +"w# c #444137", +"x# c #423E2D", +"y# c #3F3D1B", +"z# c #473C25", +"A# c #737367", +"B# c #818772", +"C# c #838365", +"D# c #431F14", +"E# c #3E251F", +"F# c #6B523E", +"G# c #8A7F5E", +"H# c #736544", +"I# c #6B5E3C", +"J# c #5D5134", +"K# c #494326", +"L# c #2C2715", +"M# c #3F371F", +"N# c #6D5835", +"O# c #79693D", +"P# c #A39B77", +"Q# c #D2CDB6", +"R# c #E4DCCD", +"S# c #E5DACF", +"T# c #C0B5AF", +"U# c #988E8B", +"V# c #9C9490", +"W# c #B6B3AF", +"X# c #AEAFAE", +"Y# c #A7A9A8", +"Z# c #55524A", +"`# c #514B37", +" $ c #434527", +".$ c #4A4D31", +"+$ c #595E45", +"@$ c #7E7A75", +"#$ c #4C4A47", +"$$ c #393023", +"%$ c #2B251E", +"&$ c #2F2C1E", +"*$ c #4B4C3C", +"=$ c #3D3E32", +"-$ c #4B4320", +";$ c #979789", +">$ c #C6CFCB", +",$ c #AFB1AB", +"'$ c #67695A", +")$ c #605136", +"!$ c #8F7E67", +"~$ c #867D62", +"{$ c #6D6D4E", +"]$ c #625434", +"^$ c #554B26", +"/$ c #464020", +"($ c #494825", +"_$ c #494926", +":$ c #75683D", +"<$ c #998149", +"[$ c #A3854F", +"}$ c #A89C86", +"|$ c #D8D5D1", +"1$ c #D9D7D5", +"2$ c #E7E6E4", +"3$ c #CDCAC9", +"4$ c #918986", +"5$ c #B5B5B0", +"6$ c #B2B6B2", +"7$ c #A8B0AB", +"8$ c #B8C2C2", +"9$ c #8B908E", +"0$ c #585B48", +"a$ c #64663A", +"b$ c #65643A", +"c$ c #565336", +"d$ c #4B3D2B", +"e$ c #443B31", +"f$ c #616057", +"g$ c #5F6251", +"h$ c #495134", +"i$ c #798175", +"j$ c #555648", +"k$ c #665D3F", +"l$ c #80857A", +"m$ c #646263", +"n$ c #48443F", +"o$ c #393528", +"p$ c #4C4836", +"q$ c #5D5739", +"r$ c #595733", +"s$ c #534A2C", +"t$ c #3C4420", +"u$ c #2C3318", +"v$ c #3E4623", +"w$ c #5C6E37", +"x$ c #7E8A52", +"y$ c #8B8964", +"z$ c #94976D", +"A$ c #797B56", +"B$ c #7C7855", +"C$ c #58573F", +"D$ c #353329", +"E$ c #413937", +"F$ c #544A47", +"G$ c #A1A09C", +"H$ c #817F79", +"I$ c #A6AEAB", +"J$ c #8C9091", +"K$ c #524F4D", +"L$ c #2D261F", +"M$ c #535645", +"N$ c #81886E", +"O$ c #938E77", +"P$ c #B0ADAA", +"Q$ c #807368", +"R$ c #A3A9A0", +"S$ c #818D86", +"T$ c #68726D", +"U$ c #727B6D", +"V$ c #444031", +"W$ c #363123", +"X$ c #545138", +"Y$ c #3C3F28", +"Z$ c #3A3B32", +"`$ c #2E3424", +" % c #383B31", +".% c #353E26", +"+% c #5E5A3A", +"@% c #403B21", +"#% c #3A4620", +"$% c #374320", +"%% c #414B2B", +"&% c #657349", +"*% c #8E9874", +"=% c #B8BBA1", +"-% c #B6BAAA", +";% c #A19D91", +">% c #9D9888", +",% c #A1AD86", +"'% c #565E41", +")% c #4A4A37", +"!% c #625C4E", +"~% c #585043", +"{% c #3D362B", +"]% c #2F2317", +"^% c #2D2619", +"/% c #3A3624", +"(% c #514C3C", +"_% c #85877D", +":% c #C2C4BF", +"<% c #AEAEAA", +"[% c #8F8D88", +"}% c #787870", +"|% c #767F77", +"1% c #757C7A", +"2% c #6C7274", +"3% c #43413B", +"4% c #2B2514", +"5% c #2D2814", +"6% c #26220F", +"7% c #3A331E", +"8% c #212413", +"9% c #273019", +"0% c #505644", +"a% c #6A6E66", +"b% c #5B644A", +"c% c #464B2A", +"d% c #404E28", +"e% c #313920", +"f% c #4C5A33", +"g% c #899377", +"h% c #737C60", +"i% c #CCCDC4", +"j% c #E9EAE5", +"k% c #DEDDDA", +"l% c #6B6159", +"m% c #665E4B", +"n% c #4B4736", +"o% c #2D3221", +"p% c #474029", +"q% c #59574B", +"r% c #726F62", +"s% c #7C7962", +"t% c #716A4E", +"u% c #958C6E", +"v% c #A39E8D", +"w% c #B8B8A9", +"x% c #CBCDC8", +"y% c #D1D8D7", +"z% c #D0DAD8", +"A% c #A1AAA6", +"B% c #7E8380", +"C% c #35382D", +"D% c #3F3D35", +"E% c #3A362B", +"F% c #2A261E", +"G% c #1F1A13", +"H% c #3B4223", +"I% c #2C2D13", +"J% c #2F2B15", +"K% c #2E2410", +"L% c #323424", +"M% c #313219", +"N% c #4F574C", +"O% c #616E64", +"P% c #4D4F40", +"Q% c #353624", +"R% c #2C341D", +"S% c #4E5D33", +"T% c #56663A", +"U% c #434A2B", +"V% c #4F5936", +"W% c #767668", +"X% c #9F9B9C", +"Y% c #878182", +"Z% c #504941", +"`% c #5E5940", +" & c #515137", +".& c #4E4B36", +"+& c #666148", +"@& c #564E34", +"#& c #4A452C", +"$& c #504829", +"%& c #66593B", +"&& c #68634F", +"*& c #9D9C92", +"=& c #DBDFDD", +"-& c #D7E0DF", +";& c #C4D4D5", +">& c #B6C8CC", +",& c #929FA3", +"'& c #555348", +")& c #423925", +"!& c #585143", +"~& c #444036", +"{& c #404524", +"]& c #35401D", +"^& c #2C3012", +"/& c #303214", +"(& c #423C1C", +"_& c #303210", +":& c #46471F", +"<& c #505E50", +"[& c #495747", +"}& c #484A3B", +"|& c #4C4832", +"1& c #363A24", +"2& c #4E522E", +"3& c #51552F", +"4& c #4E532E", +"5& c #586639", +"6& c #535D36", +"7& c #5B6249", +"8& c #38362A", +"9& c #33321F", +"0& c #52572C", +"a& c #51542A", +"b& c #4E4C2A", +"c& c #3A411E", +"d& c #2E3219", +"e& c #312B15", +"f& c #4C4220", +"g& c #51421F", +"h& c #554C27", +"i& c #747463", +"j& c #CCD6D6", +"k& c #B8CACD", +"l& c #9DB1B7", +"m& c #83959B", +"n& c #5A6368", +"o& c #403E38", +"p& c #422E18", +"q& c #503F1C", +"r& c #3C3B1B", +"s& c #343917", +"t& c #37421E", +"u& c #374820", +"v& c #2F3518", +"w& c #24230B", +"x& c #465122", +"y& c #32330A", +"z& c #414310", +"A& c #434A1B", +"B& c #3E4816", +"C& c #504D29", +"D& c #5B5A40", +"E& c #898778", +"F& c #615D3C", +"G& c #504E2A", +"H& c #586434", +"I& c #647E40", +"J& c #4E5E32", +"K& c #586D36", +"L& c #2F351C", +"M& c #373D20", +"N& c #474B23", +"O& c #6A6339", +"P& c #524B26", +"Q& c #3E3C1F", +"R& c #4E4C2B", +"S& c #4A4426", +"T& c #403B1D", +"U& c #443A1C", +"V& c #3B371B", +"W& c #5D553B", +"X& c #61635F", +"Y& c #78868B", +"Z& c #545C62", +"`& c #40444A", +" * c #252524", +".* c #27261F", +"+* c #4A4320", +"@* c #485022", +"#* c #2D3515", +"$* c #2C3415", +"%* c #3F4E26", +"&* c #273416", +"** c #262C11", +"=* c #1C1C0C", +"-* c #3D461E", +";* c #3D3815", +">* c #3B2406", +",* c #332508", +"'* c #3D390E", +")* c #504E1D", +"!* c #6C6138", +"~* c #A7A392", +"{* c #908674", +"]* c #715D3A", +"^* c #645633", +"/* c #2F2A15", +"(* c #4F5A31", +"_* c #47512E", +":* c #505933", +"<* c #40432B", +"[* c #59553D", +"}* c #554933", +"|* c #686136", +"1* c #717A3D", +"2* c #554C2F", +"3* c #604A29", +"4* c #625530", +"5* c #61572D", +"6* c #513F1D", +"7* c #443919", +"8* c #3C2E19", +"9* c #1F1812", +"0* c #1F1B17", +"a* c #23241D", +"b* c #1F2119", +"c* c #17180F", +"d* c #2F3319", +"e* c #3F421E", +"f* c #292813", +"g* c #343B19", +"h* c #35431D", +"i* c #1E290F", +"j* c #111309", +"k* c #465226", +"l* c #4A5024", +"m* c #231B09", +"n* c #492C0B", +"o* c #544113", +"p* c #605F28", +"q* c #6B7F38", +"r* c #6E7B45", +"s* c #696959", +"t* c #867E72", +"u* c #655432", +"v* c #574929", +"w* c #59663A", +"x* c #444B2D", +"y* c #444E29", +"z* c #25241F", +"A* c #41565B", +"B* c #41676B", +"C* c #5E7236", +"D* c #4D5134", +"E* c #544934", +"F* c #5B4627", +"G* c #5C4321", +"H* c #644A25", +"I* c #604A24", +"J* c #614C27", +"K* c #4F3E22", +"L* c #4A3118", +"M* c #50391F", +"N* c #292115", +"O* c #161007", +"P* c #2E2212", +"Q* c #46361C", +"R* c #533616", +"S* c #31220C", +"T* c #30250C", +"U* c #232811", +"V* c #141708", +"W* c #141407", +"X* c #536B32", +"Y* c #566434", +"Z* c #403B19", +"`* c #50471A", +" = c #3D4214", +".= c #3F4B1A", +"+= c #374519", +"@= c #3C451E", +"#= c #5B6742", +"$= c #909487", +"%= c #6B6954", +"&= c #363C22", +"*= c #3E4527", +"== c #2A2C18", +"-= c #262314", +";= c #2A2A26", +">= c #3C4E51", +",= c #3D585D", +"'= c #444D40", +")= c #39372B", +"!= c #413C24", +"~= c #534A2A", +"{= c #68532E", +"]= c #644E29", +"^= c #583A1B", +"/= c #5C492B", +"(= c #3F2F1C", +"_= c #4A321A", +":= c #4C3A1C", +"<= c #402F18", +"[= c #21140A", +"}= c #4E3D23", +"|= c #2D160B", +"1= c #422C15", +"2= c #4F411F", +"3= c #50451E", +"4= c #262A15", +"5= c #272C12", +"6= c #171606", +"7= c #425423", +"8= c #323927", +"9= c #282A17", +"0= c #483A1B", +"a= c #3A4115", +"b= c #25350C", +"c= c #212E0D", +"d= c #424F29", +"e= c #5E7137", +"f= c #8B947C", +"g= c #717868", +"h= c #3B432B", +"i= c #3B4030", +"j= c #2E2F20", +"k= c #38352A", +"l= c #485150", +"m= c #7A694F", +"n= c #7D5E39", +"o= c #624929", +"p= c #352919", +"q= c #4B3921", +"r= c #604E30", +"s= c #2F291B", +"t= c #423924", +"u= c #605736", +"v= c #605A31", +"w= c #656330", +"x= c #474C25", +"y= c #54562C", +"z= c #43361C", +"A= c #4B3A21", +"B= c #4D3417", +"C= c #5B4727", +"D= c #475228", +"E= c #222A12", +"F= c #343118", +"G= c #292811", +"H= c #282813", +"I= c #262513", +"J= c #1F2114", +"K= c #1A1F0B", +"L= c #352F12", +"M= c #3B3A12", +"N= c #303710", +"O= c #38381B", +"P= c #3B471E", +"Q= c #394420", +"R= c #3B3F2D", +"S= c #595C50", +"T= c #73756E", +"U= c #7E8079", +"V= c #7D7467", +"W= c #746244", +"X= c #736636", +"Y= c #6C6F3B", +"Z= c #646738", +"`= c #57542E", +" - c #5B5930", +".- c #625A31", +"+- c #5E5D31", +"@- c #44492A", +"#- c #4E5230", +"$- c #4C5431", +"%- c #404328", +"&- c #4A4725", +"*- c #525026", +"=- c #484C2A", +"-- c #47562B", +";- c #494E24", +">- c #3B4A28", +",- c #28341B", +"'- c #13150C", +")- c #1F2414", +"!- c #2C2C17", +"~- c #32311D", +"{- c #36362A", +"]- c #342E16", +"^- c #484C29", +"/- c #4D4827", +"(- c #4B3D22", +"_- c #393012", +":- c #3A3517", +"<- c #3A4721", +"[- c #283013", +"}- c #292F13", +"|- c #1A1E1D", +"1- c #242E32", +"2- c #505C62", +"3- c #5E534B", +"4- c #6A5A37", +"5- c #707D44", +"6- c #576931", +"7- c #49502E", +"8- c #596338", +"9- c #525F38", +"0- c #4F5833", +"a- c #4F482C", +"b- c #4C5632", +"c- c #525C33", +"d- c #454D2C", +"e- c #50562F", +"f- c #556536", +"g- c #637A3B", +"h- c #415229", +"i- c #3F5428", +"j- c #2A381B", +"k- c #242E1A", +"l- c #191E16", +"m- c #181D13", +"n- c #1F2116", +"o- c #343D22", +"p- c #313B28", +"q- c #392D15", +"r- c #38391C", +"s- c #645B45", +"t- c #8D8E7D", +"u- c #868C71", +"v- c #5D6640", +"w- c #282C17", +"x- c #282B16", +"y- c #262616", +"z- c #363426", +"A- c #30332E", +"B- c #43433A", +"C- c #55503A", +"D- c #545734", +"E- c #516038", +"F- c #42532A", +"G- c #43512E", +"H- c #3F482B", +"I- c #393C22", +"J- c #474428", +"K- c #4B452B", +"L- c #4E4A2C", +"M- c #505F35", +"N- c #48512E", +"O- c #525F35", +"P- c #46572C", +"Q- c #303D1D", +"R- c #2B371E", +"S- c #39482C", +"T- c #242D1E", +"U- c #1D2417", +"V- c #191E15", +"W- c #1B1B14", +"X- c #242718", +"Y- c #293219", +"Z- c #3B463C", +"`- c #454427", +" ; c #3F4A2B", +".; c #4F4F3E", +"+; c #8E9897", +"@; c #97A19A", +"#; c #808C77", +"$; c #5A6934", +"%; c #3A401E", +"&; c #383926", +"*; c #585747", +"=; c #485144", +"-; c #484932", +";; c #555A3E", +">; c #4B4F36", +",; c #303528", +"'; c #25271F", +"); c #26261D", +"!; c #3B3322", +"~; c #52472D", +"{; c #615E3B", +"]; c #515E34", +"^; c #475730", +"/; c #40492B", +"(; c #4C5C33", +"_; c #374425", +":; c #2E3820", +"<; c #212918", +"[; c #4A5149", +"}; c #4A5349", +"|; c #252D1F", +"1; c #1C211B", +"2; c #242219", +"3; c #2A2E1C", +"4; c #2D3622", +"5; c #464B40", +"6; c #4B5149", +"7; c #95A1A1", +"8; c #9FABA8", +"9; c #6C7966", +"0; c #556342", +"a; c #575F4A", +"b; c #6C7368", +"c; c #6F7D7D", +"d; c #586159", +"e; c #777B72", +"f; c #747267", +"g; c #524E41", +"h; c #443E31", +"i; c #463D2D", +"j; c #625644", +"k; c #685740", +"l; c #6E5F3A", +"m; c #454629", +"n; c #2C3120", +"o; c #373828", +"p; c #3A3F2A", +"q; c #3F4B30", +"r; c #24271B", +"s; c #1F1F16", +"t; c #414441", +"u; c #8E9E9F", +"v; c #3A4241", +"w; c #414849", +"x; c #2B2A21", +"y; c #48483D", +"z; c #3D4738", +"A; c #6D7872", +"B; c #A3B2B5", +"C; c #A8B6B7", +"D; c #808C85", +"E; c #555B53", +"F; c #393B2C", +"G; c #4D493E", +"H; c #878984", +"I; c #7F7B71", +"J; c #6D6252", +"K; c #574B32", +"L; c #635A3A", +"M; c #7A7452", +"N; c #6D6446", +"O; c #474431", +"P; c #404331", +"Q; c #484D39", +"R; c #4A4A39", +"S; c #33342D", +"T; c #787D77", +"U; c #5D5F59", +"V; c #2F2D23", +"W; c #36382F", +"X; c #8A9BA0", +"Y; c #637279", +"Z; c #262B2A", +"`; c #2B2C25", +" > c #404439", +".> c #403320", +"+> c #3A3C30", +"@> c #4E5648", +"#> c #646B69", +"$> c #6F7779", +"%> c #666B6D", +"&> c #434642", +"*> c #4B4C43", +"=> c #686A5F", +"-> c #666C5A", +";> c #64695C", +">> c #505240", +",> c #50553B", +"'> c #494F37", +")> c #464B30", +"!> c #52513A", +"~> c #797B67", +"{> c #848E75", +"]> c #7D8A6E", +"^> c #60675A", +"/> c #4B5147", +"(> c #767B78", +"_> c #7C807F", +":> c #37362A", +"<> c #2E3024", +"[> c #383C34", +"}> c #6D7C7F", +"|> c #63757D", +"1> c #4B5659", +"2> c #33362E", +"3> c #47534C", +"4> c #383E31", +"5> c #353A29", +"6> c #626C6A", +"7> c #4D5452", +"8> c #2A2E23", +"9> c #353B2A", +"0> c #49523F", +"a> c #4E5745", +"b> c #565A4E", +"c> c #5B6052", +"d> c #424934", +"e> c #434931", +"f> c #4C5339", +"g> c #616555", +"h> c #747E69", +"i> c #7D8679", +"j> c #95A299", +"k> c #929C99", +"l> c #79817F", +"m> c #6E776F", +"n> c #7B827D", +"o> c #858F93", +"p> c #3A3E3E", +"q> c #3E3B2E", +"r> c #596059", +"s> c #6C7B7A", +"t> c #5B6D75", +"u> c #404A4D", +"v> c #313730", +"w> c #5D6669", +"x> c #4B534F", +"y> c #32362E", +"z> c #3B4231", +"A> c #3C4437", +"B> c #545F51", +"C> c #43473D", +"D> c #3D3C2C", +"E> c #4E4F3B", +"F> c #5E5E4B", +"G> c #84887B", +"H> c #8E928C", +"I> c #99A19E", +"J> c #9AA5A5", +"K> c #A2AFB1", +"L> c #9BA7AA", +"M> c #ACBCBF", +"N> c #97A3A7", +"O> c #676F72", +"P> c #3F4538", +"Q> c #576057", +"R> c #364132", +"S> c #607172", +"T> c #54676F", +"U> c #505854", +"V> c #4A524E", +"W> c #576062", +"X> c #6E797C", +"Y> c #707D7C", +"Z> c #748080", +"`> c #737E7D", +" , c #747C79", +"., c #6E7065", +"+, c #78776B", +"@, c #767467", +"#, c #767970", +"$, c #919B96", +"%, c #859090", +"&, c #747A7C", +"*, c #909CA0", +"=, c #96A5A9", +"-, c #9BABB0", +";, c #7E8B91", +">, c #4B5153", +",, c #373B37", +"', c #21251D", +"), c #282C23", +"!, c #5D6B6F", +"~, c #546465", +"{, c #566566", +"], c #71858B", +"^, c #82979E", +"/, c #86999F", +"(, c #8D9FA5", +"_, c #8D9B9C", +":, c #828E8E", +"<, c #737B76", +"[, c #727D74", +"}, c #808D8D", +"|, c #7E898B", +"1, c #838D8E", +"2, c #7E8B8F", +"3, c #84949B", +"4, c #7B8B91", +"5, c #6C7A81", +"6, c #515B5F", +"7, c #363C3A", +"8, c #2A302F", +"9, c #36474F", +"0, c #5C6C72", +"a, c #5B6971", +"b, c #596871", +"c, c #6F838C", +"d, c #7B9099", +"e, c #778A91", +"f, c #7B8D93", +"g, c #738487", +"h, c #6E7C7D", +"i, c #78878C", +"j, c #829399", +"k, c #7D9097", +"l, c #73868C", +"m, c #6C7D83", +"n, c #63747B", +"o, c #55646B", +"p, c #4F5B62", +"q, c #515E67", +"r, c #5F737D", +"s, c #637882", +"t, c #677C85", +"u, c #647780", +"v, c #64767D", +"w, c #62747C", +"x, c #62757E", +"y, c #60737D", +"z, c #5A6B74", +"A, c #55666F", +" ", +" ", +" ", +" ", +" . + @ # $ % & * = ", +" - ; > , ' ) ! ~ { ] ^ / ( _ : < [ ", +" } | 1 2 3 4 5 6 7 8 9 0 a b c d e f g ", +" h i j k l m n o p q r s t u v w x y z A B C D E ", +" F G H I J K L M N O P Q R S T U V W X Y Z ` ...+.@. ", +" #.$.%.&.*.=.-.;.>.,.'.).!.~.{.].^./.(._.:.<.[.}.|.1.2. ", +" 3.4.5.6.7.8.9.0.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t. ", +" v.w.x.y.z.A.B.C.D.E.F.G.H.I.J.K.L.M.N.O.P.Q.R.S.T.U.V.W.X.Y. ", +" Z.`. +.+++@+#+$+%+&+*+=+-+;+>+,+'+)+!+~+{+]+^+/+(+_+:+<+[+}+|+1+ ", +" 2+3+4+5+6+7+8+9+0+a+b+c+d+e+f+g+h+i+j+k+l+m+n+o+p+q+r+s+t+u+v+w+x+y+ ", +" z+A+B+C+D+E+F+G+H+I+J+K+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @.@+@@@#@$@ ", +" %@&@*@=@-@;@>@,@'@)@!@~@{@]@^@/@(@_@:@<@[@}@|@1@2@3@4@5@6@7@8@9@0@a@b@c@ ", +" d@e@f@g@h@i@j@k@,@l@m@n@o@p@q@r@s@t@u@v@w@x@y@z@A@B@C@D@E@F@G@H@I@J@K@L@M@N@ ", +" O@P@Q@R@S@T@U@V@W@X@Y@Z@`@ #.#+#@###$#%#&#*#=#-#;#>#,#'#)#!#~#{#]#^#/#(#_#:# ", +" <#[#}#|#1#2#3#4#5#6#7#8#9#0#a#b#c#d#e#f#g#h#i#j#k#l#m#n#o#p#q#r#s#t#u#v#w#x#y# ", +" z#A#B#C#D#E#F#G#H#I#J#K#L#M#N#O#P#Q#R#S#T#U#V#W#X#Y#Z#`# $.$+$@$#$$$%$&$*$=$ ", +" -$;$>$,$'$)$!$~${$]$^$/$($_$:$<$[$}$|$1$2$3$4$5$6$7$8$9$0$a$b$c$d$e$f$g$h$i$j$ ", +" k$l$m$n$o$p$q$r$s$t$u$v$w$x$y$z$A$B$C$D$E$F$G$H$I$J$K$L$M$N$O$P$Q$R$S$T$U$V$W$ ", +" X$Y$Z$`$ %.%+%@%#%$%%%&%*%=%-%;%>%,%'%)%D$!%~%{%]%^%/%(%_%:%<%[%}%|%1%2%3%4%5%6% ", +" 7%8%9%0%a%b%c%d%e%f%g%h%i%j%k%l%m%n%o%p%q%r%s%t%u%v%w%x%y%z%A%B%C%D%E%F%G%H%I%J% ", +" K%L%M%N%O%P%Q%R%S%T%U%V%W%X%Y%Z%`% &.&+&@&#&$&%&&&*&=&-&;&>&,&V.'&)&!&~&{&]&^&/& ", +" (&_&:&<&[&}&|&1&2&3&4&5&6&7&8&9&0&a&b&c&d&e&f&g&h&i&j&k&l&m&n&o&p&q&r&s&t&u&v&w& ", +" x&y&z&A&B&C&D&E&F&G&H&I&J&K&L&M&N&O&P&Q&R&S&T&U&V&W&X&Y&Z&`& *.*+*@*#*$*%*&***=* ", +" -*;*>*,*'*)*!*~*{*]*^*/*(*_*:*<*[*}*|*1*2*3*4*5*6*7*8*9*0*a*b*c*d*e*f*g*h*i*j* ", +" k*l*m*n*o*p*q*r*s*t*u*v*w*x*y*z*A*B*C*D*E*F*G*H*I*J*K*L*M*N*O*P*Q*R*S*T*U*V*W* ", +" X*Y*Z*`* =.=+=@=#=$=%=&=*===-=;=>=,='=)=!=~={=]=^=/=(=_=:=<=[=}=|=1=2=3=4=5=6= ", +" 7=8=9=0=a=b=c=d=e=f=g=h=i=j=k=l=m=n=o=p=q=r=s=t=u=v=w=x=y=z=A=B=C=D=E=F=G=H=I= ", +" J=K=L=M=N=O=P=Q=R=S=T=U=V=W=X=Y=Z=`= -.-+-@-#-$-%-&-*-=---;->-,-'-)-!-~-{- ", +" ]-^-/-(-_-:-<-[-}-|-1-2-3-4-5-6-7-8-9-0-a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p- ", +" q-r-s-t-u-v-w-x-y-z-A-B-C-D-E-F-G-H-I-J-K-L-M-N-O-P-Q-R-S-T-U-V-W-X-Y-Z- ", +" `- ;.;+;@;#;$;%;&;*;=;-;;;>;,;';);!;~;{;];^;/;(;_;:;<;[;};|;1;2;3;4; ", +" 5;6;7;8;9;0;a;b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;s;t;u;v;w;x;3; ", +" y;z;A;B;C;D;E;F;G;H;I;J;K;L;M;N;O;P;Q;R;S;T;U;V;2;W;X;Y;Z;`; > ", +" .>+>@>#>$>%>&>*>=>->;>>>,>'>)>!>~>{>]>^>/>(>_>:><>[>}>|>1>2> ", +" 4>5>6>7>8>9>0>a>b>c>d>e>f>g>h>i>j>k>l>m>n>o>p>q>r>s>t>u> ", +" v>w>x>y>z>A>B>C>D>E>F>G>H>I>J>K>L>M>N>O>P>Q>R>S>T> ", +" U>V>W>X>Y>Z>`> ,.,+,@,#,$,%,&,*,=,-,;,>,,,',), ", +" !,~,{,],^,/,(,_,:,<,[,},|,1,2,3,4,5,6,7,8, ", +" 0,a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p, ", +" q,r,s,t,u,v,w,x,y,z,A, ", +" ", +" ", +" ", +" "}; From 470a00a2c4d74eddcb813a36fe0f0bbd25541dbc Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Wed, 14 Sep 2011 01:47:27 +0300 Subject: [PATCH 100/215] Changed: #1193 Added the feature of enable/disable plugins on startup program. --- .../src/extension_system/iplugin.h | 2 +- .../src/extension_system/iplugin_manager.h | 2 +- .../src/extension_system/iplugin_spec.h | 6 +- .../src/extension_system/plugin_manager.cpp | 43 +++++++++++- .../src/extension_system/plugin_spec.cpp | 26 ++++++- .../src/extension_system/plugin_spec.h | 12 +++- .../tools/3d/object_viewer_qt/src/main.cpp | 21 ++---- .../src/plugins/core/core_plugin.cpp | 5 +- .../src/plugins/core/main_window.cpp | 6 +- .../src/plugins/core/main_window.h | 2 +- .../src/plugins/core/plugin_view_dialog.cpp | 67 ++++++++++++++----- .../src/plugins/core/plugin_view_dialog.h | 27 +++++--- .../src/plugins/core/plugin_view_dialog.ui | 18 ++--- 13 files changed, 171 insertions(+), 66 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin.h b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin.h index fc2e0736b..864028501 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin.h @@ -105,6 +105,6 @@ public: }; //namespace ExtensionSystem -Q_DECLARE_INTERFACE(ExtensionSystem::IPlugin, "dev.ryzom.com.ObjectViewerQt.IPlugin/0.9.1") +Q_DECLARE_INTERFACE(ExtensionSystem::IPlugin, "dev.ryzom.com.ObjectViewerQt.IPlugin/0.9.2") #endif // IPLUGIN_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_manager.h b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_manager.h index 05d4b832c..e38748e77 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_manager.h +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_manager.h @@ -98,7 +98,7 @@ public: { QList all = allObjects(); QObject *result = 0; - Q_FOREACH (QObject *qobj, all) + Q_FOREACH(QObject *qobj, all) { if (qobj->objectName() == name) { diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_spec.h b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_spec.h index f0c62fec7..45895c36f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_spec.h +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_spec.h @@ -71,9 +71,13 @@ public: virtual IPlugin *plugin() const = 0; // state - virtual int getState() const = 0; + virtual int state() const = 0; virtual bool hasError() const = 0; virtual QString errorString() const = 0; + + /// Enables/disables load this plugin after restart the program + virtual void setEnabled(bool enabled) = 0; + virtual bool isEnabled() const = 0; }; } // namespace ExtensionSystem diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp index be5f18c74..3b2431a07 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp @@ -34,6 +34,7 @@ CPluginManager::CPluginManager(QObject *parent) CPluginManager::~CPluginManager() { + writeSettings(); stopAll(); deleteAll(); qDeleteAll(m_pluginSpecs); @@ -114,6 +115,7 @@ void CPluginManager::setPluginPaths(const QStringList &paths) { m_pluginPaths = paths; readPluginPaths(); + readSettings(); } QList CPluginManager::plugins() const @@ -133,10 +135,41 @@ QSettings *CPluginManager::settings() const void CPluginManager::readSettings() { + if (m_settings) + { + QStringList blackList; + m_settings->beginGroup("PluginManager"); + blackList = m_settings->value("BlackList").toStringList(); + m_settings->endGroup(); + Q_FOREACH (CPluginSpec *spec, m_pluginSpecs) + { + QString pluginName = spec->fileName(); + + if (blackList.contains(pluginName)) + { + spec->setEnabled(false); + spec->setEnabledStartup(false); + } + } + } } void CPluginManager::writeSettings() { + if (m_settings) + { + QStringList blackList; + Q_FOREACH(CPluginSpec *spec, m_pluginSpecs) + { + nlinfo(spec->fileName().toStdString().c_str()); + if (!spec->isEnabled()) + blackList.push_back(spec->fileName()); + } + m_settings->beginGroup("PluginManager"); + m_settings->setValue("BlackList", blackList); + m_settings->endGroup(); + m_settings->sync(); + } } void CPluginManager::readPluginPaths() @@ -176,7 +209,11 @@ void CPluginManager::readPluginPaths() void CPluginManager::setPluginState(CPluginSpec *spec, int destState) { - if (spec->hasError() || spec->getState() != destState-1) + if (spec->hasError() || spec->state() != destState-1) + return; + + // plugin in black list + if (!spec->isEnabledStartup()) return; switch (destState) @@ -198,7 +235,7 @@ void CPluginManager::setPluginState(CPluginSpec *spec, int destState) } Q_FOREACH (const CPluginSpec *depSpec, spec->dependencySpecs()) { - if (depSpec->getState() != destState) + if (depSpec->state() != destState) { spec->m_hasError = true; spec->m_errorString = tr("Cannot initializing plugin because dependency failed to load: %1\nReason: %2") @@ -251,7 +288,7 @@ bool CPluginManager::loadQueue(CPluginSpec *spec, QList &queue, } circularityCheckQueue.append(spec); // check if we have the dependencies - if (spec->getState() == State::Invalid || spec->getState() == State::Read) + if (spec->state() == State::Invalid || spec->state() == State::Read) { queue.append(spec); return false; diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp index d77d90962..6e15e1181 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp @@ -40,6 +40,8 @@ CPluginSpec::CPluginSpec() m_vendor(""), m_description(""), m_state(State::Invalid), + m_enabled(true), + m_enabledStartup(true), m_hasError(false), m_errorString(""), m_plugin(0), @@ -82,12 +84,12 @@ QString CPluginSpec::fileName() const return m_fileName; } -IPlugin* CPluginSpec::plugin() const +IPlugin *CPluginSpec::plugin() const { return m_plugin; } -int CPluginSpec::getState() const +int CPluginSpec::state() const { return m_state; } @@ -124,6 +126,16 @@ bool CPluginSpec::setFileName(const QString &fileName) return true; } +void CPluginSpec::setEnabled(bool enabled) +{ + m_enabled = enabled; +} + +bool CPluginSpec::isEnabled() const +{ + return m_enabled; +} + bool CPluginSpec::loadLibrary() { if (m_hasError) @@ -259,6 +271,16 @@ void CPluginSpec::kill() m_state = State::Deleted; } +void CPluginSpec::setEnabledStartup(bool enabled) +{ + m_enabledStartup = enabled; +} + +bool CPluginSpec::isEnabledStartup() const +{ + return m_enabledStartup; +} + bool CPluginSpec::reportError(const QString &err) { m_errorString = err; diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.h b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.h index 0cc895ada..5d9605de7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.h +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.h @@ -41,11 +41,15 @@ public: virtual IPlugin *plugin() const; // state - virtual int getState() const; + virtual int state() const; virtual bool hasError() const; virtual QString errorString() const; QList dependencySpecs() const; + /// Enables/disables load this plugin after restart the program + virtual void setEnabled(bool enabled); + virtual bool isEnabled() const; + private: CPluginSpec(); @@ -57,6 +61,11 @@ private: void stop(); void kill(); + /// Enables/disables load this plugin on startup the program + /// Method is used for disabling startup plugin by pluginmanager + void setEnabledStartup(bool enabled); + bool isEnabledStartup() const; + bool reportError(const QString &err); QString m_location; @@ -69,6 +78,7 @@ private: QString m_description; int m_state; + bool m_enabled, m_enabledStartup; bool m_hasError; QString m_errorString; diff --git a/code/nel/tools/3d/object_viewer_qt/src/main.cpp b/code/nel/tools/3d/object_viewer_qt/src/main.cpp index 050b7716c..1ddcc5565 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/main.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/main.cpp @@ -132,7 +132,7 @@ sint main(int argc, char **argv) QSettings::setDefaultFormat(QSettings::IniFormat); QSettings *settings = new QSettings(QSettings::IniFormat, QSettings::UserScope, - QLatin1String("RyzomCore"), QLatin1String(appNameC)); + QLatin1String("RyzomCore"), QLatin1String(appNameC)); QTranslator translator; QTranslator qtTranslator; @@ -162,17 +162,8 @@ sint main(int argc, char **argv) splash->hide(); - const QList plugins = pluginManager.plugins(); - ExtensionSystem::IPluginSpec *corePlugin = 0; - Q_FOREACH(ExtensionSystem::IPluginSpec *spec, plugins) - { - if (spec->name() == QLatin1String("Core")) - { - corePlugin = spec; - break; - } - } - + ExtensionSystem::IPluginSpec *corePlugin = pluginManager.pluginByName("Core"); + if (!corePlugin) { QDir absolutePluginPaths(pluginPaths.join(QLatin1String(","))); @@ -183,8 +174,8 @@ sint main(int argc, char **argv) QString newPath = QFileDialog::getExistingDirectory(0, QCoreApplication::translate("Application", "Change the plugins path"), QDir::homePath()); bool ok; QString text = QInputDialog::getText(0, QCoreApplication::translate("Application", "Enter the plugins path"), - QCoreApplication::translate("Application", "Plugin path:"), QLineEdit::Normal, - newPath, &ok); + QCoreApplication::translate("Application", "Plugin path:"), QLineEdit::Normal, + newPath, &ok); if (ok && !text.isEmpty()) settings->setValue("PluginPath", text); settings->sync(); @@ -203,7 +194,7 @@ sint main(int argc, char **argv) if (!errors.isEmpty()) QMessageBox::warning(0, QCoreApplication::translate("Application", "Object Viewer Qt - Plugin loader messages"), - errors.join(QString::fromLatin1("\n\n"))); + errors.join(QString::fromLatin1("\n\n"))); int result = app.exec(); return result; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.cpp index 92e776068..8aef51f2a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.cpp @@ -37,6 +37,8 @@ using namespace Core; CorePlugin::CorePlugin() + : m_plugMan(0), + m_mainWindow(0) { } @@ -49,7 +51,8 @@ CorePlugin::~CorePlugin() qDeleteAll(m_autoReleaseObjects); m_autoReleaseObjects.clear(); - delete m_mainWindow; + if (m_mainWindow) + delete m_mainWindow; } bool CorePlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.cpp index caaeb4d5c..2105b8b1b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.cpp @@ -107,8 +107,8 @@ bool MainWindow::initialize(QString *errorString) void MainWindow::extensionsInitialized() { readSettings(); - connect(m_contextManager, SIGNAL(currentContextChanged(Core::IContext*)), - this, SLOT(updateContext(Core::IContext*))); + connect(m_contextManager, SIGNAL(currentContextChanged(Core::IContext *)), + this, SLOT(updateContext(Core::IContext *))); if (m_contextManager->currentContext() != NULL) updateContext(m_contextManager->currentContext()); show(); @@ -437,7 +437,7 @@ void MainWindow::createStatusBar() void MainWindow::createDialogs() { - m_pluginView = new ExtensionSystem::CPluginView(m_pluginManager, this); + m_pluginView = new PluginView(m_pluginManager, this); // Create undo/redo command list m_dockWidget = new QDockWidget("Command List", this); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.h index d594692ee..48ce93a15 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.h @@ -93,7 +93,7 @@ private: void writeSettings(); ExtensionSystem::IPluginManager *m_pluginManager; - ExtensionSystem::CPluginView *m_pluginView; + PluginView *m_pluginView; MenuManager *m_menuManager; ContextManager *m_contextManager; CoreImpl *m_coreImpl; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.cpp index b45d46418..175902d39 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.cpp @@ -16,11 +16,12 @@ // along with this program. If not, see . #include "plugin_view_dialog.h" +#include "core_constants.h" + +#include "nel/misc/debug.h" // Qt includes #include -#include -#include #include #include #include @@ -29,45 +30,79 @@ #include "../../extension_system/iplugin_spec.h" #include "../../extension_system/iplugin_manager.h" -namespace ExtensionSystem +namespace Core { -CPluginView::CPluginView(IPluginManager *pluginManager, QWidget *parent) - : QDialog(parent) +PluginView::PluginView(ExtensionSystem::IPluginManager *pluginManager, QWidget *parent) + : QDialog(parent), + m_checkStateColumn(0) { - _ui.setupUi(this); - _pluginManager = pluginManager; + m_ui.setupUi(this); + m_pluginManager = pluginManager; - connect(_pluginManager, SIGNAL(pluginsChanged()), this, SLOT(updateList())); + connect(m_pluginManager, SIGNAL(pluginsChanged()), this, SLOT(updateList())); + connect(this, SIGNAL(accepted()), this, SLOT(updateSettings())); + // WhiteList is list of plugins which can not disable. + m_whiteList << Constants::OVQT_CORE_PLUGIN; updateList(); } -CPluginView::~CPluginView() +PluginView::~PluginView() { } -void CPluginView::updateList() +void PluginView::updateList() { static QIcon okIcon = QApplication::style()->standardIcon(QStyle::SP_DialogApplyButton); static QIcon errorIcon = QApplication::style()->standardIcon(QStyle::SP_DialogCancelButton); + static QIcon notLoadedIcon = QApplication::style()->standardIcon(QStyle::SP_DialogResetButton); + + m_specToItem.clear(); QList items; - Q_FOREACH (IPluginSpec *spec, _pluginManager->plugins()) + Q_FOREACH (ExtensionSystem::IPluginSpec *spec, m_pluginManager->plugins()) { QTreeWidgetItem *item = new QTreeWidgetItem(QStringList() - << "" << spec->name() << QString("%1").arg(spec->version()) << spec->vendor() << QDir::toNativeSeparators(spec->filePath())); - item->setIcon(0, spec->hasError() ? errorIcon : okIcon); + + bool ok = !spec->hasError(); + QIcon icon = ok ? okIcon : errorIcon; + if (ok && (spec->state() != ExtensionSystem::State::Running)) + icon = notLoadedIcon; + + item->setIcon(m_checkStateColumn, icon); + + if (!m_whiteList.contains(spec->name())) + item->setCheckState(m_checkStateColumn, spec->isEnabled() ? Qt::Checked : Qt::Unchecked); + items.append(item); + m_specToItem.insert(spec, item); } - _ui.pluginTreeWidget->clear(); + m_ui.pluginTreeWidget->clear(); if (!items.isEmpty()) - _ui.pluginTreeWidget->addTopLevelItems(items); + m_ui.pluginTreeWidget->addTopLevelItems(items); + + m_ui.pluginTreeWidget->resizeColumnToContents(m_checkStateColumn); } -} /* namespace NLQT */ \ No newline at end of file +void PluginView::updateSettings() +{ + Q_FOREACH (ExtensionSystem::IPluginSpec *spec, m_pluginManager->plugins()) + { + if (m_specToItem.contains(spec) && (!m_whiteList.contains(spec->name()))) + { + QTreeWidgetItem *item = m_specToItem.value(spec); + if (item->checkState(m_checkStateColumn) == Qt::Checked) + spec->setEnabled(true); + else + spec->setEnabled(false); + } + } +} + +} /* namespace Core */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.h index 0f12b64a3..aae16749d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.h @@ -20,28 +20,39 @@ #include "ui_plugin_view_dialog.h" +#include +#include + namespace ExtensionSystem { - class IPluginManager; +class IPluginSpec; +} -class CPluginView: public QDialog +namespace Core +{ + +class PluginView: public QDialog { Q_OBJECT public: - CPluginView(IPluginManager *pluginManager, QWidget *parent = 0); - ~CPluginView(); + PluginView(ExtensionSystem::IPluginManager *pluginManager, QWidget *parent = 0); + ~PluginView(); private Q_SLOTS: void updateList(); + void updateSettings(); private: - IPluginManager *_pluginManager; - Ui::CPluginView _ui; -}; /* class CPluginView */ + const int m_checkStateColumn; + QMap m_specToItem; + QStringList m_whiteList; + ExtensionSystem::IPluginManager *m_pluginManager; + Ui::PluginView m_ui; +}; /* class PluginView */ -} /* namespace NLQT */ +} /* namespace Core */ #endif // PLUGIN_VIEW_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.ui index 2fb0f568e..60a9b9c46 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.ui @@ -1,13 +1,13 @@ - CPluginView - + PluginView + 0 0 - 691 - 249 + 756 + 296 @@ -37,14 +37,6 @@ true - - 100 - - - - State - - Name @@ -126,7 +118,7 @@ closePushButton clicked() - CPluginView + PluginView accept() From 6c1bdbec1ab02626af08cceea3429879625e8aeb Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 16 Sep 2011 13:55:48 +0200 Subject: [PATCH 101/215] Changed: #1093 Create client_dev, client_patch and client_install directories in setup if needed. --- code/nel/tools/build_gamedata/0_setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/nel/tools/build_gamedata/0_setup.py b/code/nel/tools/build_gamedata/0_setup.py index d921d03f4..228dbe2dd 100644 --- a/code/nel/tools/build_gamedata/0_setup.py +++ b/code/nel/tools/build_gamedata/0_setup.py @@ -277,6 +277,11 @@ for projectName in ProjectsToProcess: except Exception, e: printLog(log, "<" + projectName + "> " + str(e)) printLog(log, "") +# Additional directories +printLog(log, ">>> Setup additional directories <<<") +mkPath(log, ClientDevDirectory) +mkPath(log, ClientPatchDirectory) +mkPath(log, ClientInstallDirectory) log.close() if os.path.isfile("0_setup.log"): From 3a520e2fb85fecbaab412ae03ec887e109803e7f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 16 Sep 2011 13:57:13 +0200 Subject: [PATCH 102/215] Changed: #1093 Also try ryzom_client_r.exe instead of client_ryzom_r.exe for copying exedll bnp... --- .../tools/build_gamedata/workspace/common/exedll/directories.py | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ryzom/tools/build_gamedata/workspace/common/exedll/directories.py b/code/ryzom/tools/build_gamedata/workspace/common/exedll/directories.py index 967111b05..dddf60ec8 100644 --- a/code/ryzom/tools/build_gamedata/workspace/common/exedll/directories.py +++ b/code/ryzom/tools/build_gamedata/workspace/common/exedll/directories.py @@ -76,6 +76,7 @@ CopyWindowsExeDllCfgSourceFiles += [ "nel_drv_opengl_win_r.dll" ] CopyWindowsExeDllCfgSourceFiles += [ "nel_drv_xaudio2_win_r.dll" ] CopyWindowsExeDllCfgSourceFiles += [ "client_ryzom_r.exe" ] +CopyWindowsExeDllCfgSourceFiles += [ "ryzom_client_r.exe" ] # i blame sfb CopyWindowsExeDllCfgSourceFiles += [ "ryzom_configuration_r.exe" ] From 706c0a695f8fecacc30855fd9db620ea6368e8a5 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 16 Sep 2011 13:58:20 +0200 Subject: [PATCH 103/215] Changed: #1093 Create client_install using bnps made in client_patch. --- .../tools/build_gamedata/7_client_install.py | 63 +++++++------------ 1 file changed, 24 insertions(+), 39 deletions(-) diff --git a/code/nel/tools/build_gamedata/7_client_install.py b/code/nel/tools/build_gamedata/7_client_install.py index b5a5bab1f..c4c1ab03c 100644 --- a/code/nel/tools/build_gamedata/7_client_install.py +++ b/code/nel/tools/build_gamedata/7_client_install.py @@ -45,45 +45,30 @@ printLog(log, "-------") printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") -# Find tools -BnpMake = findTool(log, ToolDirectories, BnpMakeTool, ToolSuffix) -printLog(log, "") - -if BnpMake == "": - toolLogFail(log, BnpMakeTool, ToolSuffix) -else: - for category in InstallClientData: - printLog(log, "CATEGORY " + category["Name"]) - if (category["UnpackTo"] != None): - targetPath = ClientInstallDirectory - if (category["UnpackTo"] != ""): - targetPath += "/" + category["UnpackTo"] - mkPath(log, targetPath) - for package in category["Packages"]: - printLog(log, "PACKAGE " + package[0]) - mkPath(log, InstallDirectory + "/" + package[0]) - copyFilesNoTreeIfNeeded(log, InstallDirectory + "/" + package[0], targetPath) - else: - targetPath = ClientInstallDirectory + "/data" - mkPath(log, targetPath) - for package in category["Packages"]: - printLog(log, "PACKAGE " + package[0]) - sourcePath = InstallDirectory + "/" + package[0] - mkPath(log, sourcePath) - targetBnp = targetPath + "/" + package[0] + ".bnp" - if (len(package[1]) > 0): - targetBnp = targetPath + "/" + package[1][0] - printLog(log, "TARGET " + package[1][0]) - needUpdateBnp = 1 - if (len(package) > 2): - needUpdateBnp = needUpdate(log, sourcePath + "/" + package[2], targetBnp) - else: - needUpdateBnp = needUpdateDirNoSubdirFile(log, sourcePath, targetBnp) - if (needUpdateBnp): - printLog(log, "BNP " + targetBnp) - subprocess.call([ BnpMake, "/p", sourcePath, targetPath ] + package[1]) - else: - printLog(log, "SKIP " + targetBnp) +for category in InstallClientData: + printLog(log, "CATEGORY " + category["Name"]) + if (category["UnpackTo"] != None): + targetPath = ClientInstallDirectory + if (category["UnpackTo"] != ""): + targetPath += "/" + category["UnpackTo"] + mkPath(log, targetPath) + for package in category["Packages"]: + printLog(log, "PACKAGE " + package[0]) + mkPath(log, InstallDirectory + "/" + package[0]) + copyFilesNoTreeIfNeeded(log, InstallDirectory + "/" + package[0], targetPath) + else: + sourcePath = ClientPatchDirectory + "/bnp" + targetPath = ClientInstallDirectory + "/data" + mkPath(log, targetPath) + for package in category["Packages"]: + printLog(log, "PACKAGE " + package[0]) + sourceBnp = sourcePath + "/" + package[0] + ".bnp" + targetBnp = targetPath + "/" + package[0] + ".bnp" + if (len(package[1]) > 0): + sourceBnp = sourcePath + "/" + package[1][0] + targetBnp = targetPath + "/" + package[1][0] + printLog(log, "TARGET " + package[1][0]) + copyFileIfNeeded(log, sourceBnp, targetBnp) printLog(log, "") log.close() From 2ec690675f47ee5d4b443073267216adc02ac3d4 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 16 Sep 2011 14:04:41 +0200 Subject: [PATCH 104/215] Changed: #1093 Common default setup values. --- code/nel/tools/build_gamedata/0_setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/nel/tools/build_gamedata/0_setup.py b/code/nel/tools/build_gamedata/0_setup.py index 228dbe2dd..52664460c 100644 --- a/code/nel/tools/build_gamedata/0_setup.py +++ b/code/nel/tools/build_gamedata/0_setup.py @@ -44,11 +44,11 @@ except NameError: try: ToolDirectories except NameError: - ToolDirectories = [ 'R:/code/nel', 'R:/code/ryzom/tools' ] + ToolDirectories = [ 'R:/build/dev/bin/Release', 'D:/libraries/external/bin' ] try: ToolSuffix except NameError: - ToolSuffix = "_r.exe" + ToolSuffix = ".exe" try: ScriptDirectory except NameError: @@ -112,7 +112,7 @@ except NameError: try: WindowsExeDllCfgDirectories except NameError: - WindowsExeDllCfgDirectories = [ 'C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/redist/x86', 'D:/source/external_local/bin/x86', 'D:/source/external_shared/bin/x86', 'R:/code/nel/lib', 'R:/code/ryzom/bin', 'R:/code/ryzom/client', 'R:/code/ryzom/tools/client/client_config/bin' ] + WindowsExeDllCfgDirectories = [ 'C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/redist/x86', 'D:/libraries/external/bin', 'R:/build/dev/bin/Release', 'R:/code/nel/lib', 'R:/code/ryzom/bin', 'R:/code/ryzom/client', 'R:/code/ryzom/tools/client/client_config/bin' ] try: MaxAvailable except NameError: From e6e7e2a37e4151a524d337ea3d70b6bfc59ed0aa Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 16 Sep 2011 14:14:15 +0200 Subject: [PATCH 105/215] Changed: #1093 Changed order of some default build pipeline search paths to work better. --- code/nel/tools/build_gamedata/0_setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/tools/build_gamedata/0_setup.py b/code/nel/tools/build_gamedata/0_setup.py index 52664460c..676bd45fe 100644 --- a/code/nel/tools/build_gamedata/0_setup.py +++ b/code/nel/tools/build_gamedata/0_setup.py @@ -112,7 +112,7 @@ except NameError: try: WindowsExeDllCfgDirectories except NameError: - WindowsExeDllCfgDirectories = [ 'C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/redist/x86', 'D:/libraries/external/bin', 'R:/build/dev/bin/Release', 'R:/code/nel/lib', 'R:/code/ryzom/bin', 'R:/code/ryzom/client', 'R:/code/ryzom/tools/client/client_config/bin' ] + WindowsExeDllCfgDirectories = [ 'C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/redist/x86', 'D:/libraries/external/bin', 'R:/build/dev/bin/Release', 'R:/code/ryzom/client', 'R:/code/nel/lib', 'R:/code/ryzom/bin', 'R:/code/ryzom/tools/client/client_config/bin' ] try: MaxAvailable except NameError: From 0082dad3e5361064a4f9fa9cbdb14f4f36ac4a56 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 17 Sep 2011 11:35:07 +0200 Subject: [PATCH 106/215] Added: #1093 Command line arguments for build gamedata scripts, filter projects and processes, make setup buildsite config skippable, and verify tool paths in setup script. --- code/nel/tools/build_gamedata/0_setup.py | 532 ++++++++++-------- code/nel/tools/build_gamedata/1_export.py | 55 +- code/nel/tools/build_gamedata/2_build.py | 55 +- code/nel/tools/build_gamedata/3_install.py | 55 +- .../build_gamedata/configuration/tools.py | 6 +- .../tools/build_gamedata/processes/0_setup.py | 40 +- .../build_gamedata/processes/1_export.py | 39 +- .../tools/build_gamedata/processes/2_build.py | 39 +- .../build_gamedata/processes/3_install.py | 39 +- 9 files changed, 525 insertions(+), 335 deletions(-) diff --git a/code/nel/tools/build_gamedata/0_setup.py b/code/nel/tools/build_gamedata/0_setup.py index 676bd45fe..1fe084407 100644 --- a/code/nel/tools/build_gamedata/0_setup.py +++ b/code/nel/tools/build_gamedata/0_setup.py @@ -24,9 +24,27 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("configuration") +parser = argparse.ArgumentParser(description='Ryzom Core - Build Gamedata - Setup') +parser.add_argument('--noconf', '-nc', action='store_true') +parser.add_argument('--noverify', '-nv', action='store_true') +# parser.add_argument('--haltonerror', '-eh', action='store_true') +parser.add_argument('--includeproject', '-ipj', nargs='+') +parser.add_argument('--excludeproject', '-epj', nargs='+') +parser.add_argument('--includeprocess', '-ipc', nargs='+') +parser.add_argument('--excludeprocess', '-epc', nargs='+') +args = parser.parse_args() + +if not args.includeproject == None and not args.excludeproject == None: + print "ERROR --includeproject cannot be combined with --excludeproject, exit." + exit() + +if not args.includeprocess == None and not args.excludeprocess == None: + print "ERROR --includeprocess cannot be combined with --excludeprocess, exit." + exit() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -35,221 +53,225 @@ try: from buildsite import * except ImportError: printLog(log, "*** FIRST RUN ***") + if args.noconf: + printLog(log, "ERROR --noconf is invalid on first run, exit.") + exit() from tools import * -try: - BuildQuality -except NameError: - BuildQuality = 1 -try: - ToolDirectories -except NameError: - ToolDirectories = [ 'R:/build/dev/bin/Release', 'D:/libraries/external/bin' ] -try: - ToolSuffix -except NameError: - ToolSuffix = ".exe" -try: - ScriptDirectory -except NameError: - ScriptDirectory = "R:/code/nel/tools/build_gamedata" -try: - WorkspaceDirectory -except NameError: - WorkspaceDirectory = "R:/code/ryzom/tools/build_gamedata/workspace" -try: - DatabaseDirectory -except NameError: - DatabaseDirectory = "W:/database" -try: - ExportBuildDirectory -except NameError: - ExportBuildDirectory = "W:/export" -try: - InstallDirectory -except NameError: - InstallDirectory = "W:/install" -try: - DataShardDirectory -except NameError: - DataShardDirectory = "R:/code/ryzom/server/data_shard" -try: - ClientDevDirectory -except NameError: - ClientDevDirectory = "W:/client_dev" -try: - ClientPatchDirectory -except NameError: - ClientPatchDirectory = "W:/client_patch" -try: - ClientInstallDirectory -except NameError: - ClientInstallDirectory = "W:/client_install" -try: - LeveldesignDirectory -except NameError: - LeveldesignDirectory = "L:/leveldesign" -try: - LeveldesignDfnDirectory -except NameError: - LeveldesignDfnDirectory = "L:/leveldesign/dfn" -try: - LeveldesignWorldDirectory -except NameError: - LeveldesignWorldDirectory = "L:/leveldesign/world" -try: - PrimitivesDirectory -except NameError: - PrimitivesDirectory = "L:/primitives" -try: - GamedevDirectory -except NameError: - GamedevDirectory = "R:/code/ryzom/client/data/gamedev" -try: - DataCommonDirectory -except NameError: - DataCommonDirectory = "R:/code/ryzom/common/data_common" -try: - WindowsExeDllCfgDirectories -except NameError: - WindowsExeDllCfgDirectories = [ 'C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/redist/x86', 'D:/libraries/external/bin', 'R:/build/dev/bin/Release', 'R:/code/ryzom/client', 'R:/code/nel/lib', 'R:/code/ryzom/bin', 'R:/code/ryzom/tools/client/client_config/bin' ] -try: - MaxAvailable -except NameError: - MaxAvailable = 1 -try: - MaxDirectory -except NameError: - MaxDirectory = "C:/Program Files (x86)/Autodesk/3ds Max 2010" -try: - MaxUserDirectory -except NameError: - MaxUserDirectory = "C:/Users/Kaetemi/AppData/Local/Autodesk/3dsMax/2010 - 32bit/enu" -try: - MaxExecutable -except NameError: - MaxExecutable = "3dsmax.exe" +if not args.noconf: + try: + BuildQuality + except NameError: + BuildQuality = 1 + try: + ToolDirectories + except NameError: + ToolDirectories = [ 'R:/build/dev/bin/Release', 'D:/libraries/external/bin' ] + try: + ToolSuffix + except NameError: + ToolSuffix = ".exe" + try: + ScriptDirectory + except NameError: + ScriptDirectory = "R:/code/nel/tools/build_gamedata" + try: + WorkspaceDirectory + except NameError: + WorkspaceDirectory = "R:/code/ryzom/tools/build_gamedata/workspace" + try: + DatabaseDirectory + except NameError: + DatabaseDirectory = "W:/database" + try: + ExportBuildDirectory + except NameError: + ExportBuildDirectory = "W:/export" + try: + InstallDirectory + except NameError: + InstallDirectory = "W:/install" + try: + DataShardDirectory + except NameError: + DataShardDirectory = "R:/code/ryzom/server/data_shard" + try: + ClientDevDirectory + except NameError: + ClientDevDirectory = "W:/client_dev" + try: + ClientPatchDirectory + except NameError: + ClientPatchDirectory = "W:/client_patch" + try: + ClientInstallDirectory + except NameError: + ClientInstallDirectory = "W:/client_install" + try: + LeveldesignDirectory + except NameError: + LeveldesignDirectory = "L:/leveldesign" + try: + LeveldesignDfnDirectory + except NameError: + LeveldesignDfnDirectory = "L:/leveldesign/dfn" + try: + LeveldesignWorldDirectory + except NameError: + LeveldesignWorldDirectory = "L:/leveldesign/world" + try: + PrimitivesDirectory + except NameError: + PrimitivesDirectory = "L:/primitives" + try: + GamedevDirectory + except NameError: + GamedevDirectory = "R:/code/ryzom/client/data/gamedev" + try: + DataCommonDirectory + except NameError: + DataCommonDirectory = "R:/code/ryzom/common/data_common" + try: + WindowsExeDllCfgDirectories + except NameError: + WindowsExeDllCfgDirectories = [ 'C:/Program Files (x86)/Microsoft Visual Studio 9.0/VC/redist/x86', 'D:/libraries/external/bin', 'R:/build/dev/bin/Release', 'R:/code/ryzom/client', 'R:/code/nel/lib', 'R:/code/ryzom/bin', 'R:/code/ryzom/tools/client/client_config/bin' ] + try: + MaxAvailable + except NameError: + MaxAvailable = 1 + try: + MaxDirectory + except NameError: + MaxDirectory = "C:/Program Files (x86)/Autodesk/3ds Max 2010" + try: + MaxUserDirectory + except NameError: + MaxUserDirectory = "C:/Users/Kaetemi/AppData/Local/Autodesk/3dsMax/2010 - 32bit/enu" + try: + MaxExecutable + except NameError: + MaxExecutable = "3dsmax.exe" -printLog(log, "") -printLog(log, "-------") -printLog(log, "--- Setup build site") -printLog(log, "-------") -printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) -printLog(log, "") -printLog(log, "This script will set up the buildsite configuration, and create needed directories.") -printLog(log, "To use the defaults, simply hit ENTER, else type in the new value.") -printLog(log, "Use -- if you need to insert an empty value.") -printLog(log, "") -BuildQuality = int(askVar(log, "Build Quality", str(BuildQuality))) -ToolDirectories[0] = askVar(log, "Primary Tool Directory", ToolDirectories[0]).replace("\\", "/") -ToolDirectories[1] = askVar(log, "Secondary Tool Directory", ToolDirectories[1]).replace("\\", "/") -ToolSuffix = askVar(log, "Tool Suffix", ToolSuffix) -ScriptDirectory = askVar(log, "Script Directory", os.getcwd().replace("\\", "/")).replace("\\", "/") -WorkspaceDirectory = askVar(log, "Workspace Directory", WorkspaceDirectory).replace("\\", "/") -DatabaseDirectory = askVar(log, "Database Directory", DatabaseDirectory).replace("\\", "/") -ExportBuildDirectory = askVar(log, "Export Build Directory", ExportBuildDirectory).replace("\\", "/") -InstallDirectory = askVar(log, "Install Directory", InstallDirectory).replace("\\", "/") -DataShardDirectory = askVar(log, "Data Shard Directory", DataShardDirectory).replace("\\", "/") -ClientDevDirectory = askVar(log, "Client Dev Directory", ClientDevDirectory).replace("\\", "/") -ClientPatchDirectory = askVar(log, "Client Patch Directory", ClientPatchDirectory).replace("\\", "/") -ClientInstallDirectory = askVar(log, "Client Install Directory", ClientInstallDirectory).replace("\\", "/") -LeveldesignDirectory = askVar(log, "Leveldesign Directory", LeveldesignDirectory).replace("\\", "/") -LeveldesignDfnDirectory = askVar(log, "Leveldesign DFN Directory", LeveldesignDfnDirectory).replace("\\", "/") -LeveldesignWorldDirectory = askVar(log, "Leveldesign World Directory", LeveldesignWorldDirectory).replace("\\", "/") -PrimitivesDirectory = askVar(log, "Primitives Directory", PrimitivesDirectory).replace("\\", "/") -GamedevDirectory = askVar(log, "Gamedev Directory", GamedevDirectory).replace("\\", "/") -DataCommonDirectory = askVar(log, "Data Common Directory", DataCommonDirectory).replace("\\", "/") -WindowsExeDllCfgDirectories[0] = askVar(log, "Primary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[0]).replace("\\", "/") -WindowsExeDllCfgDirectories[1] = askVar(log, "Secondary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[1]).replace("\\", "/") -WindowsExeDllCfgDirectories[2] = askVar(log, "Tertiary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[2]).replace("\\", "/") -WindowsExeDllCfgDirectories[3] = askVar(log, "Quaternary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[3]).replace("\\", "/") -WindowsExeDllCfgDirectories[4] = askVar(log, "Quinary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[4]).replace("\\", "/") -WindowsExeDllCfgDirectories[5] = askVar(log, "Senary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[5]).replace("\\", "/") -WindowsExeDllCfgDirectories[6] = askVar(log, "Septenary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[6]).replace("\\", "/") -MaxAvailable = int(askVar(log, "3dsMax Available", str(MaxAvailable))) -if MaxAvailable: - MaxDirectory = askVar(log, "3dsMax Directory", MaxDirectory).replace("\\", "/") - MaxUserDirectory = askVar(log, "3dsMax User Directory", MaxUserDirectory).replace("\\", "/") - MaxExecutable = askVar(log, "3dsMax Executable", MaxExecutable) -if os.path.isfile("configuration/buildsite.py"): - os.remove("configuration/buildsite.py") -sf = open("configuration/buildsite.py", "w") -sf.write("#!/usr/bin/python\n") -sf.write("# \n") -sf.write("# \\file site.py\n") -sf.write("# \\brief Site configuration\n") -sf.write("# \\date " + time.strftime("%Y-%m-%d-%H-%M-GMT", time.gmtime(time.time())) + "\n") -sf.write("# \\author Jan Boon (Kaetemi)\n") -sf.write("# Python port of game data build pipeline.\n") -sf.write("# Site configuration.\n") -sf.write("# \n") -sf.write("# NeL - MMORPG Framework \n") -sf.write("# Copyright (C) 2010 Winch Gate Property Limited\n") -sf.write("# \n") -sf.write("# This program is free software: you can redistribute it and/or modify\n") -sf.write("# it under the terms of the GNU Affero General Public License as\n") -sf.write("# published by the Free Software Foundation, either version 3 of the\n") -sf.write("# License, or (at your option) any later version.\n") -sf.write("# \n") -sf.write("# This program is distributed in the hope that it will be useful,\n") -sf.write("# but WITHOUT ANY WARRANTY; without even the implied warranty of\n") -sf.write("# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n") -sf.write("# GNU Affero General Public License for more details.\n") -sf.write("# \n") -sf.write("# You should have received a copy of the GNU Affero General Public License\n") -sf.write("# along with this program. If not, see .\n") -sf.write("# \n") -sf.write("\n") -sf.write("\n") -sf.write("# *** SITE INSTALLATION ***\n") -sf.write("\n") -sf.write("# Use '/' in path name, not '\'\n") -sf.write("# Don't put '/' at the end of a directory name\n") -sf.write("\n") -sf.write("\n") -sf.write("# Quality option for this site (1 for BEST, 0 for DRAFT)\n") -sf.write("BuildQuality = " + str(BuildQuality) + "\n") -sf.write("\n") -sf.write("ToolDirectories = " + str(ToolDirectories) + "\n") -sf.write("ToolSuffix = \"" + str(ToolSuffix) + "\"\n") -sf.write("\n") -sf.write("# Build script directory\n") -sf.write("ScriptDirectory = \"" + str(ScriptDirectory) + "\"\n") -sf.write("WorkspaceDirectory = \"" + str(WorkspaceDirectory) + "\"\n") -sf.write("\n") -sf.write("# Data build directories\n") -sf.write("DatabaseDirectory = \"" + str(DatabaseDirectory) + "\"\n") -sf.write("ExportBuildDirectory = \"" + str(ExportBuildDirectory) + "\"\n") -sf.write("\n") -sf.write("# Install directories\n") -sf.write("InstallDirectory = \"" + str(InstallDirectory) + "\"\n") -sf.write("DataShardDirectory = \"" + str(DataShardDirectory) + "\"\n") -sf.write("ClientDevDirectory = \"" + str(ClientDevDirectory) + "\"\n") -sf.write("ClientPatchDirectory = \"" + str(ClientPatchDirectory) + "\"\n") -sf.write("ClientInstallDirectory = \"" + str(ClientInstallDirectory) + "\"\n") -sf.write("\n") -sf.write("# TODO: NETWORK RECONNECT NOT IMPLEMENTED :)\n") -sf.write("\n") -sf.write("# Leveldesign directories\n") -sf.write("LeveldesignDirectory = \"" + str(LeveldesignDirectory) + "\"\n") -sf.write("LeveldesignDfnDirectory = \"" + str(LeveldesignDfnDirectory) + "\"\n") -sf.write("LeveldesignWorldDirectory = \"" + str(LeveldesignWorldDirectory) + "\"\n") -sf.write("PrimitivesDirectory = \"" + str(PrimitivesDirectory) + "\"\n") -sf.write("\n") -sf.write("# Misc data directories\n") -sf.write("GamedevDirectory = \"" + str(GamedevDirectory) + "\"\n") -sf.write("DataCommonDirectory = \"" + str(DataCommonDirectory) + "\"\n") -sf.write("WindowsExeDllCfgDirectories = " + str(WindowsExeDllCfgDirectories) + "\n") -sf.write("\n") -sf.write("# 3dsMax directives\n") -sf.write("MaxAvailable = " + str(MaxAvailable) + "\n") -sf.write("MaxDirectory = \"" + str(MaxDirectory) + "\"\n") -sf.write("MaxUserDirectory = \"" + str(MaxUserDirectory) + "\"\n") -sf.write("MaxExecutable = \"" + str(MaxExecutable) + "\"\n") -sf.write("\n") -sf.write("\n") -sf.write("# end of file\n") -sf.close() + printLog(log, "") + printLog(log, "-------") + printLog(log, "--- Setup build site") + printLog(log, "-------") + printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) + printLog(log, "") + printLog(log, "This script will set up the buildsite configuration, and create needed directories.") + printLog(log, "To use the defaults, simply hit ENTER, else type in the new value.") + printLog(log, "Use -- if you need to insert an empty value.") + printLog(log, "") + BuildQuality = int(askVar(log, "Build Quality", str(BuildQuality))) + ToolDirectories[0] = askVar(log, "Primary Tool Directory", ToolDirectories[0]).replace("\\", "/") + ToolDirectories[1] = askVar(log, "Secondary Tool Directory", ToolDirectories[1]).replace("\\", "/") + ToolSuffix = askVar(log, "Tool Suffix", ToolSuffix) + ScriptDirectory = askVar(log, "Script Directory", os.getcwd().replace("\\", "/")).replace("\\", "/") + WorkspaceDirectory = askVar(log, "Workspace Directory", WorkspaceDirectory).replace("\\", "/") + DatabaseDirectory = askVar(log, "Database Directory", DatabaseDirectory).replace("\\", "/") + ExportBuildDirectory = askVar(log, "Export Build Directory", ExportBuildDirectory).replace("\\", "/") + InstallDirectory = askVar(log, "Install Directory", InstallDirectory).replace("\\", "/") + DataShardDirectory = askVar(log, "Data Shard Directory", DataShardDirectory).replace("\\", "/") + ClientDevDirectory = askVar(log, "Client Dev Directory", ClientDevDirectory).replace("\\", "/") + ClientPatchDirectory = askVar(log, "Client Patch Directory", ClientPatchDirectory).replace("\\", "/") + ClientInstallDirectory = askVar(log, "Client Install Directory", ClientInstallDirectory).replace("\\", "/") + LeveldesignDirectory = askVar(log, "Leveldesign Directory", LeveldesignDirectory).replace("\\", "/") + LeveldesignDfnDirectory = askVar(log, "Leveldesign DFN Directory", LeveldesignDfnDirectory).replace("\\", "/") + LeveldesignWorldDirectory = askVar(log, "Leveldesign World Directory", LeveldesignWorldDirectory).replace("\\", "/") + PrimitivesDirectory = askVar(log, "Primitives Directory", PrimitivesDirectory).replace("\\", "/") + GamedevDirectory = askVar(log, "Gamedev Directory", GamedevDirectory).replace("\\", "/") + DataCommonDirectory = askVar(log, "Data Common Directory", DataCommonDirectory).replace("\\", "/") + WindowsExeDllCfgDirectories[0] = askVar(log, "Primary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[0]).replace("\\", "/") + WindowsExeDllCfgDirectories[1] = askVar(log, "Secondary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[1]).replace("\\", "/") + WindowsExeDllCfgDirectories[2] = askVar(log, "Tertiary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[2]).replace("\\", "/") + WindowsExeDllCfgDirectories[3] = askVar(log, "Quaternary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[3]).replace("\\", "/") + WindowsExeDllCfgDirectories[4] = askVar(log, "Quinary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[4]).replace("\\", "/") + WindowsExeDllCfgDirectories[5] = askVar(log, "Senary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[5]).replace("\\", "/") + WindowsExeDllCfgDirectories[6] = askVar(log, "Septenary Windows exe/dll/cfg Directory", WindowsExeDllCfgDirectories[6]).replace("\\", "/") + MaxAvailable = int(askVar(log, "3dsMax Available", str(MaxAvailable))) + if MaxAvailable: + MaxDirectory = askVar(log, "3dsMax Directory", MaxDirectory).replace("\\", "/") + MaxUserDirectory = askVar(log, "3dsMax User Directory", MaxUserDirectory).replace("\\", "/") + MaxExecutable = askVar(log, "3dsMax Executable", MaxExecutable) + if os.path.isfile("configuration/buildsite.py"): + os.remove("configuration/buildsite.py") + sf = open("configuration/buildsite.py", "w") + sf.write("#!/usr/bin/python\n") + sf.write("# \n") + sf.write("# \\file site.py\n") + sf.write("# \\brief Site configuration\n") + sf.write("# \\date " + time.strftime("%Y-%m-%d-%H-%M-GMT", time.gmtime(time.time())) + "\n") + sf.write("# \\author Jan Boon (Kaetemi)\n") + sf.write("# Python port of game data build pipeline.\n") + sf.write("# Site configuration.\n") + sf.write("# \n") + sf.write("# NeL - MMORPG Framework \n") + sf.write("# Copyright (C) 2010 Winch Gate Property Limited\n") + sf.write("# \n") + sf.write("# This program is free software: you can redistribute it and/or modify\n") + sf.write("# it under the terms of the GNU Affero General Public License as\n") + sf.write("# published by the Free Software Foundation, either version 3 of the\n") + sf.write("# License, or (at your option) any later version.\n") + sf.write("# \n") + sf.write("# This program is distributed in the hope that it will be useful,\n") + sf.write("# but WITHOUT ANY WARRANTY; without even the implied warranty of\n") + sf.write("# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n") + sf.write("# GNU Affero General Public License for more details.\n") + sf.write("# \n") + sf.write("# You should have received a copy of the GNU Affero General Public License\n") + sf.write("# along with this program. If not, see .\n") + sf.write("# \n") + sf.write("\n") + sf.write("\n") + sf.write("# *** SITE INSTALLATION ***\n") + sf.write("\n") + sf.write("# Use '/' in path name, not '\'\n") + sf.write("# Don't put '/' at the end of a directory name\n") + sf.write("\n") + sf.write("\n") + sf.write("# Quality option for this site (1 for BEST, 0 for DRAFT)\n") + sf.write("BuildQuality = " + str(BuildQuality) + "\n") + sf.write("\n") + sf.write("ToolDirectories = " + str(ToolDirectories) + "\n") + sf.write("ToolSuffix = \"" + str(ToolSuffix) + "\"\n") + sf.write("\n") + sf.write("# Build script directory\n") + sf.write("ScriptDirectory = \"" + str(ScriptDirectory) + "\"\n") + sf.write("WorkspaceDirectory = \"" + str(WorkspaceDirectory) + "\"\n") + sf.write("\n") + sf.write("# Data build directories\n") + sf.write("DatabaseDirectory = \"" + str(DatabaseDirectory) + "\"\n") + sf.write("ExportBuildDirectory = \"" + str(ExportBuildDirectory) + "\"\n") + sf.write("\n") + sf.write("# Install directories\n") + sf.write("InstallDirectory = \"" + str(InstallDirectory) + "\"\n") + sf.write("DataShardDirectory = \"" + str(DataShardDirectory) + "\"\n") + sf.write("ClientDevDirectory = \"" + str(ClientDevDirectory) + "\"\n") + sf.write("ClientPatchDirectory = \"" + str(ClientPatchDirectory) + "\"\n") + sf.write("ClientInstallDirectory = \"" + str(ClientInstallDirectory) + "\"\n") + sf.write("\n") + sf.write("# TODO: NETWORK RECONNECT NOT IMPLEMENTED :)\n") + sf.write("\n") + sf.write("# Leveldesign directories\n") + sf.write("LeveldesignDirectory = \"" + str(LeveldesignDirectory) + "\"\n") + sf.write("LeveldesignDfnDirectory = \"" + str(LeveldesignDfnDirectory) + "\"\n") + sf.write("LeveldesignWorldDirectory = \"" + str(LeveldesignWorldDirectory) + "\"\n") + sf.write("PrimitivesDirectory = \"" + str(PrimitivesDirectory) + "\"\n") + sf.write("\n") + sf.write("# Misc data directories\n") + sf.write("GamedevDirectory = \"" + str(GamedevDirectory) + "\"\n") + sf.write("DataCommonDirectory = \"" + str(DataCommonDirectory) + "\"\n") + sf.write("WindowsExeDllCfgDirectories = " + str(WindowsExeDllCfgDirectories) + "\n") + sf.write("\n") + sf.write("# 3dsMax directives\n") + sf.write("MaxAvailable = " + str(MaxAvailable) + "\n") + sf.write("MaxDirectory = \"" + str(MaxDirectory) + "\"\n") + sf.write("MaxUserDirectory = \"" + str(MaxUserDirectory) + "\"\n") + sf.write("MaxExecutable = \"" + str(MaxExecutable) + "\"\n") + sf.write("\n") + sf.write("\n") + sf.write("# end of file\n") + sf.close() sys.path.append(WorkspaceDirectory) from projects import * @@ -262,27 +284,83 @@ printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") # For each project for projectName in ProjectsToProcess: - os.putenv("NELBUILDACTIVEPROJECT", os.path.abspath(WorkspaceDirectory + "/" + projectName)) - os.chdir("processes") - try: - subprocess.call([ "python", "0_setup.py" ]) - except Exception, e: - printLog(log, "<" + projectName + "> " + str(e)) - os.chdir("..") - try: - projectLog = open("processes/log.log", "r") - projectLogData = projectLog.read() - projectLog.close() - log.write(projectLogData) - except Exception, e: - printLog(log, "<" + projectName + "> " + str(e)) + if ((args.includeproject == None or projectName in args.includeproject) and (args.excludeproject == None or not projectName in args.excludeproject)): + printLog(log, "PROJECT " + projectName) + os.putenv("NELBUILDACTIVEPROJECT", os.path.abspath(WorkspaceDirectory + "/" + projectName)) + os.chdir("processes") + try: + if not args.includeprocess == None: + subprocess.call([ "python", "0_setup.py", "--includeprocess" ] + args.includeprocess) + elif not args.excludeprocess == None: + subprocess.call([ "python", "0_setup.py", "--excludeprocess" ] + args.excludeprocess) + else: + subprocess.call([ "python", "0_setup.py" ]) + except Exception, e: + printLog(log, "<" + projectName + "> " + str(e)) + os.chdir("..") + try: + projectLog = open("processes/log.log", "r") + projectLogData = projectLog.read() + projectLog.close() + log.write(projectLogData) + except Exception, e: + printLog(log, "<" + projectName + "> " + str(e)) + else: + printLog(log, "IGNORE PROJECT " + projectName) printLog(log, "") + # Additional directories printLog(log, ">>> Setup additional directories <<<") mkPath(log, ClientDevDirectory) mkPath(log, ClientPatchDirectory) mkPath(log, ClientInstallDirectory) +if not args.noverify: + printLog(log, "") + printLog(log, "-------") + printLog(log, "--- Verify tool paths") + printLog(log, "-------") + printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) + printLog(log, "") + if MaxAvailable: + findMax(log, MaxDirectory, MaxExecutable) + findTool(log, ToolDirectories, TgaToDdsTool, ToolSuffix) + findTool(log, ToolDirectories, BuildInterfaceTool, ToolSuffix) + findTool(log, ToolDirectories, ExecTimeoutTool, ToolSuffix) + findTool(log, ToolDirectories, BuildSmallbankTool, ToolSuffix) + findTool(log, ToolDirectories, BuildFarbankTool, ToolSuffix) + findTool(log, ToolDirectories, ZoneDependenciesTool, ToolSuffix) + findTool(log, ToolDirectories, ZoneWelderTool, ToolSuffix) + findTool(log, ToolDirectories, BuildRbankTool, ToolSuffix) + findTool(log, ToolDirectories, BuildIndoorRbankTool, ToolSuffix) + findTool(log, ToolDirectories, BuildIgBoxesTool, ToolSuffix) + findTool(log, ToolDirectories, GetNeighborsTool, ToolSuffix) + findTool(log, ToolDirectories, ZoneLighterTool, ToolSuffix) + findTool(log, ToolDirectories, ZoneIgLighterTool, ToolSuffix) + findTool(log, ToolDirectories, IgLighterTool, ToolSuffix) + findTool(log, ToolDirectories, AnimBuilderTool, ToolSuffix) + findTool(log, ToolDirectories, TileEditTool, ToolSuffix) + # findTool(log, ToolDirectories, BuildImagesetTool, ToolSuffix) # kaetemi stuff, ignore this + findTool(log, ToolDirectories, MakeSheetIdTool, ToolSuffix) + # findTool(log, ToolDirectories, BuildSheetsTool, ToolSuffix) # kaetemi stuff, ignore this + # findTool(log, ToolDirectories, BuildSoundTool, ToolSuffix) # kaetemi stuff, ignore this + findTool(log, ToolDirectories, BuildCoarseMeshTool, ToolSuffix) + findTool(log, ToolDirectories, LightmapOptimizerTool, ToolSuffix) + findTool(log, ToolDirectories, BuildClodtexTool, ToolSuffix) + findTool(log, ToolDirectories, BuildShadowSkinTool, ToolSuffix) + findTool(log, ToolDirectories, PanoplyMakerTool, ToolSuffix) + findTool(log, ToolDirectories, HlsBankMakerTool, ToolSuffix) + findTool(log, ToolDirectories, LandExportTool, ToolSuffix) + findTool(log, ToolDirectories, PrimExportTool, ToolSuffix) + findTool(log, ToolDirectories, IgElevationTool, ToolSuffix) + findTool(log, ToolDirectories, IgAddTool, ToolSuffix) + findTool(log, ToolDirectories, BuildClodBankTool, ToolSuffix) + findTool(log, ToolDirectories, SheetsPackerTool, ToolSuffix) + findTool(log, ToolDirectories, BnpMakeTool, ToolSuffix) + findTool(log, ToolDirectories, AiBuildWmapTool, ToolSuffix) + findTool(log, ToolDirectories, TgaCutTool, ToolSuffix) + findTool(log, ToolDirectories, PatchGenTool, ToolSuffix) + log.close() if os.path.isfile("0_setup.log"): os.remove("0_setup.log") diff --git a/code/nel/tools/build_gamedata/1_export.py b/code/nel/tools/build_gamedata/1_export.py index 0fb684592..6abe0a3a5 100644 --- a/code/nel/tools/build_gamedata/1_export.py +++ b/code/nel/tools/build_gamedata/1_export.py @@ -24,9 +24,25 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("configuration") +parser = argparse.ArgumentParser(description='Ryzom Core - Build Gamedata - Export') +# parser.add_argument('--haltonerror', '-eh', action='store_true') +parser.add_argument('--includeproject', '-ipj', nargs='+') +parser.add_argument('--excludeproject', '-epj', nargs='+') +parser.add_argument('--includeprocess', '-ipc', nargs='+') +parser.add_argument('--excludeprocess', '-epc', nargs='+') +args = parser.parse_args() + +if not args.includeproject == None and not args.excludeproject == None: + print "ERROR --includeproject cannot be combined with --excludeproject, exit." + exit() + +if not args.includeprocess == None and not args.excludeprocess == None: + print "ERROR --includeprocess cannot be combined with --excludeprocess, exit." + exit() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -46,20 +62,29 @@ printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") # For each project for projectName in ProjectsToProcess: - os.putenv("NELBUILDACTIVEPROJECT", os.path.abspath(WorkspaceDirectory + "/" + projectName)) - os.chdir("processes") - try: - subprocess.call([ "python", "1_export.py" ]) - except Exception, e: - printLog(log, "<" + projectName + "> " + str(e)) - os.chdir("..") - try: - projectLog = open("processes/log.log", "r") - projectLogData = projectLog.read() - projectLog.close() - log.write(projectLogData) - except Exception, e: - printLog(log, "<" + projectName + "> " + str(e)) + if ((args.includeproject == None or projectName in args.includeproject) and (args.excludeproject == None or not projectName in args.excludeproject)): + printLog(log, "PROJECT " + projectName) + os.putenv("NELBUILDACTIVEPROJECT", os.path.abspath(WorkspaceDirectory + "/" + projectName)) + os.chdir("processes") + try: + if not args.includeprocess == None: + subprocess.call([ "python", "1_export.py", "--includeprocess" ] + args.includeprocess) + elif not args.excludeprocess == None: + subprocess.call([ "python", "1_export.py", "--excludeprocess" ] + args.excludeprocess) + else: + subprocess.call([ "python", "1_export.py" ]) + except Exception, e: + printLog(log, "<" + projectName + "> " + str(e)) + os.chdir("..") + try: + projectLog = open("processes/log.log", "r") + projectLogData = projectLog.read() + projectLog.close() + log.write(projectLogData) + except Exception, e: + printLog(log, "<" + projectName + "> " + str(e)) + else: + printLog(log, "IGNORE PROJECT " + projectName) printLog(log, "") log.close() diff --git a/code/nel/tools/build_gamedata/2_build.py b/code/nel/tools/build_gamedata/2_build.py index a6229c29c..36a171161 100644 --- a/code/nel/tools/build_gamedata/2_build.py +++ b/code/nel/tools/build_gamedata/2_build.py @@ -24,9 +24,25 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("configuration") +parser = argparse.ArgumentParser(description='Ryzom Core - Build Gamedata - Build') +# parser.add_argument('--haltonerror', '-eh', action='store_true') +parser.add_argument('--includeproject', '-ipj', nargs='+') +parser.add_argument('--excludeproject', '-epj', nargs='+') +parser.add_argument('--includeprocess', '-ipc', nargs='+') +parser.add_argument('--excludeprocess', '-epc', nargs='+') +args = parser.parse_args() + +if not args.includeproject == None and not args.excludeproject == None: + print "ERROR --includeproject cannot be combined with --excludeproject, exit." + exit() + +if not args.includeprocess == None and not args.excludeprocess == None: + print "ERROR --includeprocess cannot be combined with --excludeprocess, exit." + exit() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -46,20 +62,29 @@ printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") # For each project for projectName in ProjectsToProcess: - os.putenv("NELBUILDACTIVEPROJECT", os.path.abspath(WorkspaceDirectory + "/" + projectName)) - os.chdir("processes") - try: - subprocess.call([ "python", "2_build.py" ]) - except Exception, e: - printLog(log, "<" + projectName + "> " + str(e)) - os.chdir("..") - try: - projectLog = open("processes/log.log", "r") - projectLogData = projectLog.read() - projectLog.close() - log.write(projectLogData) - except Exception, e: - printLog(log, "<" + projectName + "> " + str(e)) + if ((args.includeproject == None or projectName in args.includeproject) and (args.excludeproject == None or not projectName in args.excludeproject)): + printLog(log, "PROJECT " + projectName) + os.putenv("NELBUILDACTIVEPROJECT", os.path.abspath(WorkspaceDirectory + "/" + projectName)) + os.chdir("processes") + try: + if not args.includeprocess == None: + subprocess.call([ "python", "2_build.py", "--includeprocess" ] + args.includeprocess) + elif not args.excludeprocess == None: + subprocess.call([ "python", "2_build.py", "--excludeprocess" ] + args.excludeprocess) + else: + subprocess.call([ "python", "2_build.py" ]) + except Exception, e: + printLog(log, "<" + projectName + "> " + str(e)) + os.chdir("..") + try: + projectLog = open("processes/log.log", "r") + projectLogData = projectLog.read() + projectLog.close() + log.write(projectLogData) + except Exception, e: + printLog(log, "<" + projectName + "> " + str(e)) + else: + printLog(log, "IGNORE PROJECT " + projectName) printLog(log, "") log.close() diff --git a/code/nel/tools/build_gamedata/3_install.py b/code/nel/tools/build_gamedata/3_install.py index 70ece522b..39a19dea3 100644 --- a/code/nel/tools/build_gamedata/3_install.py +++ b/code/nel/tools/build_gamedata/3_install.py @@ -24,9 +24,25 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("configuration") +parser = argparse.ArgumentParser(description='Ryzom Core - Build Gamedata - Install') +# parser.add_argument('--haltonerror', '-eh', action='store_true') +parser.add_argument('--includeproject', '-ipj', nargs='+') +parser.add_argument('--excludeproject', '-epj', nargs='+') +parser.add_argument('--includeprocess', '-ipc', nargs='+') +parser.add_argument('--excludeprocess', '-epc', nargs='+') +args = parser.parse_args() + +if not args.includeproject == None and not args.excludeproject == None: + print "ERROR --includeproject cannot be combined with --excludeproject, exit." + exit() + +if not args.includeprocess == None and not args.excludeprocess == None: + print "ERROR --includeprocess cannot be combined with --excludeprocess, exit." + exit() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -46,20 +62,29 @@ printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") # For each project for projectName in ProjectsToProcess: - os.putenv("NELBUILDACTIVEPROJECT", os.path.abspath(WorkspaceDirectory + "/" + projectName)) - os.chdir("processes") - try: - subprocess.call([ "python", "3_install.py" ]) - except Exception, e: - printLog(log, "<" + projectName + "> " + str(e)) - os.chdir("..") - try: - projectLog = open("processes/log.log", "r") - projectLogData = projectLog.read() - projectLog.close() - log.write(projectLogData) - except Exception, e: - printLog(log, "<" + projectName + "> " + str(e)) + if ((args.includeproject == None or projectName in args.includeproject) and (args.excludeproject == None or not projectName in args.excludeproject)): + printLog(log, "PROJECT " + projectName) + os.putenv("NELBUILDACTIVEPROJECT", os.path.abspath(WorkspaceDirectory + "/" + projectName)) + os.chdir("processes") + try: + if not args.includeprocess == None: + subprocess.call([ "python", "3_install.py", "--includeprocess" ] + args.includeprocess) + elif not args.excludeprocess == None: + subprocess.call([ "python", "3_install.py", "--excludeprocess" ] + args.excludeprocess) + else: + subprocess.call([ "python", "3_install.py" ]) + except Exception, e: + printLog(log, "<" + projectName + "> " + str(e)) + os.chdir("..") + try: + projectLog = open("processes/log.log", "r") + projectLogData = projectLog.read() + projectLog.close() + log.write(projectLogData) + except Exception, e: + printLog(log, "<" + projectName + "> " + str(e)) + else: + printLog(log, "IGNORE PROJECT " + projectName) printLog(log, "") log.close() diff --git a/code/nel/tools/build_gamedata/configuration/tools.py b/code/nel/tools/build_gamedata/configuration/tools.py index e533270c1..5eff45977 100644 --- a/code/nel/tools/build_gamedata/configuration/tools.py +++ b/code/nel/tools/build_gamedata/configuration/tools.py @@ -69,10 +69,10 @@ ZoneIgLighterTool = "zone_ig_lighter" IgLighterTool = "ig_lighter" AnimBuilderTool = "anim_builder" TileEditTool = "tile_edit" -BuildImagesetTool = "th_build_imageset" +# BuildImagesetTool = "th_build_imageset" # kaetemi stuff, ignore this MakeSheetIdTool = "make_sheet_id" -BuildSheetsTool = "th_build_sheets" -BuildSoundTool = "th_build_sound" +# BuildSheetsTool = "th_build_sheets" # kaetemi stuff, ignore this +# BuildSoundTool = "th_build_sound" # kaetemi stuff, ignore this BuildCoarseMeshTool = "build_coarse_mesh" LightmapOptimizerTool = "lightmap_optimizer" BuildClodtexTool = "build_clodtex" diff --git a/code/nel/tools/build_gamedata/processes/0_setup.py b/code/nel/tools/build_gamedata/processes/0_setup.py index cb6de2abb..213158c92 100644 --- a/code/nel/tools/build_gamedata/processes/0_setup.py +++ b/code/nel/tools/build_gamedata/processes/0_setup.py @@ -24,8 +24,14 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("../configuration") + +parser = argparse.ArgumentParser() +parser.add_argument('--includeprocess', '-ipc', nargs='+') +parser.add_argument('--excludeprocess', '-epc', nargs='+') +args = parser.parse_args() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -60,20 +66,24 @@ printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") # For each process for processName in ProcessToComplete: - os.chdir(processName) - try: - subprocess.call([ "python", "0_setup.py" ]) - except Exception, e: - printLog(log, "<" + processName + "> " + str(e)) - os.chdir("..") - try: - processLog = open(processName + "/log.log", "r") - processLogData = processLog.read() - processLog.close() - log.write(processLogData) - except Exception, e: - printLog(log, "<" + processName + "> " + str(e)) - # subprocess.call("idle.bat") + if ((args.includeprocess == None or processName in args.includeprocess) and (args.excludeprocess == None or not processName in args.excludeprocess)): + printLog(log, "PROCESS " + processName) + os.chdir(processName) + try: + subprocess.call([ "python", "0_setup.py" ]) + except Exception, e: + printLog(log, "<" + processName + "> " + str(e)) + os.chdir("..") + try: + processLog = open(processName + "/log.log", "r") + processLogData = processLog.read() + processLog.close() + log.write(processLogData) + except Exception, e: + printLog(log, "<" + processName + "> " + str(e)) + # subprocess.call("idle.bat") + else: + printLog(log, "IGNORE PROCESS " + processName) printLog(log, "") log.close() diff --git a/code/nel/tools/build_gamedata/processes/1_export.py b/code/nel/tools/build_gamedata/processes/1_export.py index fdd497720..766639f17 100644 --- a/code/nel/tools/build_gamedata/processes/1_export.py +++ b/code/nel/tools/build_gamedata/processes/1_export.py @@ -24,9 +24,14 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("../configuration") +parser = argparse.ArgumentParser() +parser.add_argument('--includeprocess', '-ipc', nargs='+') +parser.add_argument('--excludeprocess', '-epc', nargs='+') +args = parser.parse_args() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -45,20 +50,24 @@ printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") # For each process for processName in ProcessToComplete: - os.chdir(processName) - try: - subprocess.call([ "python", "1_export.py" ]) - except Exception, e: - printLog(log, "<" + processName + "> " + str(e)) - os.chdir("..") - try: - processLog = open(processName + "/log.log", "r") - processLogData = processLog.read() - processLog.close() - log.write(processLogData) - except Exception, e: - printLog(log, "<" + processName + "> " + str(e)) - # subprocess.call("idle.bat") + if ((args.includeprocess == None or processName in args.includeprocess) and (args.excludeprocess == None or not processName in args.excludeprocess)): + printLog(log, "PROCESS " + processName) + os.chdir(processName) + try: + subprocess.call([ "python", "1_export.py" ]) + except Exception, e: + printLog(log, "<" + processName + "> " + str(e)) + os.chdir("..") + try: + processLog = open(processName + "/log.log", "r") + processLogData = processLog.read() + processLog.close() + log.write(processLogData) + except Exception, e: + printLog(log, "<" + processName + "> " + str(e)) + # subprocess.call("idle.bat") + else: + printLog(log, "IGNORE PROCESS " + processName) printLog(log, "") log.close() diff --git a/code/nel/tools/build_gamedata/processes/2_build.py b/code/nel/tools/build_gamedata/processes/2_build.py index 28ee3147c..4ad70b2f4 100644 --- a/code/nel/tools/build_gamedata/processes/2_build.py +++ b/code/nel/tools/build_gamedata/processes/2_build.py @@ -24,9 +24,14 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("../configuration") +parser = argparse.ArgumentParser() +parser.add_argument('--includeprocess', '-ipc', nargs='+') +parser.add_argument('--excludeprocess', '-epc', nargs='+') +args = parser.parse_args() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -45,20 +50,24 @@ printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") # For each process for processName in ProcessToComplete: - os.chdir(processName) - try: - subprocess.call([ "python", "2_build.py" ]) - except Exception, e: - printLog(log, "<" + processName + "> " + str(e)) - os.chdir("..") - try: - processLog = open(processName + "/log.log", "r") - processLogData = processLog.read() - processLog.close() - log.write(processLogData) - except Exception, e: - printLog(log, "<" + processName + "> " + str(e)) - # subprocess.call("idle.bat") + if ((args.includeprocess == None or processName in args.includeprocess) and (args.excludeprocess == None or not processName in args.excludeprocess)): + printLog(log, "PROCESS " + processName) + os.chdir(processName) + try: + subprocess.call([ "python", "2_build.py" ]) + except Exception, e: + printLog(log, "<" + processName + "> " + str(e)) + os.chdir("..") + try: + processLog = open(processName + "/log.log", "r") + processLogData = processLog.read() + processLog.close() + log.write(processLogData) + except Exception, e: + printLog(log, "<" + processName + "> " + str(e)) + # subprocess.call("idle.bat") + else: + printLog(log, "IGNORE PROCESS " + processName) printLog(log, "") log.close() diff --git a/code/nel/tools/build_gamedata/processes/3_install.py b/code/nel/tools/build_gamedata/processes/3_install.py index fe51c0772..89bf53660 100644 --- a/code/nel/tools/build_gamedata/processes/3_install.py +++ b/code/nel/tools/build_gamedata/processes/3_install.py @@ -24,9 +24,14 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("../configuration") +parser = argparse.ArgumentParser() +parser.add_argument('--includeprocess', '-ipc', nargs='+') +parser.add_argument('--excludeprocess', '-epc', nargs='+') +args = parser.parse_args() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -44,20 +49,24 @@ printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) printLog(log, "") # For each process for processName in ProcessToComplete: - os.chdir(processName) - try: - subprocess.call([ "python", "3_install.py" ]) - except Exception, e: - printLog(log, "<" + processName + "> " + str(e)) - os.chdir("..") - try: - processLog = open(processName + "/log.log", "r") - processLogData = processLog.read() - processLog.close() - log.write(processLogData) - except Exception, e: - printLog(log, "<" + processName + "> " + str(e)) - # subprocess.call("idle.bat") + if ((args.includeprocess == None or processName in args.includeprocess) and (args.excludeprocess == None or not processName in args.excludeprocess)): + printLog(log, "PROCESS " + processName) + os.chdir(processName) + try: + subprocess.call([ "python", "3_install.py" ]) + except Exception, e: + printLog(log, "<" + processName + "> " + str(e)) + os.chdir("..") + try: + processLog = open(processName + "/log.log", "r") + processLogData = processLog.read() + processLog.close() + log.write(processLogData) + except Exception, e: + printLog(log, "<" + processName + "> " + str(e)) + # subprocess.call("idle.bat") + else: + printLog(log, "IGNORE PROCESS " + processName) printLog(log, "") log.close() From 7b033676cb02c34c4df7b5eca5a0eedce6bfdf4b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 17 Sep 2011 15:58:17 +0200 Subject: [PATCH 107/215] Added: #1093 Missing panoply_generique.cfg file. --- .../common/characters_maps_hr/panoply_generique.cfg | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 code/ryzom/tools/build_gamedata/workspace/common/characters_maps_hr/panoply_generique.cfg diff --git a/code/ryzom/tools/build_gamedata/workspace/common/characters_maps_hr/panoply_generique.cfg b/code/ryzom/tools/build_gamedata/workspace/common/characters_maps_hr/panoply_generique.cfg new file mode 100644 index 000000000..41bd8a2ef --- /dev/null +++ b/code/ryzom/tools/build_gamedata/workspace/common/characters_maps_hr/panoply_generique.cfg @@ -0,0 +1,7 @@ + +// the extension for the masks of the texture +// If "mask1" is a extension, and that there's a bitmap name "tex_mask1", it is a "mask1" mask for tex +// Each mask has its own set of colors +mask_extensions = { "skin" , "user" }; + +///////////////////////////////////////////// From 9956eec019f87edfbf880f014c170cf2aa02413d Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 20 Sep 2011 16:19:59 +0200 Subject: [PATCH 108/215] Added: Missing mode2animset.string_array --- .../anim/mode2animset.string_array | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/mode2animset.string_array diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/mode2animset.string_array b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/mode2animset.string_array new file mode 100644 index 000000000..b4600ce12 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/mode2animset.string_array @@ -0,0 +1,26 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + From a61b82fe1e37684577b6516abae977ff7129fbe8 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 20 Sep 2011 16:21:37 +0200 Subject: [PATCH 109/215] Changed: #1092 Fixed several issues concerning export with lightmaps. --- .../3d/plugin_max/nel_mesh_lib/calc_lm.cpp | 40 ++++++++++++------- .../nel_mesh_lib/export_particle_system.cpp | 2 +- .../plugin_max/nel_mesh_lib/export_scene.cpp | 16 ++++++-- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp b/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp index e65b4ea56..03326cf44 100644 --- a/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp +++ b/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp @@ -281,13 +281,21 @@ void SLightBuild::convertFromMaxLight (INode *node,TimeValue tvTime) } #if (MAX_RELEASE < 4000) - // Convert exclusion list - NameTab& ntExclu = maxLight->GetExclusionList(); - for( sint i = 0; i < ntExclu.Count(); ++i ) - { - string tmp = *ntExclu.Addr(i); - this->setExclusion.insert( tmp ); - } + // Convert exclusion list + NameTab& ntExclu = maxLight->GetExclusionList(); + for (sint i = 0; i < ntExclu.Count(); ++i) + { + string tmp = *ntExclu.Addr(i); + this->setExclusion.insert(tmp); + } +#else // (MAX_RELEASE < 4000) + ExclList& exclusionList = maxLight->GetExclusionList(); + for (sint i = 0; i < exclusionList.Count(); ++i) + { + INode *exclNode = exclusionList[i]; + string tmp = exclNode->GetName(); + this->setExclusion.insert(tmp); + } #endif // (MAX_RELEASE < 4000) // Get Soft Shadow informations @@ -1999,11 +2007,12 @@ void CExportNel::deleteLM(INode& ZeNode) sprintf( tmp, "%d", i ); sSaveName += tmp; sSaveName += ".tga"; - FILE *file; - if( file = fopen(sSaveName.c_str(),"rb") ) + if (CFile::fileExists(sSaveName)) { - fclose( file ); - DeleteFile( sSaveName.c_str() ); + if (!CFile::deleteFile(sSaveName)) + { + nlwarning("Failed to delete file %s.", sSaveName.c_str()); + } } } } @@ -2576,11 +2585,12 @@ bool CExportNel::calculateLM( CMesh::CMeshBuild *pZeMeshBuild, CMeshBase::CMeshB for (i = 0; i < 256; ++i) { string sLMName = sBaseName + NLMISC::toString(i) + ".tga"; - CIFile ifi; - if (ifi.open(sLMName)) + if (CFile::fileExists(sLMName)) { - ifi.close (); - DeleteFile (sLMName.c_str()); + if (!CFile::deleteFile(sLMName)) + { + nlwarning("Failed to delete file %s.", sLMName.c_str()); + } } } } diff --git a/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_particle_system.cpp b/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_particle_system.cpp index 4c6b6bc8b..e3504b324 100644 --- a/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_particle_system.cpp +++ b/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_particle_system.cpp @@ -36,7 +36,7 @@ IShape* CExportNel::buildParticleSystem(INode& node, TimeValue time) // if not found, get from the APP_DATAS shapeName = CExportNel::getNelObjectName(node); if (shapeName.empty()) return NULL; - shapeName = NLMISC::CPath::lookup("shapeName", false); + shapeName = NLMISC::CPath::lookup(shapeName, false); if (shapeName.empty()) return NULL; } diff --git a/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_scene.cpp b/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_scene.cpp index 4e7edea8a..2d0268a74 100644 --- a/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_scene.cpp +++ b/code/nel/tools/3d/plugin_max/nel_mesh_lib/export_scene.cpp @@ -24,6 +24,7 @@ #include "../nel_patch_lib/rpo.h" #include "../../ig_lighter_lib/ig_lighter_lib.h" +#include "nel/misc/path.h" #include "nel/3d/scene_group.h" #include "nel/3d/scene.h" #include "nel/3d/shape_bank.h" @@ -418,12 +419,21 @@ CInstanceGroup* CExportNel::buildInstanceGroup(const vector& vectNode, v if (clid.PartA() == NEL_PARTICLE_SYSTEM_CLASS_ID) { // build the shape from the file name - std::string objName = CExportNel::getNelObjectName(*pNode); - if (!objName.empty()) + // std::string objName = CExportNel::getNelObjectName(*pNode); + std::string psFilePath; + // try to get the complete path + if (!CExportNel::getValueByNameUsingParamBlock2(*pNode, "ps_file_name", (ParamType2) TYPE_STRING, &psFilePath, 0)) + { + // if not found, get from the APP_DATAS + psFilePath = CExportNel::getNelObjectName(*pNode); + if (!psFilePath.empty()) + psFilePath = CPath::lookup(psFilePath, false); + } + if (!psFilePath.empty()) { NL3D::CShapeStream ss; NLMISC::CIFile iF; - if (iF.open(objName.c_str())) + if (iF.open(psFilePath.c_str())) { try { From cf560a4898b97bda86a439167544bd27c4d641f1 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 20 Sep 2011 18:11:12 +0200 Subject: [PATCH 110/215] Added: Missing plant and flora sheets required for building newbieland continent. --- .../Flora_template/forest/FO_type_01_b.flora | 136 +++++++++++++++ .../Flora_template/forest/FO_type_04_b.flora | 147 ++++++++++++++++ .../Flora_template/lacustre/tr_palmtree.flora | 148 ++++++++++++++++ .../forest/FX/FX_Fo-Ju-ColibrisB.plant | 64 +++++++ .../ecosystem/forest/FX/FX_Fo-SolBirthA.plant | 87 ++++++++++ .../ecosystem/forest/FX/FX_Fo-bugsA.plant | 61 +++++++ .../ecosystem/forest/FX/FX_Fo-bugsB.plant | 61 +++++++ .../ecosystem/forest/FX/FX_Fo-bugsC.plant | 61 +++++++ .../ecosystem/forest/FX/FX_Fo_Fishes.plant | 159 ++++++++++++++++++ .../forest/common/FO_S2_bigroot_A.plant | 39 +++++ .../forest/common/FO_S2_bigroot_B.plant | 38 +++++ .../forest/common/FO_S2_bigroot_C.plant | 40 +++++ .../ecosystem/forest/common/FO_S2_birch.plant | 40 +++++ .../forest/common/FO_S2_spiketree.plant | 26 +++ .../forest/common/Fo_s1_giant_tree.plant | 69 ++++++++ .../forest/common/fo_S3_champignou_A.plant | 33 ++++ .../forest/common/fo_S3_champignou_B.plant | 35 ++++ .../forest/common/fo_s1_arbreagrelot.plant | 55 ++++++ .../forest/common/fo_s2_arbragrelot.plant | 53 ++++++ .../lacustre/common/tr_s2_bamboo_a.plant | 21 +++ .../lacustre/common/tr_s2_palmtree_a.plant | 21 +++ .../lacustre/common/tr_s2_palmtree_b.plant | 21 +++ .../lacustre/common/tr_s2_palmtree_c.plant | 21 +++ .../lacustre/common/tr_s2_palmtree_d.plant | 22 +++ .../lacustre/common/tr_s2_palmtree_e.plant | 23 +++ .../lacustre/common/tr_s2_palmtree_f.plant | 22 +++ 26 files changed, 1503 insertions(+) create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/forest/FO_type_01_b.flora create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/forest/FO_type_04_b.flora create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/lacustre/tr_palmtree.flora create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-Ju-ColibrisB.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-SolBirthA.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsA.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsB.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsC.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo_Fishes.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_A.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_B.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_C.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_birch.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_spiketree.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/Fo_s1_giant_tree.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_S3_champignou_A.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_S3_champignou_B.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_s1_arbreagrelot.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_s2_arbragrelot.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_bamboo_a.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_a.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_b.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_c.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_d.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_e.plant create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_f.plant diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/forest/FO_type_01_b.flora b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/forest/FO_type_01_b.flora new file mode 100644 index 000000000..ef3785f84 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/forest/FO_type_01_b.flora @@ -0,0 +1,136 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[0] = verdant_heights-forest-001 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[1] = verdant_heights-forest-002 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[2] = verdant_heights-forest-003 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[3] = verdant_heights-forest-004 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[4] = verdant_heights-forest-005 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[5] = verdant_heights-forest-006 +Wed Oct 23 14:44:00 2002 (Schnittger) Form Parents = +Wed Oct 23 14:44:00 2002 (Schnittger) formName Resized = 6 +Wed Oct 23 14:44:51 2002 (Schnittger) .Jitter_Pos = 1 +Wed Oct 23 15:41:42 2002 (Schnittger) Form Parents = +Wed Oct 23 17:47:20 2002 (Schnittger) .Include_patats[0] = verdant_heights-bush-001 +Wed Oct 23 17:47:20 2002 (Schnittger) formName Resized = 1 +Wed Oct 23 17:47:40 2002 (Schnittger) Form Parents = +Wed Oct 23 18:02:27 2002 (Schnittger) .Include_patats[0] = verdant_heights-forest_edge-001 +Wed Oct 23 18:10:50 2002 (Schnittger) .Include_patats[0] = verdant_heights-bush-001 +Thu Oct 31 16:59:51 2002 (Schnittger) .Include_patats[0] = flora_test-forest-002 +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[0].File name = Fo_s3_buissonaepine.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[1].Density = Rare +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[1].File name = fo_S3_champignou_A.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[2].Density = Rare +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[2].File name = fo_S3_champignou_B.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[3].Density = Normal +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[3].File name = FO_S3_fougere.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Scale_Max = 1.5 +Thu Oct 31 16:59:51 2002 (Schnittger) .Scale_Min = 0.5 +Thu Oct 31 16:59:51 2002 (Schnittger) Form Parents = +Thu Oct 31 16:59:51 2002 (Schnittger) formName Resized = 4 +Thu Oct 31 17:32:24 2002 (Schnittger) .Plants[1].Density = VeryRare +Thu Oct 31 17:32:24 2002 (Schnittger) .Plants[2].Density = VeryRare +Tue Nov 05 12:03:12 2002 (Schnittger) .Include_patats[0] = flora_test-forest-007 +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[0].File name = Fo_s1_giant_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[10].File name = fo_S3_champignou_A.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[11].File name = fo_S3_champignou_B.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[12].File name = fo_S3_dead_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[13].File name = FO_S3_fougere.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[14].File name = fo_s3_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[15].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[15].File name = FX_Fo-bugsA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[16].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[16].File name = FX_Fo-bugsB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[17].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[17].File name = FX_Fo-bugsC.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[18].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[18].File name = FX_Fo-Ju-ColibrisB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[19].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[19].File name = FX_Fo-SolBirthA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[1].File name = Fo_S1_giant_trunk.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[20].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[20].File name = FX_Fo-treefallA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[21].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[21].File name = FX_Fo-treefallB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[22].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[22].File name = FX_Fo-treefallC.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[23].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[23].File name = FX_Fo-treefallD.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[24].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[24].File name = FX_Fo-treefallE.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[2].File name = fo_s2_arbragrelot.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[3].File name = Fo_s2_big_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[4].File name = FO_S2_bigroot_A.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[5].File name = FO_S2_bigroot_B.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[6].File name = FO_S2_bigroot_C.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[7].File name = FO_S2_birch.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[8].File name = FO_S2_spiketree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[9].File name = Fo_s3_buissonaepine.plant +Tue Nov 05 12:03:12 2002 (Schnittger) formName Resized = 25 +Tue Nov 05 12:13:16 2002 (Schnittger) .Include_patats[0] = flora_test-forest-002 +Tue Nov 05 12:13:16 2002 (Schnittger) formName Deleted = +Tue Nov 05 12:29:21 2002 (Schnittger) .Plants[2].Density = VeryRare +Tue Nov 05 12:29:21 2002 (Schnittger) .Plants[3].Density = VeryRare +Tue Nov 05 13:10:58 2002 (Schnittger) formName Deleted = +Tue Nov 05 13:27:23 2002 (Schnittger) .Plants[0].Density = VeryRare +Tue Nov 05 13:27:23 2002 (Schnittger) .Plants[8].File name = fo_s2_arbragrelot.plant +Tue Nov 05 13:27:23 2002 (Schnittger) formName Resized = 9 +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[10].Density = Rare +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[10].File name = FO_S2_bigroot_B.plant +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[11].Density = Rare +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[11].File name = FO_S2_bigroot_C.plant +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[9].Density = Rare +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[9].File name = FO_S2_bigroot_A.plant +Tue Nov 05 13:39:03 2002 (Schnittger) formName Resized = 12 +Tue Nov 05 13:39:33 2002 (Schnittger) formName Deleted = +Tue Nov 05 13:52:17 2002 (Schnittger) .Plants[1].Density = 0.005 +Tue Nov 05 13:52:17 2002 (Schnittger) .Plants[2].Density = 0.005 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/forest/FO_type_04_b.flora b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/forest/FO_type_04_b.flora new file mode 100644 index 000000000..4cebcca24 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/forest/FO_type_04_b.flora @@ -0,0 +1,147 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[0] = verdant_heights-forest-001 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[1] = verdant_heights-forest-002 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[2] = verdant_heights-forest-003 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[3] = verdant_heights-forest-004 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[4] = verdant_heights-forest-005 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[5] = verdant_heights-forest-006 +Wed Oct 23 14:44:00 2002 (Schnittger) Form Parents = +Wed Oct 23 14:44:00 2002 (Schnittger) formName Resized = 6 +Wed Oct 23 14:44:51 2002 (Schnittger) .Jitter_Pos = 1 +Wed Oct 23 15:41:42 2002 (Schnittger) Form Parents = +Wed Oct 23 17:47:20 2002 (Schnittger) .Include_patats[0] = verdant_heights-bush-001 +Wed Oct 23 17:47:20 2002 (Schnittger) formName Resized = 1 +Wed Oct 23 17:47:40 2002 (Schnittger) Form Parents = +Wed Oct 23 18:02:27 2002 (Schnittger) .Include_patats[0] = verdant_heights-forest_edge-001 +Wed Oct 23 18:10:50 2002 (Schnittger) .Include_patats[0] = verdant_heights-bush-001 +Thu Oct 31 16:59:51 2002 (Schnittger) .Include_patats[0] = flora_test-zone-002 +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[0].File name = Fo_s3_buissonaepine.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[1].Density = Rare +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[1].File name = fo_S3_champignou_A.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[2].Density = Rare +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[2].File name = fo_S3_champignou_B.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[3].Density = Normal +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[3].File name = FO_S3_fougere.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Scale_Max = 1.5 +Thu Oct 31 16:59:51 2002 (Schnittger) .Scale_Min = 0.5 +Thu Oct 31 16:59:51 2002 (Schnittger) Form Parents = +Thu Oct 31 16:59:51 2002 (Schnittger) formName Resized = 4 +Thu Oct 31 17:32:24 2002 (Schnittger) .Plants[1].Density = VeryRare +Thu Oct 31 17:32:24 2002 (Schnittger) .Plants[2].Density = VeryRare +Tue Nov 05 12:03:12 2002 (Schnittger) .Include_patats[0] = flora_test-zone-007 +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[0].File name = Fo_s1_giant_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[10].File name = fo_S3_champignou_A.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[11].File name = fo_S3_champignou_B.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[12].File name = fo_S3_dead_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[13].File name = FO_S3_fougere.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[14].File name = fo_s3_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[15].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[15].File name = FX_Fo-bugsA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[16].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[16].File name = FX_Fo-bugsB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[17].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[17].File name = FX_Fo-bugsC.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[18].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[18].File name = FX_Fo-Ju-ColibrisB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[19].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[19].File name = FX_Fo-SolBirthA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[1].File name = Fo_S1_giant_trunk.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[20].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[20].File name = FX_Fo-treefallA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[21].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[21].File name = FX_Fo-treefallB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[22].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[22].File name = FX_Fo-treefallC.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[23].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[23].File name = FX_Fo-treefallD.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[24].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[24].File name = FX_Fo-treefallE.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[2].File name = fo_s2_arbragrelot.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[3].File name = Fo_s2_big_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[4].File name = FO_S2_bigroot_A.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[5].File name = FO_S2_bigroot_B.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[6].File name = FO_S2_bigroot_C.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[7].File name = FO_S2_birch.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[8].File name = FO_S2_spiketree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[9].File name = Fo_s3_buissonaepine.plant +Tue Nov 05 12:03:12 2002 (Schnittger) formName Resized = 25 +Tue Nov 05 12:13:16 2002 (Schnittger) .Include_patats[0] = flora_test-zone-002 +Tue Nov 05 12:13:16 2002 (Schnittger) formName Deleted = +Tue Nov 05 12:29:21 2002 (Schnittger) .Plants[2].Density = VeryRare +Tue Nov 05 12:29:21 2002 (Schnittger) .Plants[3].Density = VeryRare +Tue Nov 05 13:10:58 2002 (Schnittger) formName Deleted = +Tue Nov 05 13:27:23 2002 (Schnittger) .Plants[0].Density = VeryRare +Tue Nov 05 13:27:23 2002 (Schnittger) .Plants[8].File name = fo_s2_arbragrelot.plant +Tue Nov 05 13:27:23 2002 (Schnittger) formName Resized = 9 +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[10].Density = Rare +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[10].File name = FO_S2_bigroot_B.plant +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[11].Density = Rare +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[11].File name = FO_S2_bigroot_C.plant +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[9].Density = Rare +Tue Nov 05 13:39:03 2002 (Schnittger) .Plants[9].File name = FO_S2_bigroot_A.plant +Tue Nov 05 13:39:03 2002 (Schnittger) formName Resized = 12 +Tue Nov 05 13:40:13 2002 (Schnittger) .Include_patats[0] = flora_test-zone-003 +Tue Nov 05 13:52:07 2002 (Schnittger) .Plants[10].Density = VeryRare +Tue Nov 05 13:52:07 2002 (Schnittger) .Plants[11].Density = VeryRare +Tue Nov 05 13:52:07 2002 (Schnittger) .Plants[1].Density = 0.005 +Tue Nov 05 13:52:07 2002 (Schnittger) .Plants[2].Density = 0.005 +Tue Nov 05 13:52:07 2002 (Schnittger) .Plants[9].Density = VeryRare +Tue Nov 05 13:56:09 2002 (Schnittger) .Include_patats[0] = flora_test-zone-004 +Tue Nov 05 13:56:09 2002 (Schnittger) .Plants[8].File name = Fo_s3_buissonaepine.plant +Tue Nov 05 13:56:09 2002 (Schnittger) formName Deleted = +Tue Nov 05 13:56:09 2002 (Schnittger) formName Resized = 9 +Tue Nov 05 14:16:57 2002 (Schnittger) .Plants[8].Density = 0.005 +Tue Nov 05 14:16:57 2002 (Schnittger) .Plants[8].File name = fo_S3_champignou_A.plant +Tue Nov 05 14:16:57 2002 (Schnittger) .Plants[9].Density = 0.005 +Tue Nov 05 14:16:57 2002 (Schnittger) .Plants[9].File name = fo_S3_champignou_B.plant +Tue Nov 05 14:16:57 2002 (Schnittger) formName Resized = 10 +Tue Nov 05 14:17:05 2002 (Schnittger) .Include_patats[0] = flora_test-zone-005 +Tue Nov 05 14:37:05 2002 (Schnittger) .Plants[8].Density = Normal +Tue Nov 05 14:37:05 2002 (Schnittger) .Plants[8].File name = fo_s2_arbragrelot.plant +Tue Nov 05 14:37:05 2002 (Schnittger) formName Deleted = +Tue Nov 05 14:37:17 2002 (Schnittger) .Include_patats[0] = flora_test-zone-006 +Tue Nov 05 16:43:23 2002 (Schnittger) .Plants[5].Density = Rare +Tue Nov 05 16:43:23 2002 (Schnittger) .Plants[5].File name = FO_S2_spiketree.plant +Tue Nov 05 16:43:23 2002 (Schnittger) .Plants[6].Density = Rare +Tue Nov 05 16:43:23 2002 (Schnittger) .Plants[6].File name = Ju_S3_bamboo.plant +Tue Nov 05 16:43:23 2002 (Schnittger) .Plants[7].Density = Rare +Tue Nov 05 16:43:23 2002 (Schnittger) .Plants[7].File name = Ju_S1_bamboo.plant +Tue Nov 05 16:43:23 2002 (Schnittger) formName Deleted = +Tue Nov 05 16:43:23 2002 (Schnittger) formName Resized = 8 +Tue Nov 05 17:18:13 2002 (Schnittger) .Plants[5].Density = VeryRare +Tue Nov 05 17:18:13 2002 (Schnittger) .Plants[6].Density = VeryRare +Wed Nov 06 17:40:11 2002 (Schnittger) .Scale_Max = 0.75 +Wed Dec 18 14:40:49 2002 (Schnittger) formName Deleted = +Wed Dec 18 14:40:49 2002 (Schnittger) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/lacustre/tr_palmtree.flora b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/lacustre/tr_palmtree.flora new file mode 100644 index 000000000..7ffa744b4 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/Flora_template/lacustre/tr_palmtree.flora @@ -0,0 +1,148 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[0] = verdant_heights-forest-001 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[1] = verdant_heights-forest-002 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[2] = verdant_heights-forest-003 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[3] = verdant_heights-forest-004 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[4] = verdant_heights-forest-005 +Wed Oct 23 14:44:00 2002 (Schnittger) .Include_patats[5] = verdant_heights-forest-006 +Wed Oct 23 14:44:00 2002 (Schnittger) Form Parents = +Wed Oct 23 14:44:00 2002 (Schnittger) formName Resized = 6 +Wed Oct 23 14:44:51 2002 (Schnittger) .Jitter_Pos = 1 +Wed Oct 23 15:41:42 2002 (Schnittger) Form Parents = +Wed Oct 23 17:47:20 2002 (Schnittger) .Include_patats[0] = verdant_heights-bush-001 +Wed Oct 23 17:47:20 2002 (Schnittger) formName Resized = 1 +Wed Oct 23 17:47:40 2002 (Schnittger) Form Parents = +Wed Oct 23 18:02:27 2002 (Schnittger) .Include_patats[0] = verdant_heights-forest_edge-001 +Wed Oct 23 18:10:50 2002 (Schnittger) .Include_patats[0] = verdant_heights-bush-001 +Thu Oct 31 16:59:51 2002 (Schnittger) .Include_patats[0] = flora_test-forest-002 +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[0].File name = Fo_s3_buissonaepine.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[1].Density = Rare +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[1].File name = fo_S3_champignou_A.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[2].Density = Rare +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[2].File name = fo_S3_champignou_B.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[3].Density = Normal +Thu Oct 31 16:59:51 2002 (Schnittger) .Plants[3].File name = FO_S3_fougere.plant +Thu Oct 31 16:59:51 2002 (Schnittger) .Scale_Max = 1.5 +Thu Oct 31 16:59:51 2002 (Schnittger) .Scale_Min = 0.5 +Thu Oct 31 16:59:51 2002 (Schnittger) Form Parents = +Thu Oct 31 16:59:51 2002 (Schnittger) formName Resized = 4 +Thu Oct 31 17:32:24 2002 (Schnittger) .Plants[1].Density = VeryRare +Thu Oct 31 17:32:24 2002 (Schnittger) .Plants[2].Density = VeryRare +Tue Nov 05 12:03:12 2002 (Schnittger) .Include_patats[0] = flora_test-forest-007 +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[0].File name = Fo_s1_giant_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[10].File name = fo_S3_champignou_A.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[11].File name = fo_S3_champignou_B.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[12].File name = fo_S3_dead_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[13].File name = FO_S3_fougere.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[14].File name = fo_s3_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[15].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[15].File name = FX_Fo-bugsA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[16].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[16].File name = FX_Fo-bugsB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[17].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[17].File name = FX_Fo-bugsC.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[18].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[18].File name = FX_Fo-Ju-ColibrisB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[19].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[19].File name = FX_Fo-SolBirthA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[1].File name = Fo_S1_giant_trunk.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[20].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[20].File name = FX_Fo-treefallA.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[21].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[21].File name = FX_Fo-treefallB.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[22].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[22].File name = FX_Fo-treefallC.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[23].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[23].File name = FX_Fo-treefallD.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[24].Density = FX +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[24].File name = FX_Fo-treefallE.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[2].File name = fo_s2_arbragrelot.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[3].File name = Fo_s2_big_tree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[4].File name = FO_S2_bigroot_A.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[5].File name = FO_S2_bigroot_B.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[6].File name = FO_S2_bigroot_C.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[7].File name = FO_S2_birch.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[8].File name = FO_S2_spiketree.plant +Tue Nov 05 12:03:12 2002 (Schnittger) .Plants[9].File name = Fo_s3_buissonaepine.plant +Tue Nov 05 12:03:12 2002 (Schnittger) formName Resized = 25 +Tue Nov 05 12:14:30 2002 (Schnittger) .Include_patats[0] = flora_test-forest-001 +Tue Nov 05 12:14:30 2002 (Schnittger) formName Deleted = +Fri Jan 10 16:30:17 2003 (mauduit) .Plants[0].File name = FY_S2_palmtree_A.plant +Fri Jan 10 16:30:17 2003 (mauduit) .Plants[1].File name = FY_S2_palmtree_B.plant + +Fri Jan 10 16:30:17 2003 (mauduit) .Plants[2].File name = FY_S2_palmtree_C.plant +Fri Jan 10 16:30:17 2003 (mauduit) .Plants[3].File name = FY_S2_palmtree_D.plant +Fri Jan 10 16:30:17 2003 (mauduit) .Plants[4].File name = FY_S2_palmtree_E.plant +Fri Jan 10 16:30:17 2003 (mauduit) formName Resized = 5 +Fri Jan 10 16:34:55 2003 (mauduit) .Plants[0].Density = 0.2 +Fri Jan 10 16:34:55 2003 (mauduit) .Plants[1].Density = 0.2 +Fri Jan 10 16:34:55 2003 (mauduit) .Plants[2].Density = 0.2 +Fri Jan 10 16:34:55 2003 (mauduit) .Plants[3].Density = 0.2 +Fri Jan 10 16:34:55 2003 (mauduit) .Plants[4].Density = 0.2 +Wed Jan 15 16:55:00 2003 (mauduit) .Plants[0].Density = 0.01 +Wed Jan 15 16:55:00 2003 (mauduit) .Plants[0].File name = FY_S1_baobab_C.plant +Wed Jan 15 16:55:00 2003 (mauduit) formName Deleted = +Thu Jan 16 15:10:15 2003 (mauduit) .Plants[1].File name = FY_S1_baobab_B.plant +Thu Jan 16 15:10:15 2003 (mauduit) .Plants[2].File name = FY_S1_baobab_A.plant +Thu Jan 16 15:10:15 2003 (mauduit) formName Pasted = + +Thu Jan 16 15:10:15 2003 (mauduit) formName Resized = 3 +Thu Jan 16 15:11:12 2003 (mauduit) .Plants[0].Density = 0.1 +Thu Jan 16 15:11:12 2003 (mauduit) .Plants[1].Density = 0.1 +Thu Jan 16 15:11:12 2003 (mauduit) .Plants[2].Density = 0.1 +Mon Mar 03 10:41:45 2003 (mauduit) .Plants[0].Density = 0.001 +Mon Mar 03 10:41:45 2003 (mauduit) .Plants[1].Density = 0.001 +Mon Mar 03 10:41:45 2003 (mauduit) .Plants[2].Density = 0.001 +Mon Mar 03 10:41:45 2003 (mauduit) .Scale_Max = 1.2 +Mon Jun 30 11:14:59 2003 (mauduit) .Plants[0].File name = tr_s2_palmtree_a.plant +Mon Jun 30 11:14:59 2003 (mauduit) .Plants[1].File name = tr_s2_palmtree_b.plant +Mon Jun 30 11:14:59 2003 (mauduit) .Plants[2].File name = tr_s2_palmtree_c.plant +Mon Jun 30 11:14:59 2003 (mauduit) .Plants[3] Renamed = +Mon Jun 30 11:14:59 2003 (mauduit) .Plants[3].File name = tr_s2_palmtree_d.plant +Mon Jun 30 11:14:59 2003 (mauduit) .Plants[4].File name = tr_s2_palmtree_e.plant +Mon Jun 30 11:14:59 2003 (mauduit) formName Deleted = +Mon Jun 30 11:14:59 2003 (mauduit) formName Pasted = +Mon Jun 30 11:14:59 2003 (mauduit) formName Resized = 5 +Mon Jun 30 16:39:08 2003 (mauduit) .Plants[5].File name = tr_s2_palmtree_f.plant +Mon Jun 30 16:39:08 2003 (mauduit) formName Pasted = +Mon Jun 30 16:39:08 2003 (mauduit) formName Resized = 6 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-Ju-ColibrisB.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-Ju-ColibrisB.plant new file mode 100644 index 000000000..20cf4d719 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-Ju-ColibrisB.plant @@ -0,0 +1,64 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Wed Aug 06 10:25:21 2003 (firroloni) .3D.Shape = +Wed Aug 06 10:25:21 2003 (firroloni) .3D.SummerFX.EndHourMax = 20 +Wed Aug 06 10:25:21 2003 (firroloni) .3D.SummerFX.EndHourMin = 18 +Wed Aug 06 10:25:21 2003 (firroloni) .3D.SummerFX.FXName = Fo-Ju-ColibrisB.ps +Wed Aug 06 10:25:21 2003 (firroloni) .3D.SummerFX.Mode = UseEndHour +Wed Aug 06 10:25:21 2003 (firroloni) .3D.SummerFX.StartHourMax = 10 +Wed Aug 06 10:25:21 2003 (firroloni) .3D.SummerFX.StartHourMin = 8 +Wed Aug 06 10:26:12 2003 (firroloni) .3D.WinterFX.EndHourMax = 14 +Wed Aug 06 10:26:12 2003 (firroloni) .3D.WinterFX.EndHourMin = 13.5 +Wed Aug 06 10:26:12 2003 (firroloni) .3D.WinterFX.FXName = +Wed Aug 06 10:26:12 2003 (firroloni) .3D.WinterFX.StartHourMax = 12.5 +Wed Aug 06 10:26:12 2003 (firroloni) .3D.WinterFX.StartHourMin = 12 +Wed Aug 06 10:26:12 2003 (firroloni) formName Pasted = +Thu Mar 04 16:28:02 2004 (millas) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-SolBirthA.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-SolBirthA.plant new file mode 100644 index 000000000..6ecbac5d9 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-SolBirthA.plant @@ -0,0 +1,87 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:46 2002 (corvazier) File converted from old format +Wed Aug 06 10:29:31 2003 (firroloni) .3D.AutomnFX.FXName = Fo-SolBirthA.ps +Wed Aug 06 10:29:31 2003 (firroloni) .3D.Shape = +Wed Aug 06 10:29:31 2003 (firroloni) .3D.SpringFX.FXName = Fo-SolBirthA.ps +Wed Aug 06 10:29:31 2003 (firroloni) .3D.SummerFX.FXName = Fo-SolBirthA.ps +Wed Aug 06 10:29:31 2003 (firroloni) .3D.WinterFX.FXName = Fo-SolBirthA.ps +Wed Aug 06 10:37:39 2003 (firroloni) .3D.SpringFX.EndHourMax = 17 +Wed Aug 06 10:37:39 2003 (firroloni) .3D.SpringFX.EndHourMin = 16 +Wed Aug 06 10:37:39 2003 (firroloni) .3D.SpringFX.StartHourMax = 7 +Wed Aug 06 10:37:39 2003 (firroloni) .3D.SpringFX.StartHourMin = 5 +Wed Aug 06 10:37:39 2003 (firroloni) formName Pasted = +Wed Aug 06 10:44:39 2003 (firroloni) .3D.AutomnFX.Mode = UseEndHour +Wed Aug 06 10:44:39 2003 (firroloni) .3D.SpringFX.Mode = UseEndHour +Wed Aug 06 10:44:39 2003 (firroloni) .3D.SummerFX.Mode = UseEndHour +Wed Aug 06 10:44:39 2003 (firroloni) .3D.WinterFX.Mode = UseEndHour +Tue Mar 02 16:47:39 2004 (millas) .3D.SpringFX.EndHourMax = 20 +Tue Mar 02 16:47:39 2004 (millas) .3D.SpringFX.EndHourMin = 19 +Tue Mar 02 16:47:39 2004 (millas) .3D.WinterFX.StartHourMin = 6 +Tue Mar 02 16:47:39 2004 (millas) formName Pasted = +Tue Mar 02 18:15:48 2004 (millas) .3D.SpringFX.EndHourMax = 20.1 +Tue Mar 02 18:15:48 2004 (millas) .3D.SpringFX.EndHourMin = 20 +Tue Mar 02 18:15:48 2004 (millas) .3D.SpringFX.StartHourMax = 19.1 +Tue Mar 02 18:15:48 2004 (millas) .3D.SpringFX.StartHourMin = 19 +Tue Mar 02 18:15:48 2004 (millas) .3D.WinterFX.FXName = Fo-SolBirthA.ps +Tue Mar 02 18:15:48 2004 (millas) formName Pasted = +Tue Mar 02 19:03:49 2004 (millas) .3D.SpringFX.Mode = UseEndHour +Tue Mar 02 19:03:49 2004 (millas) .3D.WinterFX.EndHourMax = 19.1 +Tue Mar 02 19:03:49 2004 (millas) .3D.WinterFX.EndHourMin = 19 +Tue Mar 02 19:03:49 2004 (millas) .3D.WinterFX.StartHourMax = 18.1 +Tue Mar 02 19:03:49 2004 (millas) .3D.WinterFX.StartHourMin = 18 +Tue Mar 02 19:06:00 2004 (millas) formName Pasted = +Wed Mar 03 14:58:10 2004 (millas) .3D.SpringFX.EndHourMin = 19.0 +Wed Mar 03 14:58:10 2004 (millas) .3D.SpringFX.StartHourMax = 17.1 +Wed Mar 03 14:58:10 2004 (millas) .3D.SpringFX.StartHourMin = 17.0 +Wed Mar 03 15:01:14 2004 (millas) .3D.SpringFX.EndHourMax = 19.9 +Wed Mar 03 15:01:14 2004 (millas) .3D.WinterFX.EndHourMin = 19 +Wed Mar 03 15:01:14 2004 (millas) .3D.WinterFX.StartHourMin = 17 +Wed Mar 03 15:01:14 2004 (millas) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsA.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsA.plant new file mode 100644 index 000000000..b584804a7 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsA.plant @@ -0,0 +1,61 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Tue Aug 05 16:50:05 2003 (firroloni) .3D.AutomnFX.FXName = Fo-BugsA.ps +Tue Aug 05 16:50:05 2003 (firroloni) .3D.Shape = +Tue Aug 05 16:50:05 2003 (firroloni) .3D.SpringFX.FXName = Fo-BugsA.ps +Tue Aug 05 16:50:05 2003 (firroloni) .3D.SummerFX.FXName = Fo-BugsA.ps +Tue Aug 05 16:50:05 2003 (firroloni) .3D.WinterFX.FXName = Fo-BugsA.ps +Wed Aug 06 10:22:14 2003 (firroloni) .3D.SpringFX.EndHourMax = 20 +Wed Aug 06 10:22:14 2003 (firroloni) .3D.SpringFX.EndHourMin = 18 +Wed Aug 06 10:22:14 2003 (firroloni) .3D.SpringFX.Mode = UseEndHour +Wed Aug 06 10:22:14 2003 (firroloni) .3D.SpringFX.StartHourMax = 10 +Wed Aug 06 10:22:14 2003 (firroloni) .3D.SpringFX.StartHourMin = 8 +Wed Aug 06 10:22:14 2003 (firroloni) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsB.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsB.plant new file mode 100644 index 000000000..fadff666d --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsB.plant @@ -0,0 +1,61 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Wed Aug 06 10:23:00 2003 (firroloni) .3D.SpringFX.EndHourMax = 22 +Wed Aug 06 10:23:00 2003 (firroloni) .3D.SpringFX.EndHourMin = 20 +Wed Aug 06 10:23:00 2003 (firroloni) .3D.SpringFX.StartHourMax = 8 +Wed Aug 06 10:23:00 2003 (firroloni) .3D.SpringFX.StartHourMin = 5 +Wed Aug 06 10:23:00 2003 (firroloni) formName Pasted = +Wed Aug 06 10:23:48 2003 (firroloni) .3D.AutomnFX.FXName = Fo-BugsB.ps +Wed Aug 06 10:23:48 2003 (firroloni) .3D.Shape = +Wed Aug 06 10:23:48 2003 (firroloni) .3D.SpringFX.FXName = Fo-BugsB.ps +Wed Aug 06 10:23:48 2003 (firroloni) .3D.SpringFX.Mode = UseEndHour +Wed Aug 06 10:23:48 2003 (firroloni) .3D.SummerFX.FXName = Fo-BugsB.ps +Wed Aug 06 10:23:48 2003 (firroloni) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsC.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsC.plant new file mode 100644 index 000000000..9067d216a --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo-bugsC.plant @@ -0,0 +1,61 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Wed Aug 06 10:24:21 2003 (firroloni) .3D.SpringFX.EndHourMax = 13 +Wed Aug 06 10:24:21 2003 (firroloni) .3D.SpringFX.EndHourMin = 12 +Wed Aug 06 10:24:21 2003 (firroloni) .3D.SpringFX.FXName = Fo-BugsC.ps +Wed Aug 06 10:24:21 2003 (firroloni) .3D.SpringFX.Mode = UseEndHour +Wed Aug 06 10:24:21 2003 (firroloni) .3D.SpringFX.StartHourMax = 5 +Wed Aug 06 10:24:21 2003 (firroloni) .3D.SpringFX.StartHourMin = 3 +Wed Aug 06 10:24:21 2003 (firroloni) formName Pasted = +Thu Mar 04 16:24:45 2004 (millas) .3D.SpringFX.StartHourMax = 7 +Thu Mar 04 16:24:45 2004 (millas) .3D.SpringFX.StartHourMin = 6 +Thu Mar 04 16:25:22 2004 (millas) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo_Fishes.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo_Fishes.plant new file mode 100644 index 000000000..5093dedb0 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/FX/FX_Fo_Fishes.plant @@ -0,0 +1,159 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:51 2002 (corvazier) File converted from old format +Tue Jul 29 11:20:04 2003 (firroloni) .3D.Shape = Desert-SandBlast.ps +Tue Jul 29 11:22:34 2003 (firroloni) .3D.Shape = Desert-SandBlastB.ps +Tue Jul 29 11:23:34 2003 (firroloni) .3D.Shape = Fy-SandBlastADesert.ps +Tue Jul 29 11:23:51 2003 (firroloni) .3D.Shape = Fy-SandBlastDesert.ps +Wed Aug 06 11:53:50 2003 (firroloni) .3D.Shape = +Wed Aug 06 11:53:50 2003 (firroloni) .3D.SpringFX.FXName = Fy-SandBlastDesert.ps +Wed Aug 06 11:53:50 2003 (firroloni) .3D.SpringFX.Mode = Spawn +Wed Aug 06 11:53:50 2003 (firroloni) .3D.SpringFX.StartHourMax = 23.9 +Wed Aug 06 15:08:24 2003 (firroloni) .3D.SpringFX.CycleDuration = 1 +Wed Aug 06 15:08:24 2003 (firroloni) .3D.SpringFX.StartHourMax = .99 +Tue Aug 12 10:21:56 2003 (firroloni) .3D.SpringFX.CycleDuration = 24 +Tue Aug 12 10:21:56 2003 (firroloni) .3D.SpringFX.FXName = De_Scorpion2.ps +Tue Aug 12 10:21:56 2003 (firroloni) .3D.SpringFX.StartHourMax = 20 +Tue Aug 12 10:21:56 2003 (firroloni) .3D.SpringFX.StartHourMin = 12 +Tue Aug 12 10:21:56 2003 (firroloni) formName Pasted = +Tue Aug 12 12:23:01 2003 (firroloni) .3D.Shape = FY_S1_baobab_A.shape +Fri Oct 17 17:36:11 2003 (firroloni) .3D.Shape = +Mon Dec 01 15:07:38 2003 (firroloni) .3D.SpringFX.FXName = fy_geyser_feu.ps +Mon Dec 01 15:07:38 2003 (firroloni) formName Pasted = +Mon Dec 01 15:12:52 2003 (firroloni) .3D.WinterFX.CycleDuration = 1 +Mon Dec 01 15:12:52 2003 (firroloni) .3D.WinterFX.StartHourMax = .9 +Mon Dec 01 15:12:52 2003 (firroloni) .3D.WinterFX.StartHourMin = 0 +Mon Dec 01 15:12:52 2003 (firroloni) formName Pasted = +Mon Dec 01 15:51:41 2003 (firroloni) .3D.SpringFX.CycleDuration = .1 +Mon Dec 01 15:51:41 2003 (firroloni) .3D.SpringFX.StartHourMax = .09 +Mon Dec 01 16:30:44 2003 (firroloni) .3D.SpringFX.CycleDuration = .5 +Mon Dec 01 16:30:44 2003 (firroloni) .3D.SpringFX.StartHourMax = .49 +Mon Dec 01 16:30:49 2003 (firroloni) formName Pasted = +Mon Dec 01 16:44:57 2003 (firroloni) .3D.SpringFX.FXName = fy_geyser_fumee.ps +Mon Dec 01 16:44:57 2003 (firroloni) formName Pasted = +Mon Dec 01 17:16:20 2003 (firroloni) .3D.SpringFX.CycleDuration = 1.5 +Mon Dec 01 17:16:20 2003 (firroloni) .3D.SpringFX.StartHourMax = 1.49 +Mon Dec 01 17:16:20 2003 (firroloni) formName Pasted = +Mon Dec 01 17:52:48 2003 (firroloni) .3D.AutomnFX.EndHourMax = 15 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.AutomnFX.EndHourMin = 14 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.AutomnFX.StartHourMax = 2 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.AutomnFX.StartHourMin = 1 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SpringFX.CycleDuration = 24 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SpringFX.EndHourMax = 9 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SpringFX.EndHourMin = 8 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SpringFX.FXName = fy_vapeurs.ps +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SpringFX.Mode = UseEndHour +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SpringFX.StartHourMax = 4 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SpringFX.StartHourMin = 3 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SummerFX.EndHourMax = 13 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.SummerFX.EndHourMin = 12 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.WinterFX.EndHourMax = 18 +Mon Dec 01 17:52:48 2003 (firroloni) .3D.WinterFX.EndHourMin = 17 +Mon Dec 01 17:52:48 2003 (firroloni) formName Pasted = +Thu Dec 04 18:08:26 2003 (firroloni) .3D.AutomnFX.EndHourMax = 20 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.AutomnFX.EndHourMin = 18 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.AutomnFX.FXName = Fy-BirdA.ps +Thu Dec 04 18:08:26 2003 (firroloni) .3D.AutomnFX.StartHourMax = 14 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.AutomnFX.StartHourMin = 12 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SpringFX.EndHourMax = 14 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SpringFX.EndHourMin = 12 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SpringFX.FXName = Fy-BirdA.ps +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SpringFX.StartHourMax = 9 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SpringFX.StartHourMin = 8 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SummerFX.EndHourMax = 16 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SummerFX.EndHourMin = 15 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SummerFX.FXName = Fy-BirdA.ps +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SummerFX.StartHourMax = 11 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.SummerFX.StartHourMin = 10 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.WinterFX.EndHourMax = 20 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.WinterFX.EndHourMin = 19 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.WinterFX.FXName = Fy-BirdA.ps +Thu Dec 04 18:08:26 2003 (firroloni) .3D.WinterFX.StartHourMax = 11 +Thu Dec 04 18:08:26 2003 (firroloni) .3D.WinterFX.StartHourMin = 10 +Fri Dec 05 15:57:49 2003 (firroloni) .3D.AutomnFX.UserParam0Max = 1 +Fri Dec 05 15:57:49 2003 (firroloni) .3D.SpringFX.UserParam0Max = 1 +Fri Dec 05 15:57:49 2003 (firroloni) .3D.SummerFX.UserParam0Max = 1 +Fri Dec 05 15:57:49 2003 (firroloni) .3D.WinterFX.UserParam0Max = 1 +Mon Dec 08 19:14:03 2003 (firroloni) .3D.AutomnFX.StartHourMax = 4 +Mon Dec 08 19:14:03 2003 (firroloni) .3D.AutomnFX.StartHourMin = 2 +Mon Dec 08 19:14:03 2003 (firroloni) .3D.SpringFX.EndHourMax = 16 +Mon Dec 08 19:14:03 2003 (firroloni) .3D.SpringFX.EndHourMin = 15 +Mon Dec 08 19:14:03 2003 (firroloni) .3D.SummerFX.EndHourMax = 19 +Mon Dec 08 19:14:03 2003 (firroloni) .3D.SummerFX.EndHourMin = 18 +Mon Dec 08 19:14:03 2003 (firroloni) .3D.SummerFX.StartHourMax = 6 +Mon Dec 08 19:14:03 2003 (firroloni) .3D.SummerFX.StartHourMin = 5 +Thu Feb 19 10:49:53 2004 (gatto) .3D.SpringFX.CycleDuration = 24 +Thu Feb 19 10:49:53 2004 (gatto) .3D.SpringFX.EndHourMax = 24 +Thu Feb 19 10:49:53 2004 (gatto) .3D.SpringFX.EndHourMin = 24 +Thu Feb 19 10:49:53 2004 (gatto) .3D.SpringFX.FXName = tr-fishesb.ps +Thu Feb 19 10:49:53 2004 (gatto) .3D.SpringFX.StartHourMax = 24 +Thu Feb 19 10:49:53 2004 (gatto) .3D.SpringFX.StartHourMin = 0.01 +Thu Feb 19 10:50:02 2004 (gatto) .3D.SpringFX.AngleMax = 80 +Thu Feb 19 10:52:18 2004 (gatto) formName Pasted = +Thu Feb 19 10:53:42 2004 (gatto) .3D.SpringFX.FXName = tr-fishes.ps +Thu Feb 19 10:53:42 2004 (gatto) formName Pasted = +Tue Mar 02 14:51:52 2004 (rang) .3D.SpringFX.FXName = Ju_Fo_Fishes.ps +Tue Mar 02 14:51:52 2004 (rang) formName Pasted = +Tue Mar 02 15:18:18 2004 (rang) .3D.SpringFX.AngleMax = 60 +Tue Mar 02 15:18:18 2004 (rang) formName Pasted = +Fri Mar 05 15:58:50 2004 (rang) .3D.SpringFX.Mode = AlwaysStarted +Fri Mar 05 15:58:50 2004 (rang) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_A.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_A.plant new file mode 100644 index 000000000..16a904391 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_A.plant @@ -0,0 +1,39 @@ + +
+ + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Bounding Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Collision Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shadow Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .Plant Name = S2 tree +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Bounding Radius = 1.1 +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Collision Radius = 1.1 +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .Plant Name = S3 mushroom +Wed Oct 16 12:24:28 2002 (Schnittger) .3D.Bounding Radius = 1.4 +Wed Oct 16 12:24:28 2002 (Schnittger) .3D.Collision Radius = 1.4 +Wed Oct 23 16:07:19 2002 (Schnittger) .3D.Shadow Shape = FO_S2_bigroot_A.shape +Wed Oct 23 16:07:19 2002 (Schnittger) .3D.Shape = FO_S2_bigroot_A.shape +Wed Oct 23 16:07:19 2002 (Schnittger) .Plant Name = S3 big root +Wed Oct 23 16:08:09 2002 (Schnittger) .3D.Bounding Radius = 6 +Wed Oct 23 16:08:09 2002 (Schnittger) .3D.Collision Radius = 6 +Wed Oct 23 16:09:21 2002 (Schnittger) .3D.Bounding Radius = 6.0 +Wed Oct 23 16:09:21 2002 (Schnittger) .3D.Collision Radius = 6.0 +Wed Oct 23 16:12:53 2002 (Schnittger) .Plant Name = S2 big root + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_B.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_B.plant new file mode 100644 index 000000000..117908ea4 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_B.plant @@ -0,0 +1,38 @@ + +
+ + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Bounding Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Collision Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shadow Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .Plant Name = S2 tree +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Bounding Radius = 1.1 +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Collision Radius = 1.1 +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .Plant Name = S3 mushroom +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Bounding Radius = 1.4 +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Collision Radius = 1.4 +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_02.shape +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Shape = FO_S3_champignou_02.shape +Wed Oct 23 16:07:52 2002 (Schnittger) .3D.Shadow Shape = FO_S2_bigroot_B.shape +Wed Oct 23 16:07:52 2002 (Schnittger) .3D.Shape = FO_S2_bigroot_B.shape +Wed Oct 23 16:09:13 2002 (Schnittger) .3D.Bounding Radius = 3.5 +Wed Oct 23 16:09:13 2002 (Schnittger) .3D.Collision Radius = 3.5 +Wed Oct 23 16:12:35 2002 (Schnittger) .Plant Name = s2 root + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_C.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_C.plant new file mode 100644 index 000000000..d531fe87d --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_bigroot_C.plant @@ -0,0 +1,40 @@ + +
+ + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Bounding Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Collision Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shadow Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .Plant Name = S2 tree +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Bounding Radius = 1.1 +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Collision Radius = 1.1 +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .Plant Name = S3 mushroom +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Bounding Radius = 1.4 +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Collision Radius = 1.4 +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_02.shape +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Shape = FO_S3_champignou_02.shape +Wed Oct 23 16:07:52 2002 (Schnittger) .3D.Shadow Shape = FO_S2_bigroot_B.shape +Wed Oct 23 16:07:52 2002 (Schnittger) .3D.Shape = FO_S2_bigroot_B.shape +Wed Oct 23 16:09:13 2002 (Schnittger) .3D.Bounding Radius = 3.5 +Wed Oct 23 16:09:13 2002 (Schnittger) .3D.Collision Radius = 3.5 +Wed Oct 23 16:12:35 2002 (Schnittger) .Plant Name = s2 root +Fri Oct 25 15:17:22 2002 (Schnittger) .3D.Shadow Shape = FO_S2_bigroot_C.shape +Fri Oct 25 15:17:22 2002 (Schnittger) .3D.Shape = FO_S2_bigroot_C.shape + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_birch.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_birch.plant new file mode 100644 index 000000000..75b7191fa --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_birch.plant @@ -0,0 +1,40 @@ + +
+ + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Bounding Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Collision Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shadow Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .Plant Name = S2 tree +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Bounding Radius = 1.1 +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Collision Radius = 1.1 +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .Plant Name = S3 mushroom +Wed Oct 16 12:24:28 2002 (Schnittger) .3D.Bounding Radius = 1.4 +Wed Oct 16 12:24:28 2002 (Schnittger) .3D.Collision Radius = 1.4 +Tue Oct 29 11:45:31 2002 (Schnittger) .3D.Bounding Radius = 4 +Tue Oct 29 11:45:31 2002 (Schnittger) .3D.Collision Radius = 4 +Tue Oct 29 11:45:31 2002 (Schnittger) .3D.Shadow Shape = FO_S2_bouleau.shape +Tue Oct 29 11:45:31 2002 (Schnittger) .3D.Shape = FO_S2_bouleau.shape +Tue Oct 29 11:45:31 2002 (Schnittger) .Plant Name = s2 birch +Tue Oct 29 12:12:07 2002 (Schnittger) .3D.Bounding Radius = 2.0 +Tue Oct 29 12:12:07 2002 (Schnittger) .3D.Collision Radius = 0.25 +Thu Nov 07 15:56:02 2002 (Schnittger) .3D.Shadow Shape = FO_S2_birch.shape +Thu Nov 07 15:56:02 2002 (Schnittger) .3D.Shape = FO_S2_birch.shape + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_spiketree.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_spiketree.plant new file mode 100644 index 000000000..145a7e3b7 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/FO_S2_spiketree.plant @@ -0,0 +1,26 @@ + +
+ + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Bounding Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Collision Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shadow Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .Plant Name = S2 tree +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Bounding Radius = 1.1 +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Collision Radius = 1.1 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/Fo_s1_giant_tree.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/Fo_s1_giant_tree.plant new file mode 100644 index 000000000..c2e4bd2a9 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/Fo_s1_giant_tree.plant @@ -0,0 +1,69 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:43 2002 (corvazier) File converted from old format +Wed Oct 23 18:14:04 2002 (Schnittger) .3D.Bounding Radius = 5 +Wed Oct 23 18:22:39 2002 (Schnittger) .3D.Collision Radius = 1 +Wed Oct 23 18:41:25 2002 (Schnittger) .3D.Bounding Radius = 1 +Wed Oct 23 18:41:25 2002 (Schnittger) .3D.Collision Radius = 5 +Wed Oct 23 18:56:15 2002 (Schnittger) .3D.Bounding Radius = 6 +Wed Oct 23 18:56:15 2002 (Schnittger) .3D.Collision Radius = 2 +Tue Nov 05 11:11:44 2002 (Schnittger) .3D.Collision Radius = 4 +Wed Nov 19 14:21:47 2003 (firroloni) .3D.SpringFX.CycleDuration = 12 +Wed Nov 19 14:21:47 2003 (firroloni) .3D.SpringFX.FXName = Ju_Fo_Birds.ps +Wed Nov 19 14:21:47 2003 (firroloni) .3D.SpringFX.Mode = Spawn +Wed Nov 19 14:21:47 2003 (firroloni) .3D.SpringFX.StartHourMax = 11.9 +Wed Nov 19 14:21:47 2003 (firroloni) .3D.SpringFX.UserParam0Max = 1 +Wed Nov 19 14:21:47 2003 (firroloni) .3D.SpringFX.UserParam0Min = .5 +Wed Nov 19 14:21:47 2003 (firroloni) formName Pasted = +Wed Nov 19 14:23:27 2003 (firroloni) .3D.WinterFX.CycleDuration = 24 +Wed Nov 19 14:23:27 2003 (firroloni) .3D.WinterFX.StartHourMax = 23.9 +Wed Nov 19 14:23:27 2003 (firroloni) .3D.WinterFX.UserParam0Max = .5 +Wed Nov 19 14:23:27 2003 (firroloni) .3D.WinterFX.UserParam0Min = 0 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_S3_champignou_A.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_S3_champignou_A.plant new file mode 100644 index 000000000..c126a8a2a --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_S3_champignou_A.plant @@ -0,0 +1,33 @@ + +
+ + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Bounding Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Collision Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shadow Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .Plant Name = S2 tree +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Bounding Radius = 1.1 +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Collision Radius = 1.1 +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .Plant Name = S3 mushroom +Wed Oct 16 12:24:28 2002 (Schnittger) .3D.Bounding Radius = 1.4 +Wed Oct 16 12:24:28 2002 (Schnittger) .3D.Collision Radius = 1.4 +Wed Nov 27 10:13:21 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_A.shape +Wed Nov 27 10:13:21 2002 (Schnittger) .3D.Shape = FO_S3_champignou_A.shape + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_S3_champignou_B.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_S3_champignou_B.plant new file mode 100644 index 000000000..9bb4e1fd2 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_S3_champignou_B.plant @@ -0,0 +1,35 @@ + +
+ + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:45 2002 (corvazier) File converted from old format +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Bounding Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Collision Radius = 3 +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shadow Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .3D.Shape = FO_S2_spiketree.shape +Tue Oct 08 16:16:31 2002 (Schnittger) .Plant Name = S2 tree +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Bounding Radius = 1.1 +Wed Oct 09 17:28:56 2002 (Schnittger) .3D.Collision Radius = 1.1 +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .3D.Shape = FO_S3_champignou_01.shape +Wed Oct 16 12:23:19 2002 (Schnittger) .Plant Name = S3 mushroom +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Bounding Radius = 1.4 +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Collision Radius = 1.4 +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_02.shape +Wed Oct 16 12:24:10 2002 (Schnittger) .3D.Shape = FO_S3_champignou_02.shape +Wed Nov 27 10:13:04 2002 (Schnittger) .3D.Shadow Shape = FO_S3_champignou_B.shape +Wed Nov 27 10:13:04 2002 (Schnittger) .3D.Shape = FO_S3_champignou_B.shape + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_s1_arbreagrelot.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_s1_arbreagrelot.plant new file mode 100644 index 000000000..0d433b8b7 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_s1_arbreagrelot.plant @@ -0,0 +1,55 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Thu Aug 28 14:33:11 2003 (delord) .3D.Shadow Shape = fo_S1_arbreagrelot +Thu Aug 28 14:33:11 2003 (delord) .3D.Shape = fo_s1_arbreagrelot +Thu Aug 28 15:26:21 2003 (delord) .3D.Bounding Radius = 5 +Thu Aug 28 16:50:35 2003 (delord) .3D.Bounding Radius = 10 +Thu Aug 28 16:50:35 2003 (delord) .3D.Collision Radius = 5 +Thu Aug 28 16:50:46 2003 (delord) .3D.Shadow Shape = fo_S1_arbreagrelot.shape +Thu Aug 28 16:50:46 2003 (delord) .3D.Shape = fo_s1_arbreagrelot.shape +Mon Sep 08 10:22:30 2003 (mauduit) .3D.Shadow Shape = fo_S1_arbragrelot.shape +Mon Sep 08 10:22:30 2003 (mauduit) .3D.Shape = fo_s1_arbragrelot.shape +Wed Nov 19 14:18:06 2003 (firroloni) .3D.AutomnFX.CycleDuration = 10 +Wed Nov 19 14:18:06 2003 (firroloni) .3D.AutomnFX.StartHourMax = 9.9 +Wed Nov 19 14:18:06 2003 (firroloni) .3D.WinterFX.CycleDuration = 24 +Wed Nov 19 14:18:06 2003 (firroloni) .3D.WinterFX.StartHourMax = 20 +Wed Nov 19 14:18:06 2003 (firroloni) .3D.WinterFX.StartHourMin = 5 +Wed Nov 19 14:18:06 2003 (firroloni) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_s2_arbragrelot.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_s2_arbragrelot.plant new file mode 100644 index 000000000..210ab664e --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/forest/common/fo_s2_arbragrelot.plant @@ -0,0 +1,53 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Converted from old format + Fri May 17 15:17:44 2002 (corvazier) File converted from old format +Wed Nov 19 14:18:56 2003 (firroloni) .3D.AutomnFX.CycleDuration = 10 +Wed Nov 19 14:18:56 2003 (firroloni) .3D.AutomnFX.StartHourMax = 9.9 +Wed Nov 19 14:18:56 2003 (firroloni) .3D.WinterFX.CycleDuration = 24 +Wed Nov 19 14:18:56 2003 (firroloni) .3D.WinterFX.StartHourMax = 22 +Wed Nov 19 14:18:56 2003 (firroloni) .3D.WinterFX.StartHourMin = 2 +Wed Nov 19 14:18:56 2003 (firroloni) formName Pasted = + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_bamboo_a.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_bamboo_a.plant new file mode 100644 index 000000000..d82d18211 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_bamboo_a.plant @@ -0,0 +1,21 @@ + +
+ + + + + + + + + + + + Converted from old format + Fri May 17 15:17:35 2002 (corvazier) File converted from old format +Mon Jun 30 10:46:13 2003 (mauduit) .3D.Shape = tr_s2_bamboo_a.shape +Mon Jun 30 10:54:00 2003 (mauduit) .3D.Bounding Radius = 2.5 +Mon Jun 30 10:54:00 2003 (mauduit) .3D.Collision Radius = 1.5 +Mon Jun 30 16:38:25 2003 (mauduit) .3D.Bounding Radius = 5 +Mon Jun 30 16:38:25 2003 (mauduit) .3D.Collision Radius = 3 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_a.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_a.plant new file mode 100644 index 000000000..9a6ebf7bb --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_a.plant @@ -0,0 +1,21 @@ + +
+ + + + + + + + + + + + Converted from old format + Fri May 17 15:17:35 2002 (corvazier) File converted from old format +Mon Jun 30 10:44:58 2003 (mauduit) .3D.Shape = tr_s2_palmtree_a.shape +Mon Jun 30 10:51:52 2003 (mauduit) .3D.Bounding Radius = 2.0 +Mon Jun 30 10:51:52 2003 (mauduit) .3D.Collision Radius = 0.25 +Mon Jun 30 16:33:25 2003 (mauduit) .3D.Bounding Radius = 4 +Mon Jun 30 16:33:25 2003 (mauduit) .3D.Collision Radius = 0.5 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_b.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_b.plant new file mode 100644 index 000000000..c6723b02a --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_b.plant @@ -0,0 +1,21 @@ + +
+ + + + + + + + + + + + Converted from old format + Fri May 17 15:17:35 2002 (corvazier) File converted from old format +Mon Jun 30 10:45:09 2003 (mauduit) .3D.Shape = tr_s2_palmtree_b.shape +Mon Jun 30 10:52:08 2003 (mauduit) .3D.Bounding Radius = 3.0 +Mon Jun 30 10:52:08 2003 (mauduit) .3D.Collision Radius = 0.25 +Mon Jun 30 16:33:12 2003 (mauduit) .3D.Bounding Radius = 6.0 +Mon Jun 30 16:33:12 2003 (mauduit) .3D.Collision Radius = 0.5 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_c.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_c.plant new file mode 100644 index 000000000..8b2d89062 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_c.plant @@ -0,0 +1,21 @@ + +
+ + + + + + + + + + + + Converted from old format + Fri May 17 15:17:35 2002 (corvazier) File converted from old format +Mon Jun 30 10:45:20 2003 (mauduit) .3D.Shape = tr_s2_palmtree_c.shape +Mon Jun 30 10:52:28 2003 (mauduit) .3D.Bounding Radius = 3.0 +Mon Jun 30 10:52:28 2003 (mauduit) .3D.Collision Radius = 0.25 +Mon Jun 30 16:33:05 2003 (mauduit) .3D.Bounding Radius = 6 +Mon Jun 30 16:33:05 2003 (mauduit) .3D.Collision Radius = 0.5 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_d.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_d.plant new file mode 100644 index 000000000..b05685470 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_d.plant @@ -0,0 +1,22 @@ + +
+ + + + + + + + + + + + Converted from old format + Fri May 17 15:17:35 2002 (corvazier) File converted from old format +Mon Jun 30 10:45:28 2003 (mauduit) .3D.Shape = tr_s2_palmtree_c.shape +Mon Jun 30 10:45:37 2003 (mauduit) .3D.Shape = tr_s2_palmtree_d.shape +Mon Jun 30 10:52:47 2003 (mauduit) .3D.Bounding Radius = 3.0 +Mon Jun 30 10:52:47 2003 (mauduit) .3D.Collision Radius = 0.25 +Mon Jun 30 16:32:59 2003 (mauduit) .3D.Bounding Radius = 6 +Mon Jun 30 16:32:59 2003 (mauduit) .3D.Collision Radius = 0.5 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_e.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_e.plant new file mode 100644 index 000000000..3a78b30e8 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_e.plant @@ -0,0 +1,23 @@ + +
+ + + + + + + + + + + + Converted from old format + Fri May 17 15:17:35 2002 (corvazier) File converted from old format +Mon Jun 30 10:45:50 2003 (mauduit) .3D.Shape = tr_s2_palmtree_e.shape +Mon Jun 30 10:53:01 2003 (mauduit) .3D.Bounding Radius = 5.0 +Mon Jun 30 10:53:01 2003 (mauduit) .3D.Collision Radius = 0.5 +Mon Jun 30 16:32:50 2003 (mauduit) .3D.Bounding Radius = 10 +Mon Jun 30 16:32:50 2003 (mauduit) .3D.Collision Radius = 1 +Mon Jun 30 16:35:01 2003 (mauduit) .3D.Bounding Radius = 6 +Mon Jun 30 16:35:01 2003 (mauduit) .3D.Collision Radius = 0.5 + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_f.plant b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_f.plant new file mode 100644 index 000000000..7e9c16adc --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Plant/ecosystem/lacustre/common/tr_s2_palmtree_f.plant @@ -0,0 +1,22 @@ + +
+ + + + + + + + + + + + Converted from old format + Fri May 17 15:17:35 2002 (corvazier) File converted from old format +Mon Jun 30 10:45:50 2003 (mauduit) .3D.Shape = tr_s2_palmtree_e.shape +Mon Jun 30 10:53:01 2003 (mauduit) .3D.Bounding Radius = 5.0 +Mon Jun 30 10:53:01 2003 (mauduit) .3D.Collision Radius = 0.5 +Mon Jun 30 16:32:14 2003 (mauduit) .3D.Bounding Radius = 10.0 +Mon Jun 30 16:32:14 2003 (mauduit) .3D.Collision Radius = 1.0 +Mon Jun 30 16:38:45 2003 (mauduit) .3D.Shape = tr_s2_palmtree_f.shape + From 8c10508b0f2f66d4d818082b083ff96f4e97fa3a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 20 Sep 2011 18:11:51 +0200 Subject: [PATCH 111/215] Added: #1092 Some extra log info. --- code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp b/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp index 03326cf44..e4bc7006a 100644 --- a/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp +++ b/code/nel/tools/3d/plugin_max/nel_mesh_lib/calc_lm.cpp @@ -2587,9 +2587,10 @@ bool CExportNel::calculateLM( CMesh::CMeshBuild *pZeMeshBuild, CMeshBase::CMeshB string sLMName = sBaseName + NLMISC::toString(i) + ".tga"; if (CFile::fileExists(sLMName)) { + nlinfo("DELETE %s", sLMName.c_str()); if (!CFile::deleteFile(sLMName)) { - nlwarning("Failed to delete file %s.", sLMName.c_str()); + nlwarning("Failed to delete file %s", sLMName.c_str()); } } } @@ -2608,6 +2609,7 @@ bool CExportNel::calculateLM( CMesh::CMeshBuild *pZeMeshBuild, CMeshBase::CMeshB COFile f( sSaveName ); try { + nlinfo("SAVE %s", sSaveName.c_str()); if (lmcEnabled) { // In fact the output is 32 bits because we need the alpha channel From 69472443003b64b1a8bbaf2e6fc68e2538ddf659 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 20 Sep 2011 18:13:27 +0200 Subject: [PATCH 112/215] Added: #1093 Added command line argument for client_patch script to only generate updated bnp files without creating a patch. --- .../tools/build_gamedata/6_client_patch.py | 106 +++++++++--------- 1 file changed, 56 insertions(+), 50 deletions(-) diff --git a/code/nel/tools/build_gamedata/6_client_patch.py b/code/nel/tools/build_gamedata/6_client_patch.py index 7689cc404..c41312df3 100644 --- a/code/nel/tools/build_gamedata/6_client_patch.py +++ b/code/nel/tools/build_gamedata/6_client_patch.py @@ -24,9 +24,13 @@ # along with this program. If not, see . # -import time, sys, os, shutil, subprocess, distutils.dir_util +import time, sys, os, shutil, subprocess, distutils.dir_util, argparse sys.path.append("configuration") +parser = argparse.ArgumentParser(description='Ryzom Core - Build Gamedata - Client Patch') +parser.add_argument('--bnponly', '-bo', action='store_true') +args = parser.parse_args() + if os.path.isfile("log.log"): os.remove("log.log") log = open("log.log", "w") @@ -59,56 +63,57 @@ printLog(log, "") if BnpMake == "": toolLogFail(log, BnpMakeTool, ToolSuffix) -elif PatchGen == "": +elif PatchGen == "" and not args.bnponly: toolLogFail(log, PatchGenTool, ToolSuffix) -elif Lzma == "": +elif Lzma == "" and not args.bnponly: toolLogFail(log, "LZMA", ToolSuffix) -elif XDelta == "": +elif XDelta == "" and not args.bnponly: toolLogFail(log, "XDELTA", ToolSuffix) elif os.path.dirname(Lzma) != os.path.dirname(XDelta): printLog(log, "FAIL lzma.exe and xdelta.exe must be in the same directory") else: mkPath(log, ClientPatchDirectory) - productXml = ClientPatchDirectory + "/" + ProductName + ".xml" - if not os.path.isfile(productXml): - printLog(log, ">>> Create new product <<<") - subprocess.call([ PatchGen, "createNewProduct", productXml ]) - printLog(log, "") - printLog(log, ">>> Rewrite " + ProductName + ".xml <<<") # because we know better. - shutil.move(productXml, productXml + ".old") - oldCfg = open(productXml + ".old", "r") - cfg = open(productXml, "w") - inCategories = 0 - for line in oldCfg: - if not inCategories: - if line.strip() == "<_Categories>": - inCategories = 1 - cfg.write("\t<_Categories>\n") - for category in InstallClientData: - cfg.write("\t\t<_Category>\n") - cfg.write("\t\t\t<_Name type=\"STRING\" value=\"" + category["Name"] + "\"/>\n") - if category["UnpackTo"] != None: - if category["UnpackTo"] != "": - cfg.write("\t\t\t<_UnpackTo type=\"STRING\" value=\"./" + category["UnpackTo"] + "/\"/>\n") - else: - cfg.write("\t\t\t<_UnpackTo type=\"SINT32\" value=\"./\"/>\n") - cfg.write("\t\t\t<_IsOptional type=\"SINT32\" value=\"" + str(category["IsOptional"]) + "\"/>\n") - cfg.write("\t\t\t<_IsIncremental type=\"SINT32\" value=\"" + str(category["IsIncremental"]) + "\"/>\n") - for package in category["Packages"]: - if (len(package[1]) > 0): - cfg.write("\t\t\t<_Files type=\"STRING\" value=\"" + package[1][0] + "\"/>\n") - else: - cfg.write("\t\t\t<_Files type=\"STRING\" value=\"" + package[0] + ".bnp\"/>\n") - cfg.write("\t\t\n") - cfg.write("\t\n") + if not args.bnponly: + productXml = ClientPatchDirectory + "/" + ProductName + ".xml" + if not os.path.isfile(productXml): + printLog(log, ">>> Create new product <<<") + subprocess.call([ PatchGen, "createNewProduct", productXml ]) + printLog(log, "") + printLog(log, ">>> Rewrite " + ProductName + ".xml <<<") # because we know better. + shutil.move(productXml, productXml + ".old") + oldCfg = open(productXml + ".old", "r") + cfg = open(productXml, "w") + inCategories = 0 + for line in oldCfg: + if not inCategories: + if line.strip() == "<_Categories>": + inCategories = 1 + cfg.write("\t<_Categories>\n") + for category in InstallClientData: + cfg.write("\t\t<_Category>\n") + cfg.write("\t\t\t<_Name type=\"STRING\" value=\"" + category["Name"] + "\"/>\n") + if category["UnpackTo"] != None: + if category["UnpackTo"] != "": + cfg.write("\t\t\t<_UnpackTo type=\"STRING\" value=\"./" + category["UnpackTo"] + "/\"/>\n") + else: + cfg.write("\t\t\t<_UnpackTo type=\"SINT32\" value=\"./\"/>\n") + cfg.write("\t\t\t<_IsOptional type=\"SINT32\" value=\"" + str(category["IsOptional"]) + "\"/>\n") + cfg.write("\t\t\t<_IsIncremental type=\"SINT32\" value=\"" + str(category["IsIncremental"]) + "\"/>\n") + for package in category["Packages"]: + if (len(package[1]) > 0): + cfg.write("\t\t\t<_Files type=\"STRING\" value=\"" + package[1][0] + "\"/>\n") + else: + cfg.write("\t\t\t<_Files type=\"STRING\" value=\"" + package[0] + ".bnp\"/>\n") + cfg.write("\t\t\n") + cfg.write("\t\n") + else: + cfg.write(line) else: - cfg.write(line) - else: - if line.strip() == "": - inCategories = 0 - oldCfg.close() - cfg.close() - os.remove(productXml + ".old") + if line.strip() == "": + inCategories = 0 + oldCfg.close() + cfg.close() + os.remove(productXml + ".old") printLog(log, "") printLog(log, ">>> Make bnp <<<") targetPath = ClientPatchDirectory + "/bnp" @@ -133,13 +138,14 @@ else: else: printLog(log, "SKIP " + targetBnp) printLog(log, "") - printLog(log, ">>> Update product <<<") - cwDir = os.getcwd().replace("\\", "/") - toolDir = os.path.dirname(Lzma).replace("\\", "/") - os.chdir(toolDir) - subprocess.call([ PatchGen, "updateProduct", productXml ]) - os.chdir(cwDir) - printLog(log, "") + if not args.bnponly: + printLog(log, ">>> Update product <<<") + cwDir = os.getcwd().replace("\\", "/") + toolDir = os.path.dirname(Lzma).replace("\\", "/") + os.chdir(toolDir) + subprocess.call([ PatchGen, "updateProduct", productXml ]) + os.chdir(cwDir) + printLog(log, "") log.close() From c7a00d4ce0a6ab22019369eec038635d1efd19f7 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 21 Sep 2011 23:16:03 +0200 Subject: [PATCH 113/215] Changed: #1093 #1092 Stability workarounds for plant shape exporting. --- .../generators/max_exporter_scripts/shape.ms | 33 ++++++++++++++++- .../export_footer.ms | 4 +- .../build_gamedata/processes/anim/1_export.py | 2 +- .../processes/anim/maxscript/anim_export.ms | 4 +- .../processes/clodbank/1_export.py | 2 +- .../clodbank/maxscript/clod_export.ms | 4 +- .../processes/ig/maxscript/ig_export.ms | 4 +- .../processes/rbank/1_export.py | 2 +- .../processes/rbank/maxscript/cmb_export.ms | 4 +- .../processes/shape/maxscript/shape_export.ms | 37 ++++++++++++++++++- .../processes/veget/1_export.py | 2 +- .../processes/veget/maxscript/veget_export.ms | 4 +- 12 files changed, 89 insertions(+), 13 deletions(-) diff --git a/code/nel/tools/build_gamedata/generators/max_exporter_scripts/shape.ms b/code/nel/tools/build_gamedata/generators/max_exporter_scripts/shape.ms index 0f8af5276..c1f4761ff 100644 --- a/code/nel/tools/build_gamedata/generators/max_exporter_scripts/shape.ms +++ b/code/nel/tools/build_gamedata/generators/max_exporter_scripts/shape.ms @@ -187,7 +187,7 @@ fn haveCoarseMesh node = return false ) -fn runNelMaxExport inputMaxFile = +fn runNelMaxExportSub inputMaxFile retryCount = ( tagThisFile = false @@ -354,8 +354,39 @@ fn runNelMaxExport inputMaxFile = ( -- Error nlerror("WARNING no shape exported from the file " + inputMaxFile) + if tagThisFile then + ( + if retryCount < 2 then + ( + nlerror("INFO retry this file") + + -- Free memory and file handles + gc() + heapfree + + -- Reset 3dsmax + resetMAXFile #noprompt + + if (loadMaxFile inputMaxFile quiet:true) == true then + ( + tagThisFile = runNelMaxExportSub inputMaxFile (retryCount + 1) + ) + else + ( + -- Error + nlerror("ERROR exporting '%PreGenFileExtension%': can't open the file " + inputMaxFile) + nlerror("FAIL Mysterious error occured") + NelForceQuitRightNow() + ) + ) + ) ) return tagThisFile ) +fn runNelMaxExport inputMaxFile = +( + return runNelMaxExportSub inputMaxFile 0 +) + diff --git a/code/nel/tools/build_gamedata/generators/tagged_max_exporter_template/export_footer.ms b/code/nel/tools/build_gamedata/generators/tagged_max_exporter_template/export_footer.ms index 5facf07e7..2db696b7f 100644 --- a/code/nel/tools/build_gamedata/generators/tagged_max_exporter_template/export_footer.ms +++ b/code/nel/tools/build_gamedata/generators/tagged_max_exporter_template/export_footer.ms @@ -90,7 +90,9 @@ try catch ( -- Error - nlerror("ERROR fatal error exporting '%PreGenFileExtension%' in folder %MaxSourceDirectory%") + nlerror("ERROR Fatal error exporting '%PreGenFileExtension%' in folder %MaxSourceDirectory%") + nlerror("FAIL Fatal error occured") + NelForceQuitRightNow() removeRunningTag = false ) diff --git a/code/nel/tools/build_gamedata/processes/anim/1_export.py b/code/nel/tools/build_gamedata/processes/anim/1_export.py index 3bdd5e028..5d5fe4b84 100644 --- a/code/nel/tools/build_gamedata/processes/anim/1_export.py +++ b/code/nel/tools/build_gamedata/processes/anim/1_export.py @@ -6,7 +6,7 @@ # # \file 1_export.py # \brief Export anim -# \date 2010-09-26-08-38-GMT +# \date 2011-09-21-20-51-GMT # \author Jan Boon (Kaetemi) # Python port of game data build pipeline. # Export anim diff --git a/code/nel/tools/build_gamedata/processes/anim/maxscript/anim_export.ms b/code/nel/tools/build_gamedata/processes/anim/maxscript/anim_export.ms index 86d02fd15..7121e16d5 100644 --- a/code/nel/tools/build_gamedata/processes/anim/maxscript/anim_export.ms +++ b/code/nel/tools/build_gamedata/processes/anim/maxscript/anim_export.ms @@ -224,7 +224,9 @@ try catch ( -- Error - nlerror("ERROR fatal error exporting 'anim' in folder %MaxSourceDirectory%") + nlerror("ERROR Fatal error exporting 'anim' in folder %MaxSourceDirectory%") + nlerror("FAIL Fatal error occured") + NelForceQuitRightNow() removeRunningTag = false ) diff --git a/code/nel/tools/build_gamedata/processes/clodbank/1_export.py b/code/nel/tools/build_gamedata/processes/clodbank/1_export.py index a9f6cf7c1..183307e30 100644 --- a/code/nel/tools/build_gamedata/processes/clodbank/1_export.py +++ b/code/nel/tools/build_gamedata/processes/clodbank/1_export.py @@ -6,7 +6,7 @@ # # \file 1_export.py # \brief Export clodbank -# \date 2010-09-26-08-38-GMT +# \date 2011-09-21-20-51-GMT # \author Jan Boon (Kaetemi) # Python port of game data build pipeline. # Export clodbank diff --git a/code/nel/tools/build_gamedata/processes/clodbank/maxscript/clod_export.ms b/code/nel/tools/build_gamedata/processes/clodbank/maxscript/clod_export.ms index 1ab3ce7e2..f01a973e4 100644 --- a/code/nel/tools/build_gamedata/processes/clodbank/maxscript/clod_export.ms +++ b/code/nel/tools/build_gamedata/processes/clodbank/maxscript/clod_export.ms @@ -279,7 +279,9 @@ try catch ( -- Error - nlerror("ERROR fatal error exporting 'clod' in folder %MaxSourceDirectory%") + nlerror("ERROR Fatal error exporting 'clod' in folder %MaxSourceDirectory%") + nlerror("FAIL Fatal error occured") + NelForceQuitRightNow() removeRunningTag = false ) diff --git a/code/nel/tools/build_gamedata/processes/ig/maxscript/ig_export.ms b/code/nel/tools/build_gamedata/processes/ig/maxscript/ig_export.ms index 5618a68d9..526d06dc8 100644 --- a/code/nel/tools/build_gamedata/processes/ig/maxscript/ig_export.ms +++ b/code/nel/tools/build_gamedata/processes/ig/maxscript/ig_export.ms @@ -328,7 +328,9 @@ try catch ( -- Error - nlerror("ERROR fatal error exporting 'ig' in folder %MaxSourceDirectory%") + nlerror("ERROR Fatal error exporting 'ig' in folder %MaxSourceDirectory%") + nlerror("FAIL Fatal error occured") + NelForceQuitRightNow() removeRunningTag = false ) diff --git a/code/nel/tools/build_gamedata/processes/rbank/1_export.py b/code/nel/tools/build_gamedata/processes/rbank/1_export.py index de32ae841..20c7aa54d 100644 --- a/code/nel/tools/build_gamedata/processes/rbank/1_export.py +++ b/code/nel/tools/build_gamedata/processes/rbank/1_export.py @@ -6,7 +6,7 @@ # # \file 1_export.py # \brief Export rbank -# \date 2010-09-26-08-38-GMT +# \date 2011-09-21-20-51-GMT # \author Jan Boon (Kaetemi) # Python port of game data build pipeline. # Export rbank diff --git a/code/nel/tools/build_gamedata/processes/rbank/maxscript/cmb_export.ms b/code/nel/tools/build_gamedata/processes/rbank/maxscript/cmb_export.ms index c60a675d0..74cf3a8dd 100644 --- a/code/nel/tools/build_gamedata/processes/rbank/maxscript/cmb_export.ms +++ b/code/nel/tools/build_gamedata/processes/rbank/maxscript/cmb_export.ms @@ -215,7 +215,9 @@ try catch ( -- Error - nlerror("ERROR fatal error exporting 'cmb' in folder %MaxSourceDirectory%") + nlerror("ERROR Fatal error exporting 'cmb' in folder %MaxSourceDirectory%") + nlerror("FAIL Fatal error occured") + NelForceQuitRightNow() removeRunningTag = false ) diff --git a/code/nel/tools/build_gamedata/processes/shape/maxscript/shape_export.ms b/code/nel/tools/build_gamedata/processes/shape/maxscript/shape_export.ms index 06a02bcbb..e979f6b0d 100644 --- a/code/nel/tools/build_gamedata/processes/shape/maxscript/shape_export.ms +++ b/code/nel/tools/build_gamedata/processes/shape/maxscript/shape_export.ms @@ -253,7 +253,7 @@ fn haveCoarseMesh node = return false ) -fn runNelMaxExport inputMaxFile = +fn runNelMaxExportSub inputMaxFile retryCount = ( tagThisFile = false @@ -420,11 +420,42 @@ fn runNelMaxExport inputMaxFile = ( -- Error nlerror("WARNING no shape exported from the file " + inputMaxFile) + if tagThisFile then + ( + if retryCount < 2 then + ( + nlerror("INFO retry this file") + + -- Free memory and file handles + gc() + heapfree + + -- Reset 3dsmax + resetMAXFile #noprompt + + if (loadMaxFile inputMaxFile quiet:true) == true then + ( + tagThisFile = runNelMaxExportSub inputMaxFile (retryCount + 1) + ) + else + ( + -- Error + nlerror("ERROR exporting 'shape': can't open the file " + inputMaxFile) + nlerror("FAIL Mysterious error occured") + NelForceQuitRightNow() + ) + ) + ) ) return tagThisFile ) +fn runNelMaxExport inputMaxFile = +( + return runNelMaxExportSub inputMaxFile 0 +) + removeRunningTag = true @@ -517,7 +548,9 @@ try catch ( -- Error - nlerror("ERROR fatal error exporting 'shape' in folder %MaxSourceDirectory%") + nlerror("ERROR Fatal error exporting 'shape' in folder %MaxSourceDirectory%") + nlerror("FAIL Fatal error occured") + NelForceQuitRightNow() removeRunningTag = false ) diff --git a/code/nel/tools/build_gamedata/processes/veget/1_export.py b/code/nel/tools/build_gamedata/processes/veget/1_export.py index 61e33ea74..e6d2c53cc 100644 --- a/code/nel/tools/build_gamedata/processes/veget/1_export.py +++ b/code/nel/tools/build_gamedata/processes/veget/1_export.py @@ -6,7 +6,7 @@ # # \file 1_export.py # \brief Export veget -# \date 2010-09-26-08-38-GMT +# \date 2011-09-21-20-51-GMT # \author Jan Boon (Kaetemi) # Python port of game data build pipeline. # Export veget diff --git a/code/nel/tools/build_gamedata/processes/veget/maxscript/veget_export.ms b/code/nel/tools/build_gamedata/processes/veget/maxscript/veget_export.ms index 4390e5cca..828a78d42 100644 --- a/code/nel/tools/build_gamedata/processes/veget/maxscript/veget_export.ms +++ b/code/nel/tools/build_gamedata/processes/veget/maxscript/veget_export.ms @@ -269,7 +269,9 @@ try catch ( -- Error - nlerror("ERROR fatal error exporting 'veget' in folder %MaxSourceDirectory%") + nlerror("ERROR Fatal error exporting 'veget' in folder %MaxSourceDirectory%") + nlerror("FAIL Fatal error occured") + NelForceQuitRightNow() removeRunningTag = false ) From b7eae7319bb87bdb1efd9681031b3ac715dfffa2 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 21 Sep 2011 23:17:00 +0200 Subject: [PATCH 114/215] Changed: #1093 Also copy .packed files in sheets install. --- code/nel/tools/build_gamedata/processes/sheets/3_install.py | 1 + 1 file changed, 1 insertion(+) diff --git a/code/nel/tools/build_gamedata/processes/sheets/3_install.py b/code/nel/tools/build_gamedata/processes/sheets/3_install.py index 98b5cb825..f4093b12e 100644 --- a/code/nel/tools/build_gamedata/processes/sheets/3_install.py +++ b/code/nel/tools/build_gamedata/processes/sheets/3_install.py @@ -49,6 +49,7 @@ mkPath(log, installPath) printLog(log, ">>> Install sheets <<<") mkPath(log, ExportBuildDirectory + "/" + SheetsBuildDirectory) copyFilesExtNoTreeIfNeeded(log, ExportBuildDirectory + "/" + SheetsBuildDirectory, installPath, ".packed_sheets") +copyFilesExtNoTreeIfNeeded(log, ExportBuildDirectory + "/" + SheetsBuildDirectory, installPath, ".packed") printLog(log, "") log.close() From 5a5304f823fba430d3d13aaeced331f82a214e2a Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 21 Sep 2011 23:17:39 +0200 Subject: [PATCH 115/215] Changed: #1093 Added generic helmets directory to export shape sources. --- .../build_gamedata/workspace/common/characters/directories.py | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ryzom/tools/build_gamedata/workspace/common/characters/directories.py b/code/ryzom/tools/build_gamedata/workspace/common/characters/directories.py index cd97ef675..c5db38fd3 100644 --- a/code/ryzom/tools/build_gamedata/workspace/common/characters/directories.py +++ b/code/ryzom/tools/build_gamedata/workspace/common/characters/directories.py @@ -91,6 +91,7 @@ ShapeSourceDirectories += [ "stuff/caravan/agents/actors/visages" ] ShapeSourceDirectories += [ "stuff/caravan/agents/actors/ship" ] ShapeSourceDirectories += [ "stuff/generique/agents/actors/female" ] ShapeSourceDirectories += [ "stuff/generique/agents/actors/male" ] +ShapeSourceDirectories += [ "stuff/generique/agents/actors/visages" ] # Animation directories AnimSourceDirectories = [ ] From 9501c9065418d8c229258e9743b1e68e7ae53b27 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 21 Sep 2011 23:38:40 +0200 Subject: [PATCH 116/215] Added: Leveldesign sheets required for building client sheets used for newbieland. --- .../Game_elem/Building/guard_tower.building | 12 + .../Game_elem/Pacts/pacts.death_impact | 9 + .../anim/fx/auras.id_to_string_array | 9 + .../anim/fx/auras/_aura.animation_fx | 13 + .../damage_shield.attack_list | 12 + .../homin/magic_cur_homin.attack_list | 34 ++ .../homin/magic_off_homin.attack_list | 33 ++ .../fx/combat/attack_melee/melee.attack_list | 41 +++ .../fx/combat/attack_range/range.attack_list | 12 + .../fx_cast/_cast_base.animation_fx_set | 9 + .../cast_cur_healhp_begin.animation_fx_set | 33 ++ .../cast_cur_healhp_end.animation_fx_set | 22 ++ .../cast_cur_healhp_fail.animation_fx_set | 22 ++ .../cast_cur_healhp_loop.animation_fx_set | 26 ++ .../cast_off_acid_begin.animation_fx_set | 33 ++ .../cast_off_acid_end.animation_fx_set | 22 ++ .../cast_off_acid_fail.animation_fx_set | 22 ++ .../cast_off_acid_loop.animation_fx_set | 26 ++ .../acid_impact.animation_fx_set | 13 + .../healhp_impact.animation_fx_set | 13 + ...agic_projectile_cur_chain.animation_fx_set | 17 + ...agic_projectile_off_chain.animation_fx_set | 24 ++ .../_magic_projectile_acid.animation_fx_set | 13 + .../_magic_projectile_healhp.animation_fx_set | 13 + ...gic_projectile_acid_chain.animation_fx_set | 10 + ...c_projectile_healhp_chain.animation_fx_set | 10 + .../range_impact_generic.animation_fx_set | 18 + .../range_projectile_missile.animation_fx_set | 17 + .../anim/fx/links.id_to_string_array | 9 + .../anim/fx/links/_link.animation_fx | 13 + .../_range_impact_generic_base.animation_fx | 12 + .../fx/misc/anim_fx_misc.id_to_string_array | 9 + .../fx/static/object_street_lamp.animation_fx | 13 + .../forage_source/0_0.forage_source | 6 + .../forage_source/_fx0.forage_source | 11 + .../game_element/fx/toxic_cloud_0.fx | 19 + .../game_element/fx/toxic_cloud_1.fx | 19 + .../game_element/fx/toxic_cloud_2.fx | 19 + .../outpost/building/empty.outpost_building | 14 + .../outpost/outpost/_outpost.outpost | 13 + .../outpost/example_outpost_01.outpost | 10 + .../squad/_free_squad_parent.outpost_squad | 12 + ..._squad_light_melee_fighter_b.outpost_squad | 10 + ..._squad_light_melee_fighter_c.outpost_squad | 10 + ..._squad_light_melee_fighter_d.outpost_squad | 10 + ..._squad_light_melee_fighter_e.outpost_squad | 10 + ..._squad_light_melee_fighter_f.outpost_squad | 10 + .../game_element/test_sky/fo_mainland_au.sky | 343 ++++++++++++++++++ .../game_element/test_sky/fo_mainland_sp.sky | 334 +++++++++++++++++ .../game_element/test_sky/fo_mainland_su.sky | 335 +++++++++++++++++ .../game_element/test_sky/fo_mainland_wi.sky | 335 +++++++++++++++++ .../game_element/xp_table/unblock.titles | 52 +++ 52 files changed, 2166 insertions(+) create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Building/guard_tower.building create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Pacts/pacts.death_impact create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/auras.id_to_string_array create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/auras/_aura.animation_fx create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_damage_shield/damage_shield.attack_list create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_magic/homin/magic_cur_homin.attack_list create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_magic/homin/magic_off_homin.attack_list create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_melee/melee.attack_list create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_range/range.attack_list create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/_cast_base.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_begin.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_end.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_fail.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_loop.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_begin.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_end.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_fail.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_loop.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_impact/acid_impact.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_impact/healhp_impact.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base/_magic_projectile_cur_chain.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base/_magic_projectile_off_chain.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base_per_spell/_magic_projectile_acid.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base_per_spell/_magic_projectile_healhp.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/magic_projectile_acid_chain.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/magic_projectile_healhp_chain.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_range_impact/range_impact_generic.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_range_projectile/range_projectile_missile.animation_fx_set create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/links.id_to_string_array create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/links/_link.animation_fx create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/misc/_range_impact_generic_base.animation_fx create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/misc/anim_fx_misc.id_to_string_array create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/static/object_street_lamp.animation_fx create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/forage_source/0_0.forage_source create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/forage_source/_fx0.forage_source create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_0.fx create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_1.fx create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_2.fx create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/building/empty.outpost_building create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/outpost/_outpost.outpost create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/outpost/example_outpost_01.outpost create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/_free_squad_parent.outpost_squad create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_b.outpost_squad create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_c.outpost_squad create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_d.outpost_squad create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_e.outpost_squad create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_f.outpost_squad create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_au.sky create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_sp.sky create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_su.sky create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_wi.sky create mode 100644 code/ryzom/common/data_leveldesign/leveldesign/game_element/xp_table/unblock.titles diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Building/guard_tower.building b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Building/guard_tower.building new file mode 100644 index 000000000..5e7652f13 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Building/guard_tower.building @@ -0,0 +1,12 @@ + +
+ + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Pacts/pacts.death_impact b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Pacts/pacts.death_impact new file mode 100644 index 000000000..f1639ce18 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/Game_elem/Pacts/pacts.death_impact @@ -0,0 +1,9 @@ + +
+ + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/auras.id_to_string_array b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/auras.id_to_string_array new file mode 100644 index 000000000..f1639ce18 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/auras.id_to_string_array @@ -0,0 +1,9 @@ + +
+ + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/auras/_aura.animation_fx b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/auras/_aura.animation_fx new file mode 100644 index 000000000..a509e1207 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/auras/_aura.animation_fx @@ -0,0 +1,13 @@ + +
+ + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_damage_shield/damage_shield.attack_list b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_damage_shield/damage_shield.attack_list new file mode 100644 index 000000000..ab547e781 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_damage_shield/damage_shield.attack_list @@ -0,0 +1,12 @@ + +
+ + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_magic/homin/magic_cur_homin.attack_list b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_magic/homin/magic_cur_homin.attack_list new file mode 100644 index 000000000..e12e37af7 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_magic/homin/magic_cur_homin.attack_list @@ -0,0 +1,34 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_magic/homin/magic_off_homin.attack_list b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_magic/homin/magic_off_homin.attack_list new file mode 100644 index 000000000..0d328d7eb --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_magic/homin/magic_off_homin.attack_list @@ -0,0 +1,33 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_melee/melee.attack_list b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_melee/melee.attack_list new file mode 100644 index 000000000..0e1bca9e9 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_melee/melee.attack_list @@ -0,0 +1,41 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_range/range.attack_list b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_range/range.attack_list new file mode 100644 index 000000000..303af5eaa --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/attack_range/range.attack_list @@ -0,0 +1,12 @@ + +
+ + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/_cast_base.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/_cast_base.animation_fx_set new file mode 100644 index 000000000..9cfb9cb44 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/_cast_base.animation_fx_set @@ -0,0 +1,9 @@ + +
+ + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_begin.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_begin.animation_fx_set new file mode 100644 index 000000000..714ff935e --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_begin.animation_fx_set @@ -0,0 +1,33 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + Thu Feb 05 19:15:32 2004 (vizerie) .FX0.PSName = fdsfdsf +Wed Jul 21 10:24:15 2004 (gatto) .FX0.RepeatMode = Respawn +Wed Jul 21 10:45:42 2004 (gatto) .FX0.ScaleFX = true +Wed Jul 21 10:45:42 2004 (gatto) .FX1.RepeatMode = Respawn +Wed Jul 21 10:45:42 2004 (gatto) .FX1.ScaleFX = true + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_end.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_end.animation_fx_set new file mode 100644 index 000000000..b032a83e5 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_end.animation_fx_set @@ -0,0 +1,22 @@ + +
+ + + + + + + + + + + + + + + + + Thu Feb 05 19:15:32 2004 (vizerie) .FX0.PSName = fdsfdsf +Wed Jul 21 10:24:23 2004 (gatto) .FX0.RepeatMode = Respawn +Wed Jul 21 10:45:31 2004 (gatto) .FX0.ScaleFX = true + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_fail.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_fail.animation_fx_set new file mode 100644 index 000000000..d72c0a0ba --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_fail.animation_fx_set @@ -0,0 +1,22 @@ + +
+ + + + + + + + + + + + + + + + + Thu Feb 05 19:15:32 2004 (vizerie) .FX0.PSName = fdsfdsf +Wed Jul 21 10:24:07 2004 (gatto) .FX0.RepeatMode = Respawn +Wed Jul 21 10:45:20 2004 (gatto) .FX0.ScaleFX = true + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_loop.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_loop.animation_fx_set new file mode 100644 index 000000000..aecddc3f3 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_cur_healhp_loop.animation_fx_set @@ -0,0 +1,26 @@ + +
+ + + + + + + + + + + + + + + + + + + + Thu Feb 05 19:15:32 2004 (vizerie) .FX0.PSName = fdsfdsf +Wed Jul 21 10:23:47 2004 (gatto) .FX0.RepeatMode = Respawn +Wed Jul 21 10:45:12 2004 (gatto) .FX0.ScaleFX = true +Wed Jul 21 10:45:12 2004 (gatto) .FX1.ScaleFX = true + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_begin.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_begin.animation_fx_set new file mode 100644 index 000000000..c8323d2ba --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_begin.animation_fx_set @@ -0,0 +1,33 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + Thu Feb 05 19:15:32 2004 (vizerie) .FX0.PSName = fdsfdsf +Wed Jul 21 10:25:38 2004 (gatto) .FX0.RepeatMode = Respawn +Wed Jul 21 10:43:23 2004 (gatto) .FX0.ScaleFX = true +Wed Jul 21 10:43:23 2004 (gatto) .FX1.RepeatMode = Respawn +Wed Jul 21 10:43:23 2004 (gatto) .FX1.ScaleFX = true + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_end.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_end.animation_fx_set new file mode 100644 index 000000000..c07d41023 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_end.animation_fx_set @@ -0,0 +1,22 @@ + +
+ + + + + + + + + + + + + + + + + Thu Feb 05 19:15:32 2004 (vizerie) .FX0.PSName = fdsfdsf +Wed Jul 21 10:32:01 2004 (gatto) .FX0.RepeatMode = Respawn +Wed Jul 21 10:43:10 2004 (gatto) .FX0.ScaleFX = true + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_fail.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_fail.animation_fx_set new file mode 100644 index 000000000..19d8e8e1d --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_fail.animation_fx_set @@ -0,0 +1,22 @@ + +
+ + + + + + + + + + + + + + + + + Thu Feb 05 19:15:32 2004 (vizerie) .FX0.PSName = fdsfdsf +Wed Jul 21 10:31:50 2004 (gatto) .FX0.RepeatMode = Respawn +Wed Jul 21 10:43:03 2004 (gatto) .FX0.ScaleFX = true + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_loop.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_loop.animation_fx_set new file mode 100644 index 000000000..af4ac7c13 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_cast/homin/new_casts/cast_off_acid_loop.animation_fx_set @@ -0,0 +1,26 @@ + +
+ + + + + + + + + + + + + + + + + + + + Thu Feb 05 19:15:32 2004 (vizerie) .FX0.PSName = fdsfdsf +Wed Jul 21 10:30:11 2004 (gatto) .FX0.RepeatMode = Respawn +Wed Jul 21 10:42:56 2004 (gatto) .FX0.ScaleFX = true +Wed Jul 21 10:42:56 2004 (gatto) .FX1.ScaleFX = true + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_impact/acid_impact.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_impact/acid_impact.animation_fx_set new file mode 100644 index 000000000..041374f16 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_impact/acid_impact.animation_fx_set @@ -0,0 +1,13 @@ + +
+ + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_impact/healhp_impact.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_impact/healhp_impact.animation_fx_set new file mode 100644 index 000000000..2dad4d39f --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_impact/healhp_impact.animation_fx_set @@ -0,0 +1,13 @@ + +
+ + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base/_magic_projectile_cur_chain.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base/_magic_projectile_cur_chain.animation_fx_set new file mode 100644 index 000000000..2d6e50e37 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base/_magic_projectile_cur_chain.animation_fx_set @@ -0,0 +1,17 @@ + +
+ + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base/_magic_projectile_off_chain.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base/_magic_projectile_off_chain.animation_fx_set new file mode 100644 index 000000000..0a1bf25e2 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base/_magic_projectile_off_chain.animation_fx_set @@ -0,0 +1,24 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base_per_spell/_magic_projectile_acid.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base_per_spell/_magic_projectile_acid.animation_fx_set new file mode 100644 index 000000000..0b2dbd524 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base_per_spell/_magic_projectile_acid.animation_fx_set @@ -0,0 +1,13 @@ + +
+ + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base_per_spell/_magic_projectile_healhp.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base_per_spell/_magic_projectile_healhp.animation_fx_set new file mode 100644 index 000000000..126a0afb0 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/base_per_spell/_magic_projectile_healhp.animation_fx_set @@ -0,0 +1,13 @@ + +
+ + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/magic_projectile_acid_chain.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/magic_projectile_acid_chain.animation_fx_set new file mode 100644 index 000000000..d50382993 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/magic_projectile_acid_chain.animation_fx_set @@ -0,0 +1,10 @@ + +
+ + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/magic_projectile_healhp_chain.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/magic_projectile_healhp_chain.animation_fx_set new file mode 100644 index 000000000..40bb6c5f0 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_magic_projectile/magic_projectile_healhp_chain.animation_fx_set @@ -0,0 +1,10 @@ + +
+ + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_range_impact/range_impact_generic.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_range_impact/range_impact_generic.animation_fx_set new file mode 100644 index 000000000..45de25fc5 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_range_impact/range_impact_generic.animation_fx_set @@ -0,0 +1,18 @@ + +
+ + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_range_projectile/range_projectile_missile.animation_fx_set b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_range_projectile/range_projectile_missile.animation_fx_set new file mode 100644 index 000000000..4a54c42fa --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/combat/fx_range_projectile/range_projectile_missile.animation_fx_set @@ -0,0 +1,17 @@ + +
+ + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/links.id_to_string_array b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/links.id_to_string_array new file mode 100644 index 000000000..f1639ce18 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/links.id_to_string_array @@ -0,0 +1,9 @@ + +
+ + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/links/_link.animation_fx b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/links/_link.animation_fx new file mode 100644 index 000000000..d47ad132f --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/links/_link.animation_fx @@ -0,0 +1,13 @@ + +
+ + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/misc/_range_impact_generic_base.animation_fx b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/misc/_range_impact_generic_base.animation_fx new file mode 100644 index 000000000..068b23f78 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/misc/_range_impact_generic_base.animation_fx @@ -0,0 +1,12 @@ + +
+ + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/misc/anim_fx_misc.id_to_string_array b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/misc/anim_fx_misc.id_to_string_array new file mode 100644 index 000000000..f1639ce18 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/misc/anim_fx_misc.id_to_string_array @@ -0,0 +1,9 @@ + +
+ + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/static/object_street_lamp.animation_fx b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/static/object_street_lamp.animation_fx new file mode 100644 index 000000000..30b916944 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/anim/fx/static/object_street_lamp.animation_fx @@ -0,0 +1,13 @@ + +
+ + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/forage_source/0_0.forage_source b/code/ryzom/common/data_leveldesign/leveldesign/game_element/forage_source/0_0.forage_source new file mode 100644 index 000000000..7f17235b8 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/forage_source/0_0.forage_source @@ -0,0 +1,6 @@ + +
+ + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/forage_source/_fx0.forage_source b/code/ryzom/common/data_leveldesign/leveldesign/game_element/forage_source/_fx0.forage_source new file mode 100644 index 000000000..43393bcf0 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/forage_source/_fx0.forage_source @@ -0,0 +1,11 @@ + +
+ + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_0.fx b/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_0.fx new file mode 100644 index 000000000..c5917c2cc --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_0.fx @@ -0,0 +1,19 @@ + +
+ + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_1.fx b/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_1.fx new file mode 100644 index 000000000..bb84bf339 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_1.fx @@ -0,0 +1,19 @@ + +
+ + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_2.fx b/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_2.fx new file mode 100644 index 000000000..d506fb5ec --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/fx/toxic_cloud_2.fx @@ -0,0 +1,19 @@ + +
+ + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/building/empty.outpost_building b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/building/empty.outpost_building new file mode 100644 index 000000000..59acb953d --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/building/empty.outpost_building @@ -0,0 +1,14 @@ + +
+ + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/outpost/_outpost.outpost b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/outpost/_outpost.outpost new file mode 100644 index 000000000..297c4c418 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/outpost/_outpost.outpost @@ -0,0 +1,13 @@ + +
+ + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/outpost/example_outpost_01.outpost b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/outpost/example_outpost_01.outpost new file mode 100644 index 000000000..674a882d5 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/outpost/example_outpost_01.outpost @@ -0,0 +1,10 @@ + +
+ + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/_free_squad_parent.outpost_squad b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/_free_squad_parent.outpost_squad new file mode 100644 index 000000000..391d5da75 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/_free_squad_parent.outpost_squad @@ -0,0 +1,12 @@ + +
+ + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_b.outpost_squad b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_b.outpost_squad new file mode 100644 index 000000000..14fa58ac6 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_b.outpost_squad @@ -0,0 +1,10 @@ + +
+ + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_c.outpost_squad b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_c.outpost_squad new file mode 100644 index 000000000..14fa58ac6 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_c.outpost_squad @@ -0,0 +1,10 @@ + +
+ + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_d.outpost_squad b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_d.outpost_squad new file mode 100644 index 000000000..14fa58ac6 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_d.outpost_squad @@ -0,0 +1,10 @@ + +
+ + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_e.outpost_squad b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_e.outpost_squad new file mode 100644 index 000000000..14fa58ac6 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_e.outpost_squad @@ -0,0 +1,10 @@ + +
+ + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_f.outpost_squad b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_f.outpost_squad new file mode 100644 index 000000000..14fa58ac6 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/outpost/squad/free_squad_light_melee_fighter_f.outpost_squad @@ -0,0 +1,10 @@ + +
+ + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_au.sky b/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_au.sky new file mode 100644 index 000000000..83e981b17 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_au.sky @@ -0,0 +1,343 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_sp.sky b/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_sp.sky new file mode 100644 index 000000000..25576a4d0 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_sp.sky @@ -0,0 +1,334 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_su.sky b/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_su.sky new file mode 100644 index 000000000..7ff9ca703 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_su.sky @@ -0,0 +1,335 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_wi.sky b/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_wi.sky new file mode 100644 index 000000000..704dec27b --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/test_sky/fo_mainland_wi.sky @@ -0,0 +1,335 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/code/ryzom/common/data_leveldesign/leveldesign/game_element/xp_table/unblock.titles b/code/ryzom/common/data_leveldesign/leveldesign/game_element/xp_table/unblock.titles new file mode 100644 index 000000000..eeccae7d7 --- /dev/null +++ b/code/ryzom/common/data_leveldesign/leveldesign/game_element/xp_table/unblock.titles @@ -0,0 +1,52 @@ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 33467640137cf4fae840b1974dee9b7e318f8d33 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 23 Sep 2011 00:14:51 +0200 Subject: [PATCH 117/215] Fixed: #1364 Only transfer bonus item infos when context help string is used for tooltip --- .../ryzom/client/src/interface_v3/ctrl_base.h | 2 + .../client/src/interface_v3/dbctrl_sheet.cpp | 65 +++++++++++-------- .../client/src/interface_v3/dbctrl_sheet.h | 3 + .../src/interface_v3/interface_manager.cpp | 2 +- 4 files changed, 45 insertions(+), 27 deletions(-) diff --git a/code/ryzom/client/src/interface_v3/ctrl_base.h b/code/ryzom/client/src/interface_v3/ctrl_base.h index 0033dce6b..bf0f5f10c 100644 --- a/code/ryzom/client/src/interface_v3/ctrl_base.h +++ b/code/ryzom/client/src/interface_v3/ctrl_base.h @@ -68,6 +68,8 @@ public: /// Get the ContextHelp for this control. Default is to return _ContextHelp virtual void getContextHelp(ucstring &help) const {help= _ContextHelp;} + /// Get the ContextHelp for this control, with tooltip specific code. Default behaviour is identical to getContextHelp. + virtual void getContextHelpToolTip(ucstring &help) const { getContextHelp(help); } // Get the name of the context help window. Default to "context_help" virtual std::string getContextHelpWindowName() const; /// Get the ContextHelp ActionHandler. If "", noop diff --git a/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp b/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp index 42f438202..ce551a0b7 100644 --- a/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp +++ b/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp @@ -2975,32 +2975,9 @@ void CDBCtrlSheet::getContextHelp(ucstring &help) const } else if(getType() == CCtrlSheetInfo::SheetType_Item) { - const CItemSheet *item= asItemSheet(); - if(item) - { - if (item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL || item->Family == ITEMFAMILY::JEWELRY || item->Family == ITEMFAMILY::ARMOR) - { - string luaMethodName = ( (item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) ? "updateCrystallizedSpellTooltip" : "updateBuffItemTooltip"); - CDBCtrlSheet *ctrlSheet = const_cast(this); - if ( ! getInventory().isItemInfoUpToDate(getInventory().getItemSlotId(ctrlSheet))) - { - // Prepare the waiter - ControlSheetTooltipUpdater.ItemSheet= ctrlSheet->getSheetId(); - ControlSheetTooltipUpdater.LuaMethodName = luaMethodName; - ControlSheetTooltipUpdater.ItemSlotId= getInventory().getItemSlotId(ctrlSheet); - ControlSheetTooltipUpdater.CtrlSheet = ctrlSheet; - - // Add the waiter - getInventory().addItemInfoWaiter(&ControlSheetTooltipUpdater); - } - - help = ControlSheetTooltipUpdater.infoValidated(ctrlSheet, luaMethodName); - - } - else - help= getItemActualName(); - - } + const CItemSheet *item = asItemSheet(); + if (item) + help = getItemActualName(); else help= _ContextHelp; } @@ -3106,6 +3083,42 @@ void CDBCtrlSheet::getContextHelp(ucstring &help) const } } +// *************************************************************************** +void CDBCtrlSheet::getContextHelpToolTip(ucstring &help) const +{ + // Special case for buff items and spell crystals, only for tooltips + if (getType() == CCtrlSheetInfo::SheetType_Item) + { + const CItemSheet *item = asItemSheet(); + if (item) + { + if (item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL + || item->Family == ITEMFAMILY::JEWELRY || item->Family == ITEMFAMILY::ARMOR) + { + string luaMethodName = (item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) ? "updateCrystallizedSpellTooltip" : "updateBuffItemTooltip"; + CDBCtrlSheet *ctrlSheet = const_cast(this); + if ( ! getInventory().isItemInfoUpToDate(getInventory().getItemSlotId(ctrlSheet))) + { + // Prepare the waiter + ControlSheetTooltipUpdater.ItemSheet= ctrlSheet->getSheetId(); + ControlSheetTooltipUpdater.LuaMethodName = luaMethodName; + ControlSheetTooltipUpdater.ItemSlotId= getInventory().getItemSlotId(ctrlSheet); + ControlSheetTooltipUpdater.CtrlSheet = ctrlSheet; + + // Add the waiter + getInventory().addItemInfoWaiter(&ControlSheetTooltipUpdater); + } + + help = ControlSheetTooltipUpdater.infoValidated(ctrlSheet, luaMethodName); + return; + } + } + } + + // Default + getContextHelp(help); +} + // *************************************************************************** bool CDBCtrlSheet::canDropItem(CDBCtrlSheet *src) const { diff --git a/code/ryzom/client/src/interface_v3/dbctrl_sheet.h b/code/ryzom/client/src/interface_v3/dbctrl_sheet.h index 5f4425419..2cf34e722 100644 --- a/code/ryzom/client/src/interface_v3/dbctrl_sheet.h +++ b/code/ryzom/client/src/interface_v3/dbctrl_sheet.h @@ -355,6 +355,9 @@ public: /// Special ContextHelp for ctrl sheet. virtual void getContextHelp(ucstring &help) const; + /// Special ContextHelp for ctrl sheet. + virtual void getContextHelpToolTip(ucstring &help) const; + /** true if an item of another ctrlSheet can be dropped on this slot. * also return true if src is 0, or if _ItemSlot==UNDEFINED */ diff --git a/code/ryzom/client/src/interface_v3/interface_manager.cpp b/code/ryzom/client/src/interface_v3/interface_manager.cpp index 9762193d9..180a25763 100644 --- a/code/ryzom/client/src/interface_v3/interface_manager.cpp +++ b/code/ryzom/client/src/interface_v3/interface_manager.cpp @@ -2372,7 +2372,7 @@ void CInterfaceManager::drawContextHelp () if(newCtrl) { // get the text - newCtrl->getContextHelp(_ContextHelpText); + newCtrl->getContextHelpToolTip(_ContextHelpText); // UserDefined context help if( !newCtrl->getContextHelpActionHandler().empty() ) { From ab4468c48ff83522de60aafad59d221576f6fe78 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Fri, 23 Sep 2011 00:20:20 +0200 Subject: [PATCH 118/215] Changed: #28 Clean comments --- code/ryzom/tools/translation_tools/extract_bot_names.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/tools/translation_tools/extract_bot_names.cpp b/code/ryzom/tools/translation_tools/extract_bot_names.cpp index 1b8e69d94..01f50d6cd 100644 --- a/code/ryzom/tools/translation_tools/extract_bot_names.cpp +++ b/code/ryzom/tools/translation_tools/extract_bot_names.cpp @@ -737,7 +737,7 @@ int extractBotNames(int argc, char *argv[]) } } - // display resumé + // display summary nlinfo("Adding %u new simple name", nbAddSimpleName); nlinfo("Adding %u new generic name", nbAddGenericName); nlinfo("Adding %u new function name", nbAddFunction); From 2eac5ab06497f88ca5ed1b4905fe37ee932f4507 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Mon, 26 Sep 2011 00:55:00 +0300 Subject: [PATCH 119/215] Changed: #1193 Now plugin system reads plugin information from plugin-spec file(ovqt_plugin_*.xml) for each plugin. --- .../src/extension_system/iplugin.h | 6 - .../src/extension_system/iplugin_spec.h | 2 +- .../src/extension_system/plugin_manager.cpp | 90 ++++---- .../src/extension_system/plugin_manager.h | 19 +- .../src/extension_system/plugin_spec.cpp | 205 +++++++++++++----- .../src/extension_system/plugin_spec.h | 23 +- .../tools/3d/object_viewer_qt/src/main.cpp | 2 +- 7 files changed, 222 insertions(+), 125 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin.h b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin.h index 864028501..973f80e14 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin.h @@ -95,12 +95,6 @@ public: @endcode */ virtual void setNelContext(NLMISC::INelContext *nelContext) = 0; - - virtual QString name() const = 0; - virtual QString version() const = 0; - virtual QString vendor() const = 0; - virtual QString description() const = 0; - virtual QStringList dependencies() const = 0; }; }; //namespace ExtensionSystem diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_spec.h b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_spec.h index 45895c36f..2aefbb894 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_spec.h +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/iplugin_spec.h @@ -37,8 +37,8 @@ struct State { Invalid = 1, Read, - Loaded, Resolved, + Loaded, Initialized, Running, Stopped, diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp index 3b2431a07..bcd90e351 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp @@ -26,13 +26,14 @@ namespace ExtensionSystem { -CPluginManager::CPluginManager(QObject *parent) +PluginManager::PluginManager(QObject *parent) :IPluginManager(parent), - m_settings(0) + m_settings(0), + m_extension("xml") { } -CPluginManager::~CPluginManager() +PluginManager::~PluginManager() { writeSettings(); stopAll(); @@ -40,7 +41,7 @@ CPluginManager::~CPluginManager() qDeleteAll(m_pluginSpecs); } -void CPluginManager::addObject(QObject *obj) +void PluginManager::addObject(QObject *obj) { QWriteLocker lock(&m_lock); if (obj == 0) @@ -60,7 +61,7 @@ void CPluginManager::addObject(QObject *obj) Q_EMIT objectAdded(obj); } -void CPluginManager::removeObject(QObject *obj) +void PluginManager::removeObject(QObject *obj) { if (obj == 0) { @@ -80,25 +81,25 @@ void CPluginManager::removeObject(QObject *obj) m_allObjects.removeAll(obj); } -QList CPluginManager::allObjects() const +QList PluginManager::allObjects() const { return m_allObjects; } -void CPluginManager::loadPlugins() +void PluginManager::loadPlugins() { - Q_FOREACH (CPluginSpec *spec, m_pluginSpecs) - setPluginState(spec, State::Loaded); - - Q_FOREACH (CPluginSpec *spec, m_pluginSpecs) + Q_FOREACH (PluginSpec *spec, m_pluginSpecs) setPluginState(spec, State::Resolved); - QList queue = loadQueue(); + QList queue = loadQueue(); - Q_FOREACH (CPluginSpec *spec, queue) + Q_FOREACH (PluginSpec *spec, queue) + setPluginState(spec, State::Loaded); + + Q_FOREACH (PluginSpec *spec, queue) setPluginState(spec, State::Initialized); - QListIterator it(queue); + QListIterator it(queue); it.toBack(); while (it.hasPrevious()) setPluginState(it.previous(), State::Running); @@ -106,34 +107,34 @@ void CPluginManager::loadPlugins() Q_EMIT pluginsChanged(); } -QStringList CPluginManager::getPluginPaths() const +QStringList PluginManager::getPluginPaths() const { return m_pluginPaths; } -void CPluginManager::setPluginPaths(const QStringList &paths) +void PluginManager::setPluginPaths(const QStringList &paths) { m_pluginPaths = paths; readPluginPaths(); readSettings(); } -QList CPluginManager::plugins() const +QList PluginManager::plugins() const { return m_ipluginSpecs; } -void CPluginManager::setSettings(QSettings *settings) +void PluginManager::setSettings(QSettings *settings) { m_settings = settings; } -QSettings *CPluginManager::settings() const +QSettings *PluginManager::settings() const { return m_settings; } -void CPluginManager::readSettings() +void PluginManager::readSettings() { if (m_settings) { @@ -141,7 +142,7 @@ void CPluginManager::readSettings() m_settings->beginGroup("PluginManager"); blackList = m_settings->value("BlackList").toStringList(); m_settings->endGroup(); - Q_FOREACH (CPluginSpec *spec, m_pluginSpecs) + Q_FOREACH (PluginSpec *spec, m_pluginSpecs) { QString pluginName = spec->fileName(); @@ -154,14 +155,13 @@ void CPluginManager::readSettings() } } -void CPluginManager::writeSettings() +void PluginManager::writeSettings() { if (m_settings) { QStringList blackList; - Q_FOREACH(CPluginSpec *spec, m_pluginSpecs) + Q_FOREACH(PluginSpec *spec, m_pluginSpecs) { - nlinfo(spec->fileName().toStdString().c_str()); if (!spec->isEnabled()) blackList.push_back(spec->fileName()); } @@ -172,7 +172,7 @@ void CPluginManager::writeSettings() } } -void CPluginManager::readPluginPaths() +void PluginManager::readPluginPaths() { qDeleteAll(m_pluginSpecs); m_pluginSpecs.clear(); @@ -183,11 +183,7 @@ void CPluginManager::readPluginPaths() while (!searchPaths.isEmpty()) { const QDir dir(searchPaths.takeFirst()); -#ifdef Q_OS_WIN - const QFileInfoList files = dir.entryInfoList(QStringList() << QString("ovqt_plugin_*.dll"), QDir::Files); -#else - const QFileInfoList files = dir.entryInfoList(QStringList() << QString("libovqt_plugin_*.so"), QDir::Files); -#endif + const QFileInfoList files = dir.entryInfoList(QStringList() << QString("ovqt_plugin_*.%1").arg(m_extension), QDir::Files); Q_FOREACH (const QFileInfo &file, files) pluginsList << file.absoluteFilePath(); const QFileInfoList dirs = dir.entryInfoList(QDir::Dirs|QDir::NoDotAndDotDot); @@ -197,9 +193,9 @@ void CPluginManager::readPluginPaths() Q_FOREACH (const QString &pluginFile, pluginsList) { - CPluginSpec *spec = new CPluginSpec; - spec->setFileName(pluginFile); + PluginSpec *spec = new PluginSpec; spec->m_pluginManager = this; + spec->setSpecFileName(pluginFile); m_pluginSpecs.append(spec); m_ipluginSpecs.append(spec); } @@ -207,7 +203,7 @@ void CPluginManager::readPluginPaths() Q_EMIT pluginsChanged(); } -void CPluginManager::setPluginState(CPluginSpec *spec, int destState) +void PluginManager::setPluginState(PluginSpec *spec, int destState) { if (spec->hasError() || spec->state() != destState-1) return; @@ -233,7 +229,7 @@ void CPluginManager::setPluginState(CPluginSpec *spec, int destState) default: break; } - Q_FOREACH (const CPluginSpec *depSpec, spec->dependencySpecs()) + Q_FOREACH (const PluginSpec *depSpec, spec->dependencySpecs()) { if (depSpec->state() != destState) { @@ -256,19 +252,19 @@ void CPluginManager::setPluginState(CPluginSpec *spec, int destState) } } -QList CPluginManager::loadQueue() +QList PluginManager::loadQueue() { - QList queue; - Q_FOREACH(CPluginSpec *spec, m_pluginSpecs) + QList queue; + Q_FOREACH(PluginSpec *spec, m_pluginSpecs) { - QList circularityCheckQueue; + QList circularityCheckQueue; loadQueue(spec, queue, circularityCheckQueue); } return queue; } -bool CPluginManager::loadQueue(CPluginSpec *spec, QList &queue, - QList &circularityCheckQueue) +bool PluginManager::loadQueue(PluginSpec *spec, QList &queue, + QList &circularityCheckQueue) { if (queue.contains(spec)) return true; @@ -295,7 +291,7 @@ bool CPluginManager::loadQueue(CPluginSpec *spec, QList &queue, } // add dependencies - Q_FOREACH (CPluginSpec *depSpec, spec->dependencySpecs()) + Q_FOREACH (PluginSpec *depSpec, spec->dependencySpecs()) { if (!loadQueue(depSpec, queue, circularityCheckQueue)) { @@ -311,17 +307,17 @@ bool CPluginManager::loadQueue(CPluginSpec *spec, QList &queue, return true; } -void CPluginManager::stopAll() +void PluginManager::stopAll() { - QList queue = loadQueue(); - Q_FOREACH (CPluginSpec *spec, queue) + QList queue = loadQueue(); + Q_FOREACH (PluginSpec *spec, queue) setPluginState(spec, State::Stopped); } -void CPluginManager::deleteAll() +void PluginManager::deleteAll() { - QList queue = loadQueue(); - QListIterator it(queue); + QList queue = loadQueue(); + QListIterator it(queue); it.toBack(); while (it.hasPrevious()) { diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.h b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.h index 4ef3b2208..9ab1ae135 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.h +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.h @@ -29,15 +29,15 @@ namespace ExtensionSystem { class IPlugin; -class CPluginSpec; +class PluginSpec; -class CPluginManager : public IPluginManager +class PluginManager : public IPluginManager { Q_OBJECT public: - CPluginManager(QObject *parent = 0); - ~CPluginManager(); + PluginManager(QObject *parent = 0); + ~PluginManager(); // Object pool operations virtual void addObject(QObject *obj); @@ -49,7 +49,7 @@ public: virtual QStringList getPluginPaths() const; virtual void setPluginPaths(const QStringList &paths); virtual QList plugins() const; - QList loadQueue(); + QList loadQueue(); // Settings virtual void setSettings(QSettings *settings); @@ -58,21 +58,22 @@ public: void writeSettings(); private: - void setPluginState(CPluginSpec *spec, int destState); + void setPluginState(PluginSpec *spec, int destState); void readPluginPaths(); - bool loadQueue(CPluginSpec *spec, QList &queue, QList &circularityCheckQueue); + bool loadQueue(PluginSpec *spec, QList &queue, QList &circularityCheckQueue); void stopAll(); void deleteAll(); mutable QReadWriteLock m_lock; QSettings *m_settings; - QList m_pluginSpecs; + QString m_extension; + QList m_pluginSpecs; QList m_ipluginSpecs; QStringList m_pluginPaths; QList m_allObjects; -}; // class CPluginManager +}; // class PluginManager } // namespace ExtensionSystem diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp index 6e15e1181..030f9507b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp @@ -16,12 +16,15 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +// Project includes #include "plugin_spec.h" #include "iplugin.h" #include "iplugin_manager.h" #include "nel/misc/app_context.h" +#include "nel/misc/debug.h" +// Qt includes #include #include #include @@ -30,8 +33,17 @@ namespace ExtensionSystem { +const char *const PLUGIN_SPEC_NAME = "name"; +const char *const PLUGIN_SPEC_VENDOR = "vendor"; +const char *const PLUGIN_SPEC_VERSION = "version"; +const char *const PLUGIN_SPEC_LIBRARY_NAME = "library-name"; +const char *const PLUGIN_SPEC_DESCRIPTION = "description"; +const char *const PLUGIN_SPEC_DEPENDENCIES = "dependencies"; +const char *const PLUGIN_SPEC_DEPENDENCY = "dependency"; +const char *const PLUGIN_SPEC_DEPENDENCY_NAME = "plugin-name"; +const char *const PLUGIN_SPEC_DEPENDENCY_VERSION = "version"; -CPluginSpec::CPluginSpec() +PluginSpec::PluginSpec() : m_location(""), m_filePath(""), m_fileName(""), @@ -39,6 +51,8 @@ CPluginSpec::CPluginSpec() m_version(""), m_vendor(""), m_description(""), + m_nameSpecFile(""), + m_suffix(""), m_state(State::Invalid), m_enabled(true), m_enabledStartup(true), @@ -47,104 +61,195 @@ CPluginSpec::CPluginSpec() m_plugin(0), m_pluginManager(0) { +#ifdef Q_OS_WIN +# ifdef DEBUG + m_suffix = "_d.dll"; +# else + m_suffix = "_r.dll"; +# endif +#else + m_suffix = ".so"; +#endif } -QString CPluginSpec::name() const +QString PluginSpec::name() const { return m_name; } -QString CPluginSpec::version() const +QString PluginSpec::version() const { return m_version; } -QString CPluginSpec::vendor() const +QString PluginSpec::vendor() const { return m_vendor; } -QString CPluginSpec::description() const +QString PluginSpec::description() const { return m_description; } -QString CPluginSpec::location() const +QString PluginSpec::location() const { return m_location; } -QString CPluginSpec::filePath() const +QString PluginSpec::filePath() const { return m_filePath; } -QString CPluginSpec::fileName() const +QString PluginSpec::fileName() const { return m_fileName; } -IPlugin *CPluginSpec::plugin() const +IPlugin *PluginSpec::plugin() const { return m_plugin; } -int CPluginSpec::state() const +int PluginSpec::state() const { return m_state; } -bool CPluginSpec::hasError() const +bool PluginSpec::hasError() const { return m_hasError; } -QString CPluginSpec::errorString() const +QString PluginSpec::errorString() const { return m_errorString; } -QList CPluginSpec::dependencySpecs() const +QList PluginSpec::dependencySpecs() const { return m_dependencySpecs; } -bool CPluginSpec::setFileName(const QString &fileName) +bool PluginSpec::setFileName(const QString &fileName) { - QFile file(fileName); + m_fileName = fileName + m_suffix; + m_filePath = m_location + "/" + m_fileName; + + nlinfo(m_filePath.toStdString().c_str()); + QFile file(m_filePath); if (!file.exists()) - return reportError(QCoreApplication::translate("CPluginSpec", "File does not exist: %1").arg(file.fileName())); + return reportError(QCoreApplication::translate("PluginSpec", "File does not exist: %1").arg(file.fileName())); if (!file.open(QIODevice::ReadOnly)) - return reportError(QCoreApplication::translate("CPluginSpec", "Could not open file for read: %1").arg(file.fileName())); + return reportError(QCoreApplication::translate("PluginSpec", "Could not open file for read: %1").arg(file.fileName())); + return true; +} + +bool PluginSpec::setSpecFileName(const QString &specFileName) +{ + m_nameSpecFile = specFileName; + + QFile file(specFileName); + if (!file.exists()) + return reportError(QCoreApplication::translate("PluginSpec", "Spec file does not exist: %1").arg(file.fileName())); QFileInfo fileInfo(file); m_location = fileInfo.absolutePath(); - m_filePath = fileInfo.absoluteFilePath(); - m_fileName = fileInfo.fileName(); + readSpec(); + return true; +} +bool PluginSpec::readSpec() +{ + QFile file(m_nameSpecFile); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + return reportError(QCoreApplication::translate("PluginSpec", "Could not open spec file for read: %1").arg(file.fileName())); + + QXmlStreamReader reader(&file); + while (!reader.atEnd()) + { + if (reader.isStartElement()) + parseSpec(reader); + reader.readNext(); + } + if (reader.hasError()) + return reportError(QCoreApplication::translate("PluginSpec", "Error parsing file %1: %2, at line %3, column %4") + .arg(file.fileName()) + .arg(reader.errorString()) + .arg(reader.lineNumber()) + .arg(reader.columnNumber())); m_state = State::Read; return true; } -void CPluginSpec::setEnabled(bool enabled) +void PluginSpec::parseSpec(QXmlStreamReader &reader) +{ + QString elemName = reader.name().toString(); + reader.readNext(); + if (reader.isCharacters()) + { + QString elemText = reader.text().toString(); + if (elemName == PLUGIN_SPEC_LIBRARY_NAME) + setFileName(elemText); + if (elemName == PLUGIN_SPEC_NAME) + m_name = elemText; + if (elemName == PLUGIN_SPEC_VERSION) + m_version = elemText; + if (elemName == PLUGIN_SPEC_VENDOR) + m_vendor = elemText; + if (elemName == PLUGIN_SPEC_DESCRIPTION) + m_description = elemText; + if (elemName == PLUGIN_SPEC_DEPENDENCIES) + parseDependency(reader); + } +} + +void PluginSpec::parseDependency(QXmlStreamReader &reader) +{ + QString elemName; + while (!reader.atEnd() && (elemName != PLUGIN_SPEC_DEPENDENCIES)) + { + reader.readNext(); + elemName = reader.name().toString(); + if (reader.isStartElement() && (elemName == PLUGIN_SPEC_DEPENDENCY)) + { + // Read name dependency plugin + QString dependencyName = reader.attributes().value(PLUGIN_SPEC_DEPENDENCY_NAME).toString(); + if (dependencyName.isEmpty()) + { + reader.raiseError(QCoreApplication::translate("CPluginSpec", "'%1' misses attribute '%2'") + .arg(PLUGIN_SPEC_DEPENDENCY) + .arg(PLUGIN_SPEC_DEPENDENCY_NAME)); + return; + } + // Read version dependency plugin + QString dependencyVersion = reader.attributes().value(PLUGIN_SPEC_DEPENDENCY_VERSION).toString(); + + m_dependencies.push_back(dependencyName); + } + } +} + +void PluginSpec::setEnabled(bool enabled) { m_enabled = enabled; } -bool CPluginSpec::isEnabled() const +bool PluginSpec::isEnabled() const { return m_enabled; } -bool CPluginSpec::loadLibrary() +bool PluginSpec::loadLibrary() { if (m_hasError) return false; - if (m_state != State::Read) + if (m_state != State::Resolved) { if (m_state == State::Loaded) return true; - return reportError(QCoreApplication::translate("CPluginSpec", "Loading the library failed because state != Resolved")); + return reportError(QCoreApplication::translate("PluginSpec", "Loading the library failed because state != Resolved")); } QPluginLoader loader(m_filePath); @@ -155,38 +260,32 @@ bool CPluginSpec::loadLibrary() if (!pluginObject) { loader.unload(); - return reportError(QCoreApplication::translate("CPluginSpec", "Plugin is not valid (does not derive from IPlugin)")); + return reportError(QCoreApplication::translate("PluginSpec", "Plugin is not valid (does not derive from IPlugin)")); } pluginObject->setNelContext(&NLMISC::INelContext::getInstance()); - m_name = pluginObject->name(); - m_version = pluginObject->version(); - m_vendor = pluginObject->vendor(); - m_description = pluginObject->description(); - m_state = State::Loaded; m_plugin = pluginObject; return true; } -bool CPluginSpec::resolveDependencies(const QList &specs) +bool PluginSpec::resolveDependencies(const QList &specs) { if (m_hasError) return false; - if (m_state != State::Loaded) + if (m_state != State::Read) { - m_errorString = QCoreApplication::translate("CPluginSpec", "Resolving dependencies failed because state != Read"); + m_errorString = QCoreApplication::translate("PluginSpec", "Resolving dependencies failed because state != Read"); m_hasError = true; return false; } - QList resolvedDependencies; - QStringList dependencies = m_plugin->dependencies(); - Q_FOREACH(const QString &dependency, dependencies) + QList resolvedDependencies; + Q_FOREACH(const QString &dependency, m_dependencies) { - CPluginSpec *found = 0; + PluginSpec *found = 0; - Q_FOREACH(CPluginSpec *spec, specs) + Q_FOREACH(PluginSpec *spec, specs) { if (QString::compare(dependency, spec->name(), Qt::CaseInsensitive) == 0) { @@ -199,7 +298,7 @@ bool CPluginSpec::resolveDependencies(const QList &specs) m_hasError = true; if (!m_errorString.isEmpty()) m_errorString.append(QLatin1Char('\n')); - m_errorString.append(QCoreApplication::translate("CPluginSpec", "Could not resolve dependency '%1'") + m_errorString.append(QCoreApplication::translate("PluginSpec", "Could not resolve dependency '%1'") .arg(dependency)); continue; } @@ -209,34 +308,32 @@ bool CPluginSpec::resolveDependencies(const QList &specs) return false; m_dependencySpecs = resolvedDependencies; - m_state = State::Resolved; - return true; } -bool CPluginSpec::initializePlugin() +bool PluginSpec::initializePlugin() { if (m_hasError) return false; - if (m_state != State::Resolved) + if (m_state != State::Loaded) { if (m_state == State::Initialized) return true; - return reportError(QCoreApplication::translate("CPluginSpec", "Initializing the plugin failed because state != Resolved)")); + return reportError(QCoreApplication::translate("PluginSpec", "Initializing the plugin failed because state != Loaded)")); } if (!m_plugin) - return reportError(QCoreApplication::translate("CPluginSpec", "Internal error: have no plugin instance to initialize")); + return reportError(QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to initialize")); QString err; if (!m_plugin->initialize(m_pluginManager, &err)) - return reportError(QCoreApplication::translate("CPluginSpec", "Plugin initialization failed: %1").arg(err)); + return reportError(QCoreApplication::translate("PluginSpec", "Plugin initialization failed: %1").arg(err)); m_state = State::Initialized; return true; } -bool CPluginSpec::initializeExtensions() +bool PluginSpec::initializeExtensions() { if (m_hasError) return false; @@ -244,17 +341,17 @@ bool CPluginSpec::initializeExtensions() { if (m_state == State::Running) return true; - return reportError(QCoreApplication::translate("CPluginSpec", "Cannot perform extensionsInitialized because state != Initialized")); + return reportError(QCoreApplication::translate("PluginSpec", "Cannot perform extensionsInitialized because state != Initialized")); } if (!m_plugin) - return reportError(QCoreApplication::translate("CPluginSpec", "Internal error: have no plugin instance to perform extensionsInitialized")); + return reportError(QCoreApplication::translate("PluginSpec", "Internal error: have no plugin instance to perform extensionsInitialized")); m_plugin->extensionsInitialized(); m_state = State::Running; return true; } -void CPluginSpec::stop() +void PluginSpec::stop() { if (!m_plugin) return; @@ -262,7 +359,7 @@ void CPluginSpec::stop() m_state = State::Stopped; } -void CPluginSpec::kill() +void PluginSpec::kill() { if (!m_plugin) return; @@ -271,17 +368,17 @@ void CPluginSpec::kill() m_state = State::Deleted; } -void CPluginSpec::setEnabledStartup(bool enabled) +void PluginSpec::setEnabledStartup(bool enabled) { m_enabledStartup = enabled; } -bool CPluginSpec::isEnabledStartup() const +bool PluginSpec::isEnabledStartup() const { return m_enabledStartup; } -bool CPluginSpec::reportError(const QString &err) +bool PluginSpec::reportError(const QString &err) { m_errorString = err; m_hasError = true; diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.h b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.h index 5d9605de7..c28980d69 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.h +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.h @@ -21,12 +21,14 @@ #include "iplugin_spec.h" -#include "QtCore/QList" +#include +#include +#include namespace ExtensionSystem { -class CPluginSpec: public IPluginSpec +class PluginSpec: public IPluginSpec { public: virtual QString name() const; @@ -44,18 +46,22 @@ public: virtual int state() const; virtual bool hasError() const; virtual QString errorString() const; - QList dependencySpecs() const; + QList dependencySpecs() const; /// Enables/disables load this plugin after restart the program virtual void setEnabled(bool enabled); virtual bool isEnabled() const; private: - CPluginSpec(); + PluginSpec(); bool setFileName(const QString &fileName); + bool setSpecFileName(const QString &specFileName); + bool readSpec(); + void parseSpec(QXmlStreamReader &reader); + void parseDependency(QXmlStreamReader &reader); bool loadLibrary(); - bool resolveDependencies(const QList &specs); + bool resolveDependencies(const QList &specs); bool initializePlugin(); bool initializeExtensions(); void stop(); @@ -77,16 +83,19 @@ private: QString m_vendor; QString m_description; + QString m_nameSpecFile; + QString m_suffix; int m_state; bool m_enabled, m_enabledStartup; bool m_hasError; QString m_errorString; + QStringList m_dependencies; IPlugin *m_plugin; IPluginManager *m_pluginManager; - QList m_dependencySpecs; + QList m_dependencySpecs; - friend class CPluginManager; + friend class PluginManager; }; } // namespace ExtensionSystem diff --git a/code/nel/tools/3d/object_viewer_qt/src/main.cpp b/code/nel/tools/3d/object_viewer_qt/src/main.cpp index 1ddcc5565..4b904bdc6 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/main.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/main.cpp @@ -148,7 +148,7 @@ sint main(int argc, char **argv) NLMISC::CLibrary::addLibPath((qApp->applicationDirPath() + QString("/../PlugIns/nel")).toStdString()); #endif - ExtensionSystem::CPluginManager pluginManager; + ExtensionSystem::PluginManager pluginManager; pluginManager.setSettings(settings); QStringList pluginPaths; #if !defined(NL_OS_MAC) From 54b76eec022e5a8aadc87e2d07a5a17e3a4a0ba0 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Mon, 26 Sep 2011 01:25:10 +0300 Subject: [PATCH 120/215] Changed: #1193 Updated core plugin. Added the plugin-spec file. --- .../src/plugins/core/core.cpp | 3 +- .../object_viewer_qt/src/plugins/core/core.h | 2 +- .../src/plugins/core/core_constants.h | 144 +++++++++--------- .../src/plugins/core/core_plugin.cpp | 29 +--- .../src/plugins/core/core_plugin.h | 6 - .../plugins/core/general_settings_page.cpp | 22 +-- .../object_viewer_qt/src/plugins/core/icore.h | 4 +- .../src/plugins/core/imenu_manager.h | 62 -------- .../src/plugins/core/main_window.cpp | 7 +- .../src/plugins/core/main_window.h | 3 +- .../src/plugins/core/menu_manager.cpp | 48 +++--- .../src/plugins/core/menu_manager.h | 40 +++-- .../src/plugins/core/ovqt_plugin_core.xml | 7 + .../src/plugins/core/plugin_view_dialog.ui | 2 +- .../src/plugins/core/qtwin.cpp | 6 +- .../core/search_paths_settings_page.cpp | 36 ++--- .../plugins/core/search_paths_settings_page.h | 10 +- .../core/search_paths_settings_page.ui | 4 +- .../src/plugins/core/settings_dialog.cpp | 78 +++++----- .../src/plugins/core/settings_dialog.h | 24 +-- .../src/plugins/core/settings_dialog.ui | 8 +- 21 files changed, 233 insertions(+), 312 deletions(-) delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/core/imenu_manager.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/core/ovqt_plugin_core.xml diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.cpp index fa61b5700..b21d934c8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.cpp @@ -17,7 +17,6 @@ // along with this program. If not, see . #include "core.h" -#include "imenu_manager.h" #include "context_manager.h" #include "main_window.h" #include "../../extension_system/iplugin_manager.h" @@ -50,7 +49,7 @@ bool CoreImpl::showOptionsDialog(const QString &group, return m_mainWindow->showOptionsDialog(group, page, parent); } -IMenuManager *CoreImpl::menuManager() const +MenuManager *CoreImpl::menuManager() const { return m_mainWindow->menuManager(); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h index 2613a06a5..c62fac593 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h @@ -37,7 +37,7 @@ public: const QString &page = QString(), QWidget *parent = 0); - virtual IMenuManager *menuManager() const; + virtual MenuManager *menuManager() const; virtual ContextManager *contextManager() const; virtual QSettings *settings() const; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h index 65327ae38..d1b4e902d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_constants.h @@ -23,97 +23,97 @@ namespace Core namespace Constants { -const char * const OVQT_VERSION_LONG = "0.1"; -const char * const OVQT_VENDOR = "Ryzom Core"; -const char * const OVQT_YEAR = "2010, 2011"; -const char * const OVQT_CORE_PLUGIN = "Core"; +const char *const OVQT_VERSION_LONG = "0.8"; +const char *const OVQT_VENDOR = "Ryzom Core"; +const char *const OVQT_YEAR = "2010, 2011"; +const char *const OVQT_CORE_PLUGIN = "Core"; //mainwindow -const char * const MAIN_WINDOW = "ObjectViewerQt.MainWindow"; +const char *const MAIN_WINDOW = "ObjectViewerQt.MainWindow"; //menubar -const char * const MENU_BAR = "ObjectViewerQt.MenuBar"; +const char *const MENU_BAR = "ObjectViewerQt.MenuBar"; //menus -const char * const M_FILE = "ObjectViewerQt.Menu.File"; -const char * const M_EDIT = "ObjectViewerQt.Menu.Edit"; -const char * const M_VIEW = "ObjectViewerQt.Menu.View"; -const char * const M_SCENE = "ObjectViewerQt.Menu.Scene"; -const char * const M_TOOLS = "ObjectViewerQt.Menu.Tools"; -const char * const M_WINDOW = "ObjectViewerQt.Menu.Window"; -const char * const M_HELP = "ObjectViewerQt.Menu.Help"; +const char *const M_FILE = "ObjectViewerQt.Menu.File"; +const char *const M_EDIT = "ObjectViewerQt.Menu.Edit"; +const char *const M_VIEW = "ObjectViewerQt.Menu.View"; +const char *const M_SCENE = "ObjectViewerQt.Menu.Scene"; +const char *const M_TOOLS = "ObjectViewerQt.Menu.Tools"; +const char *const M_WINDOW = "ObjectViewerQt.Menu.Window"; +const char *const M_HELP = "ObjectViewerQt.Menu.Help"; -const char * const M_FILE_RECENTFILES = "ObjectViewerQt.Menu.File.RecentFiles"; -const char * const M_SHEET = "ObjectViewerQt.Menu.Sheet"; +const char *const M_FILE_RECENTFILES = "ObjectViewerQt.Menu.File.RecentFiles"; +const char *const M_SHEET = "ObjectViewerQt.Menu.Sheet"; //actions -const char * const NEW = "ObjectViewerQt.New"; -const char * const OPEN = "ObjectViewerQt.Open"; -const char * const SAVE = "ObjectViewerQt.Save"; -const char * const SAVE_AS = "ObjectViewerQt.SaveAs"; -const char * const SAVE_ALL = "ObjectViewerQt.SaveAll"; -const char * const EXIT = "ObjectViewerQt.Exit"; +const char *const NEW = "ObjectViewerQt.New"; +const char *const OPEN = "ObjectViewerQt.Open"; +const char *const SAVE = "ObjectViewerQt.Save"; +const char *const SAVE_AS = "ObjectViewerQt.SaveAs"; +const char *const SAVE_ALL = "ObjectViewerQt.SaveAll"; +const char *const EXIT = "ObjectViewerQt.Exit"; -const char * const UNDO = "ObjectViewerQt.Undo"; -const char * const REDO = "ObjectViewerQt.Redo"; -const char * const CUT = "ObjectViewerQt.Cut"; -const char * const COPY = "ObjectViewerQt.Copy"; -const char * const PASTE = "ObjectViewerQt.Paste"; -const char * const DEL = "ObjectViewerQt.Del"; -const char * const FIND = "ObjectViewerQt.Find"; -const char * const SELECT_ALL = "ObjectViewerQt.SelectAll"; -const char * const GOTO_POS = "ObjectViewerQt.Goto"; +const char *const UNDO = "ObjectViewerQt.Undo"; +const char *const REDO = "ObjectViewerQt.Redo"; +const char *const CUT = "ObjectViewerQt.Cut"; +const char *const COPY = "ObjectViewerQt.Copy"; +const char *const PASTE = "ObjectViewerQt.Paste"; +const char *const DEL = "ObjectViewerQt.Del"; +const char *const FIND = "ObjectViewerQt.Find"; +const char *const SELECT_ALL = "ObjectViewerQt.SelectAll"; +const char *const GOTO_POS = "ObjectViewerQt.Goto"; -const char * const SETTINGS = "ObjectViewerQt.Settings"; -const char * const TOGGLE_FULLSCREEN = "ObjectViewerQt.ToggleFullScreen"; +const char *const SETTINGS = "ObjectViewerQt.Settings"; +const char *const TOGGLE_FULLSCREEN = "ObjectViewerQt.ToggleFullScreen"; -const char * const CLOSE = "ObjectViewerQt.Close"; -const char * const CLOSEALL = "ObjectViewerQt.CloseAll"; -const char * const CLOSEOTHERS = "ObjectViewerQt.CloseOthers"; -const char * const ABOUT = "ObjectViewerQt.About"; -const char * const ABOUT_PLUGINS = "ObjectViewerQt.AboutPlugins"; -const char * const ABOUT_QT = "ObjectViewerQt.AboutQt"; +const char *const CLOSE = "ObjectViewerQt.Close"; +const char *const CLOSEALL = "ObjectViewerQt.CloseAll"; +const char *const CLOSEOTHERS = "ObjectViewerQt.CloseOthers"; +const char *const ABOUT = "ObjectViewerQt.About"; +const char *const ABOUT_PLUGINS = "ObjectViewerQt.AboutPlugins"; +const char *const ABOUT_QT = "ObjectViewerQt.AboutQt"; //settings -const char * const SETTINGS_CATEGORY_GENERAL = "general"; -const char * const SETTINGS_CATEGORY_GENERAL_ICON = ":/icons/ic_nel_generic_settings.png"; -const char * const SETTINGS_TR_CATEGORY_GENERAL = QT_TR_NOOP("General"); +const char *const SETTINGS_CATEGORY_GENERAL = "general"; +const char *const SETTINGS_CATEGORY_GENERAL_ICON = ":/icons/ic_nel_generic_settings.png"; +const char *const SETTINGS_TR_CATEGORY_GENERAL = QT_TR_NOOP("General"); -const char * const MAIN_WINDOW_SECTION = "MainWindow"; -const char * const MAIN_WINDOW_STATE = "WindowState"; -const char * const MAIN_WINDOW_GEOMETRY = "WindowGeometry"; -const char * const QT_STYLE = "QtStyle"; -const char * const QT_PALETTE = "QtPalette"; +const char *const MAIN_WINDOW_SECTION = "MainWindow"; +const char *const MAIN_WINDOW_STATE = "WindowState"; +const char *const MAIN_WINDOW_GEOMETRY = "WindowGeometry"; +const char *const QT_STYLE = "QtStyle"; +const char *const QT_PALETTE = "QtPalette"; -const char * const LANGUAGE = "Language"; -const char * const PLUGINS_PATH = "PluginPath"; -const char * const DATA_PATH_SECTION = "DataPath"; -const char * const SEARCH_PATHS = "SearchPaths"; -const char * const RECURSIVE_SEARCH_PATHS = "RecursiveSearchPathes"; -const char * const LEVELDESIGN_PATH = "LevelDesignPath"; -const char * const ASSETS_PATH = "AssetsPath"; -const char * const PRIMITIVES_PATH = "PrimitivesPath"; -const char * const LIGOCONFIG_FILE = "LigoConfigFile"; -const char * const REMAP_EXTENSIONS = "RemapExtensions"; +const char *const LANGUAGE = "Language"; +const char *const PLUGINS_PATH = "PluginPath"; +const char *const DATA_PATH_SECTION = "DataPath"; +const char *const SEARCH_PATHS = "SearchPaths"; +const char *const RECURSIVE_SEARCH_PATHS = "RecursiveSearchPathes"; +const char *const LEVELDESIGN_PATH = "LevelDesignPath"; +const char *const ASSETS_PATH = "AssetsPath"; +const char *const PRIMITIVES_PATH = "PrimitivesPath"; +const char *const LIGOCONFIG_FILE = "LigoConfigFile"; +const char *const REMAP_EXTENSIONS = "RemapExtensions"; -const char * const LOG_SECTION = "LogSettings"; -const char * const LOG_ERROR = "LogError"; -const char * const LOG_WARNING = "LogWarning"; -const char * const LOG_DEBUG = "LogDebug"; -const char * const LOG_ASSERT = "LogAssert"; -const char * const LOG_INFO = "LogInfo"; +const char *const LOG_SECTION = "LogSettings"; +const char *const LOG_ERROR = "LogError"; +const char *const LOG_WARNING = "LogWarning"; +const char *const LOG_DEBUG = "LogDebug"; +const char *const LOG_ASSERT = "LogAssert"; +const char *const LOG_INFO = "LogInfo"; //resources -const char * const ICON_NEL = ":/core/images/nel.png"; -const char * const ICON_SETTINGS = ":/core/images/preferences.png"; -const char * const ICON_PILL = ":/core/icons/ic_nel_pill.png"; -const char * const ICON_OPEN = ":/core/icons/ic_nel_open.png"; -const char * const ICON_NEW = ":/core/icons/ic_nel_new.png"; -const char * const ICON_SAVE = ":/core/icons/ic_nel_save.png"; -const char * const ICON_SAVE_AS = ":/core/icons/ic_nel_save_as.png"; -const char * const ICON_CRASH = ":/core/icons/ic_nel_crash.png"; -const char * const ICON_UNDO = ":/core/icons/ic_nel_undo.png"; -const char * const ICON_REDO = ":/core/icons/ic_nel_redo.png"; +const char *const ICON_NEL = ":/core/images/nel.png"; +const char *const ICON_SETTINGS = ":/core/images/preferences.png"; +const char *const ICON_PILL = ":/core/icons/ic_nel_pill.png"; +const char *const ICON_OPEN = ":/core/icons/ic_nel_open.png"; +const char *const ICON_NEW = ":/core/icons/ic_nel_new.png"; +const char *const ICON_SAVE = ":/core/icons/ic_nel_save.png"; +const char *const ICON_SAVE_AS = ":/core/icons/ic_nel_save_as.png"; +const char *const ICON_CRASH = ":/core/icons/ic_nel_crash.png"; +const char *const ICON_UNDO = ":/core/icons/ic_nel_undo.png"; +const char *const ICON_REDO = ":/core/icons/ic_nel_redo.png"; } // namespace Constants } // namespace Core diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.cpp index 8aef51f2a..55633feaf 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.cpp @@ -64,8 +64,8 @@ bool CorePlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QStr bool success = m_mainWindow->initialize(errorString); GeneralSettingsPage *generalSettings = new GeneralSettingsPage(this); - CSearchPathsSettingsPage *searchPathPage = new CSearchPathsSettingsPage(false, this); - CSearchPathsSettingsPage *recureseSearchPathPage = new CSearchPathsSettingsPage(true, this); + SearchPathsSettingsPage *searchPathPage = new SearchPathsSettingsPage(false, this); + SearchPathsSettingsPage *recureseSearchPathPage = new SearchPathsSettingsPage(true, this); generalSettings->applyGeneralSettings(); searchPathPage->applySearchPaths(); @@ -95,31 +95,6 @@ void CorePlugin::setNelContext(NLMISC::INelContext *nelContext) m_libContext = new NLMISC::CLibraryContext(*nelContext); } -QString CorePlugin::name() const -{ - return QLatin1String(Constants::OVQT_CORE_PLUGIN); -} - -QString CorePlugin::version() const -{ - return Constants::OVQT_VERSION_LONG; -} - -QString CorePlugin::vendor() const -{ - return Constants::OVQT_VENDOR; -} - -QString CorePlugin::description() const -{ - return "Core plugin."; -} - -QStringList CorePlugin::dependencies() const -{ - return QStringList(); -} - void CorePlugin::addAutoReleasedObject(QObject *obj) { m_plugMan->addObject(obj); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.h index 5c95d22c9..5062227e3 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core_plugin.h @@ -53,12 +53,6 @@ public: void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - void addAutoReleasedObject(QObject *obj); ExtensionSystem::IPluginManager *pluginManager() const diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp index 88bd3a298..b1ef3b659 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.cpp @@ -148,25 +148,25 @@ void GeneralSettingsPage::setLevelDesignPath() } } -void GeneralSettingsPage::setPrimitivesPath() -{ +void GeneralSettingsPage::setPrimitivesPath() +{ QString newPath = QFileDialog::getExistingDirectory(0, tr("Set the primitives path"), m_ui.primitivesPathLineEdit->text()); if (!newPath.isEmpty()) { m_ui.primitivesPathLineEdit->setText(newPath); - } -} - -void GeneralSettingsPage::setLigoConfigFile() -{ - QString newFile = QFileDialog::getOpenFileName(0, tr("Set the ligo config file"), - m_ui.ligoConfigFileLineEdit->text()); + } +} + +void GeneralSettingsPage::setLigoConfigFile() +{ + QString newFile = QFileDialog::getOpenFileName(0, tr("Set the ligo config file"), + m_ui.ligoConfigFileLineEdit->text()); if (!newFile.isEmpty()) { m_ui.ligoConfigFileLineEdit->setText(newFile); - } -} + } +} void GeneralSettingsPage::setAssetsPath() diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/icore.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/icore.h index 13b22bfa5..4f1ee474f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/icore.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/icore.h @@ -35,7 +35,7 @@ class IPluginManager; namespace Core { -class IMenuManager; +class MenuManager; class ContextManager; class CORE_EXPORT ICore : public QObject @@ -52,7 +52,7 @@ public: const QString &page = QString(), QWidget *parent = 0) = 0; - virtual IMenuManager *menuManager() const = 0; + virtual MenuManager *menuManager() const = 0; virtual ContextManager *contextManager() const = 0; virtual QSettings *settings() const = 0; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/imenu_manager.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/imenu_manager.h deleted file mode 100644 index be80dbbc4..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/imenu_manager.h +++ /dev/null @@ -1,62 +0,0 @@ -// Object Viewer Qt - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// Copyright (C) 2011 Dzmitry Kamiahin -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License -// along with this program. If not, see . - -#ifndef IMENU_MANAGER_H -#define IMENU_MANAGER_H - -#include "core_global.h" - -#include -#include - -QT_BEGIN_NAMESPACE -class QMenu; -class QAction; -class QString; -class QMenuBar; -QT_END_NAMESPACE - -namespace Core -{ -/* -@interface IMenuManager -@brief The IMenuManager is an interface for providing a registration of menus and menu item. -@details The IMenuManager provides centralized access to menus and menu items. -All menus and menu items should be registered in the IMenuManager. -*/ -class CORE_EXPORT IMenuManager : public QObject -{ - Q_OBJECT -public: - IMenuManager(QObject *parent = 0): QObject(parent) {} - virtual ~IMenuManager() {} - - virtual void registerMenu(QMenu *menu, const QString &id) = 0; - virtual void registerAction(QAction *action, const QString &id) = 0; - - virtual QMenu *menu(const QString &id) const = 0; - virtual QAction *action(const QString &id) const = 0; - - virtual void unregisterMenu(const QString &id) = 0; - virtual void unregisterAction(const QString &id) = 0; - - virtual QMenuBar *menuBar() const = 0; -}; - -} // namespace Core - -#endif // IMENU_MANAGER_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.cpp index 2105b8b1b..f00075b21 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.cpp @@ -65,8 +65,7 @@ MainWindow::MainWindow(ExtensionSystem::IPluginManager *pluginManager, QWidget * setMenuBar(m_menuBar); #endif - m_menuManager = new MenuManager(this); - m_menuManager->setMenuBar(m_menuBar); + m_menuManager = new MenuManager(m_menuBar, this); m_tabWidget = new QTabWidget(this); m_tabWidget->setTabPosition(QTabWidget::South); @@ -114,7 +113,7 @@ void MainWindow::extensionsInitialized() show(); } -IMenuManager *MainWindow::menuManager() const +MenuManager *MainWindow::menuManager() const { return m_menuManager; } @@ -205,7 +204,7 @@ bool MainWindow::showOptionsDialog(const QString &group, { if (!parent) parent = this; - CSettingsDialog settingsDialog(m_pluginManager, group, page, parent); + SettingsDialog settingsDialog(m_pluginManager, group, page, parent); settingsDialog.show(); bool ok = settingsDialog.execDialog(); if (ok) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.h index 48ce93a15..ae0d0522c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/main_window.h @@ -34,7 +34,6 @@ namespace Core class CSettingsDialog; class CorePlugin; class IContext; -class IMenuManager; class MenuManager; class ContextManager; class CoreImpl; @@ -50,7 +49,7 @@ public: bool initialize(QString *errorString); void extensionsInitialized(); - IMenuManager *menuManager() const; + MenuManager *menuManager() const; ContextManager *contextManager() const; QSettings *settings() const; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.cpp index 4bd80a616..76a4d7d91 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.cpp @@ -21,74 +21,76 @@ // NeL includes #include -// Qt includes -#include -#include -#include - namespace Core { -MenuManager::MenuManager(QObject *parent) - : IMenuManager(parent), - _menuBar(0) +struct MenuManagerPrivate { + MenuManagerPrivate(): m_menuBar(0) {} + QMenuBar *m_menuBar; + typedef QHash IdMenuMap; + IdMenuMap m_menuMap; + typedef QHash IdActionMap; + IdActionMap m_actionMap; +}; + +MenuManager::MenuManager(QMenuBar *menuBar, QObject *parent) + : QObject(parent), + d(new MenuManagerPrivate()) +{ + d->m_menuBar = menuBar; } MenuManager::~MenuManager() { - _menuMap.clear(); + d->m_menuMap.clear(); + delete d; } void MenuManager::registerMenu(QMenu *menu, const QString &id) { menu->setObjectName(id); - _menuMap.insert(id, menu); + d->m_menuMap.insert(id, menu); } void MenuManager::registerAction(QAction *action, const QString &id) { action->setObjectName(id); - _actionMap.insert(id, action); + d->m_actionMap.insert(id, action); } QMenu *MenuManager::menu(const QString &id) const { QMenu *result = 0; - if (!_menuMap.contains(id)) + if (!d->m_menuMap.contains(id)) nlwarning("QMenu %s not found", id.toStdString().c_str()); else - result = _menuMap.value(id); + result = d->m_menuMap.value(id); return result; } QAction *MenuManager::action(const QString &id) const { QAction *result = 0; - if (!_actionMap.contains(id)) + if (!d->m_actionMap.contains(id)) nlwarning("QAction %s not found", id.toStdString().c_str()); else - result = _actionMap.value(id); + result = d->m_actionMap.value(id); return result; } void MenuManager::unregisterMenu(const QString &id) { - _menuMap.remove(id); + d->m_menuMap.remove(id); } void MenuManager::unregisterAction(const QString &id) { - _actionMap.remove(id); + d->m_actionMap.remove(id); } QMenuBar *MenuManager::menuBar() const { - return _menuBar; -} - -void MenuManager::setMenuBar(QMenuBar *menuBar) -{ - _menuBar = menuBar; + return d->m_menuBar; } } /* namespace Core */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h index aab603355..3a5e2c710 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h @@ -19,39 +19,47 @@ #define MENU_MANAGER_H // Project includes -#include "imenu_manager.h" +#include "core_global.h" // Qt includes #include +#include +#include +#include +#include +#include namespace Core { +struct MenuManagerPrivate; -class MenuManager : public IMenuManager +/* +@interface MenuManager +@brief The MenuManager provide the interface for registration of menus and menu item. +@details The MenuManager provides centralized access to menus and menu items. +All menus and menu items should be registered in the MenuManager. +*/ +class CORE_EXPORT MenuManager: public QObject { Q_OBJECT public: - MenuManager(QObject *parent = 0); + explicit MenuManager(QMenuBar *menuBar, QObject *parent = 0); virtual ~MenuManager(); - virtual void registerMenu(QMenu *menu, const QString &id); - virtual void registerAction(QAction *action, const QString &id); + void registerMenu(QMenu *menu, const QString &id); + void registerAction(QAction *action, const QString &id); - virtual QMenu *menu(const QString &id) const; - virtual QAction *action(const QString &id) const; + QMenu *menu(const QString &id) const; + QAction *action(const QString &id) const; - virtual void unregisterMenu(const QString &id); - virtual void unregisterAction(const QString &id); + void unregisterMenu(const QString &id); + void unregisterAction(const QString &id); - virtual QMenuBar *menuBar() const; - void setMenuBar(QMenuBar *menuBar); + QMenuBar *menuBar() const; private: - QMenuBar *_menuBar; - typedef QHash IdMenuMap; - IdMenuMap _menuMap; - typedef QHash IdActionMap; - IdActionMap _actionMap; + + MenuManagerPrivate *d; }; } // namespace Core diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/ovqt_plugin_core.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/ovqt_plugin_core.xml new file mode 100644 index 000000000..c0d38aa75 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/ovqt_plugin_core.xml @@ -0,0 +1,7 @@ + + ovqt_plugin_core + Core + 0.8 + Ryzom Core + Core plugin. + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.ui index 60a9b9c46..9d7d395be 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/plugin_view_dialog.ui @@ -112,7 +112,7 @@
- + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/qtwin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/qtwin.cpp index dc0fb6cf0..2ebb69134 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/qtwin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/qtwin.cpp @@ -40,9 +40,9 @@ typedef struct _MARGINS int cyBottomHeight; } MARGINS, *PMARGINS; -typedef HRESULT (WINAPI *PtrDwmIsCompositionEnabled)(BOOL* pfEnabled); -typedef HRESULT (WINAPI *PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset); -typedef HRESULT (WINAPI *PtrDwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind); +typedef HRESULT (WINAPI *PtrDwmIsCompositionEnabled)(BOOL *pfEnabled); +typedef HRESULT (WINAPI *PtrDwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS *pMarInset); +typedef HRESULT (WINAPI *PtrDwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND *pBlurBehind); typedef HRESULT (WINAPI *PtrDwmGetColorizationColor)(DWORD *pcrColorization, BOOL *pfOpaqueBlend); static PtrDwmIsCompositionEnabled pDwmIsCompositionEnabled= 0; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.cpp index 355a9c0e3..7fca0f252 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.cpp @@ -33,18 +33,18 @@ namespace Core QString lastDir = "."; -CSearchPathsSettingsPage::CSearchPathsSettingsPage(bool recurse, QObject *parent) +SearchPathsSettingsPage::SearchPathsSettingsPage(bool recurse, QObject *parent) : IOptionsPage(parent), m_recurse(recurse), m_page(0) { } -CSearchPathsSettingsPage::~CSearchPathsSettingsPage() +SearchPathsSettingsPage::~SearchPathsSettingsPage() { } -QString CSearchPathsSettingsPage::id() const +QString SearchPathsSettingsPage::id() const { if (m_recurse) return QLatin1String("search_recurse_paths"); @@ -52,7 +52,7 @@ QString CSearchPathsSettingsPage::id() const return QLatin1String("search_paths"); } -QString CSearchPathsSettingsPage::trName() const +QString SearchPathsSettingsPage::trName() const { if (m_recurse) return tr("Search Recurse Paths"); @@ -60,22 +60,22 @@ QString CSearchPathsSettingsPage::trName() const return tr("Search Paths"); } -QString CSearchPathsSettingsPage::category() const +QString SearchPathsSettingsPage::category() const { return QLatin1String(Constants::SETTINGS_CATEGORY_GENERAL); } -QString CSearchPathsSettingsPage::trCategory() const +QString SearchPathsSettingsPage::trCategory() const { return tr(Constants::SETTINGS_TR_CATEGORY_GENERAL); } -QIcon CSearchPathsSettingsPage::categoryIcon() const +QIcon SearchPathsSettingsPage::categoryIcon() const { return QIcon(); } -QWidget *CSearchPathsSettingsPage::createPage(QWidget *parent) +QWidget *SearchPathsSettingsPage::createPage(QWidget *parent) { m_page = new QWidget(parent); m_ui.setupUi(m_page); @@ -90,19 +90,19 @@ QWidget *CSearchPathsSettingsPage::createPage(QWidget *parent) return m_page; } -void CSearchPathsSettingsPage::apply() +void SearchPathsSettingsPage::apply() { writeSettings(); applySearchPaths(); } -void CSearchPathsSettingsPage::finish() +void SearchPathsSettingsPage::finish() { delete m_page; m_page = 0; } -void CSearchPathsSettingsPage::applySearchPaths() +void SearchPathsSettingsPage::applySearchPaths() { QStringList paths, remapExt; QSettings *settings = Core::ICore::instance()->settings(); @@ -124,7 +124,7 @@ void CSearchPathsSettingsPage::applySearchPaths() } } -void CSearchPathsSettingsPage::addPath() +void SearchPathsSettingsPage::addPath() { QString newPath = QFileDialog::getExistingDirectory(m_page, "", lastDir); if (!newPath.isEmpty()) @@ -139,7 +139,7 @@ void CSearchPathsSettingsPage::addPath() checkEnabledButton(); } -void CSearchPathsSettingsPage::delPath() +void SearchPathsSettingsPage::delPath() { QListWidgetItem *removeItem = m_ui.pathsListWidget->takeItem(m_ui.pathsListWidget->currentRow()); if (!removeItem) @@ -148,7 +148,7 @@ void CSearchPathsSettingsPage::delPath() checkEnabledButton(); } -void CSearchPathsSettingsPage::upPath() +void SearchPathsSettingsPage::upPath() { int currentRow = m_ui.pathsListWidget->currentRow(); if (!(currentRow == 0)) @@ -159,7 +159,7 @@ void CSearchPathsSettingsPage::upPath() } } -void CSearchPathsSettingsPage::downPath() +void SearchPathsSettingsPage::downPath() { int currentRow = m_ui.pathsListWidget->currentRow(); if (!(currentRow == m_ui.pathsListWidget->count()-1)) @@ -170,7 +170,7 @@ void CSearchPathsSettingsPage::downPath() } } -void CSearchPathsSettingsPage::readSettings() +void SearchPathsSettingsPage::readSettings() { QStringList paths; QSettings *settings = Core::ICore::instance()->settings(); @@ -189,7 +189,7 @@ void CSearchPathsSettingsPage::readSettings() } } -void CSearchPathsSettingsPage::writeSettings() +void SearchPathsSettingsPage::writeSettings() { QStringList paths; for (int i = 0; i < m_ui.pathsListWidget->count(); ++i) @@ -205,7 +205,7 @@ void CSearchPathsSettingsPage::writeSettings() settings->sync(); } -void CSearchPathsSettingsPage::checkEnabledButton() +void SearchPathsSettingsPage::checkEnabledButton() { bool bEnabled = true; if (m_ui.pathsListWidget->count() == 0) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.h index c7d4c9734..c45b29571 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.h @@ -30,15 +30,15 @@ class QWidget; namespace Core { /** -@class CSearchPathsSettingsPage +@class SearchPathsSettingsPage */ -class CSearchPathsSettingsPage : public Core::IOptionsPage +class SearchPathsSettingsPage : public Core::IOptionsPage { Q_OBJECT public: - explicit CSearchPathsSettingsPage(bool recurse, QObject *parent = 0); - ~CSearchPathsSettingsPage(); + explicit SearchPathsSettingsPage(bool recurse, QObject *parent = 0); + ~SearchPathsSettingsPage(); QString id() const; QString trName() const; @@ -66,7 +66,7 @@ private: bool m_recurse; QWidget *m_page; - Ui::CSearchPathsSettingsPage m_ui; + Ui::SearchPathsSettingsPage m_ui; }; } // namespace Core diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.ui index 79a6b2141..cf47314b8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/search_paths_settings_page.ui @@ -1,7 +1,7 @@ - CSearchPathsSettingsPage - + SearchPathsSettingsPage + 0 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.cpp index 30fd7835e..960644599 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.cpp @@ -35,33 +35,33 @@ Q_DECLARE_METATYPE(PageData); namespace Core { -CSettingsDialog::CSettingsDialog(ExtensionSystem::IPluginManager *pluginManager, - const QString &categoryId, - const QString &pageId, - QWidget *parent) +SettingsDialog::SettingsDialog(ExtensionSystem::IPluginManager *pluginManager, + const QString &categoryId, + const QString &pageId, + QWidget *parent) : QDialog(parent), - _applied(false) + m_applied(false) { - _ui.setupUi(this); + m_ui.setupUi(this); - _plugMan = pluginManager; + m_plugMan = pluginManager; QString initialCategory = categoryId; QString initialPage = pageId; - _ui.buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + m_ui.buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); - connect(_ui.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); + connect(m_ui.buttonBox->button(QDialogButtonBox::Apply), SIGNAL(clicked()), this, SLOT(apply())); - _ui.splitter->setCollapsible(1, false); - _ui.pageTree->header()->setVisible(false); + m_ui.splitter->setCollapsible(1, false); + m_ui.pageTree->header()->setVisible(false); - connect(_ui.pageTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), + connect(m_ui.pageTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)), this, SLOT(pageSelected())); QMap categories; - QList pages = _plugMan->getObjects(); + QList pages = m_plugMan->getObjects(); int index = 0; Q_FOREACH(IOptionsPage *page, pages) @@ -82,7 +82,7 @@ CSettingsDialog::CSettingsDialog(ExtensionSystem::IPluginManager *pluginManager, QTreeWidgetItem *treeitem; if (!categories.contains(currentCategory)) { - treeitem = new QTreeWidgetItem(_ui.pageTree); + treeitem = new QTreeWidgetItem(m_ui.pageTree); treeitem->setText(0, trCategories.at(0)); treeitem->setData(0, Qt::UserRole, qVariantFromValue(pageData)); categories.insert(currentCategory, treeitem); @@ -108,13 +108,13 @@ CSettingsDialog::CSettingsDialog(ExtensionSystem::IPluginManager *pluginManager, categories.value(currentCategory)->addChild(item); - _pages.append(page); - _ui.stackedPages->addWidget(page->createPage(_ui.stackedPages)); + m_pages.append(page); + m_ui.stackedPages->addWidget(page->createPage(m_ui.stackedPages)); if (page->id() == initialPage && currentCategory == initialCategory) { - _ui.stackedPages->setCurrentIndex(_ui.stackedPages->count()); - _ui.pageTree->setCurrentItem(item); + m_ui.stackedPages->setCurrentIndex(m_ui.stackedPages->count()); + m_ui.pageTree->setCurrentItem(item); } index++; @@ -122,30 +122,30 @@ CSettingsDialog::CSettingsDialog(ExtensionSystem::IPluginManager *pluginManager, QList sizes; sizes << 150 << 300; - _ui.splitter->setSizes(sizes); + m_ui.splitter->setSizes(sizes); - _ui.splitter->setStretchFactor(_ui.splitter->indexOf(_ui.pageTree), 0); - _ui.splitter->setStretchFactor(_ui.splitter->indexOf(_ui.layoutWidget), 1); + m_ui.splitter->setStretchFactor(m_ui.splitter->indexOf(m_ui.pageTree), 0); + m_ui.splitter->setStretchFactor(m_ui.splitter->indexOf(m_ui.layoutWidget), 1); } -CSettingsDialog::~CSettingsDialog() +SettingsDialog::~SettingsDialog() { } -void CSettingsDialog::pageSelected() +void SettingsDialog::pageSelected() { - QTreeWidgetItem *item = _ui.pageTree->currentItem(); + QTreeWidgetItem *item = m_ui.pageTree->currentItem(); PageData data = item->data(0, Qt::UserRole).value(); int index = data.index; - _currentCategory = data.category; - _currentPage = data.id; - _ui.stackedPages->setCurrentIndex(index); + m_currentCategory = data.category; + m_currentPage = data.id; + m_ui.stackedPages->setCurrentIndex(index); } -void CSettingsDialog::accept() +void SettingsDialog::accept() { - _applied = true; - Q_FOREACH(IOptionsPage *page, _pages) + m_applied = true; + Q_FOREACH(IOptionsPage *page, m_pages) { page->apply(); page->finish(); @@ -153,28 +153,28 @@ void CSettingsDialog::accept() done(QDialog::Accepted); } -void CSettingsDialog::reject() +void SettingsDialog::reject() { - Q_FOREACH(IOptionsPage *page, _pages) + Q_FOREACH(IOptionsPage *page, m_pages) page->finish(); done(QDialog::Rejected); } -void CSettingsDialog::apply() +void SettingsDialog::apply() { - Q_FOREACH(IOptionsPage *page, _pages) + Q_FOREACH(IOptionsPage *page, m_pages) page->apply(); - _applied = true; + m_applied = true; } -bool CSettingsDialog::execDialog() +bool SettingsDialog::execDialog() { - _applied = false; + m_applied = false; exec(); - return _applied; + return m_applied; } -void CSettingsDialog::done(int val) +void SettingsDialog::done(int val) { QDialog::done(val); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.h index 07adc7fee..9e1c86444 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.h @@ -35,17 +35,17 @@ class IOptionsPage; @class CSettingsDialog @brief Settings dialog */ -class CSettingsDialog: public QDialog +class SettingsDialog: public QDialog { Q_OBJECT public: - CSettingsDialog(ExtensionSystem::IPluginManager *pluginManager, - const QString &initialCategory = QString(), - const QString &initialPage = QString(), - QWidget *parent = 0); + SettingsDialog(ExtensionSystem::IPluginManager *pluginManager, + const QString &initialCategory = QString(), + const QString &initialPage = QString(), + QWidget *parent = 0); - ~CSettingsDialog(); + ~SettingsDialog(); /// Run the dialog and return true if 'Ok' was choosen or 'Apply' was invoked at least once bool execDialog(); @@ -60,14 +60,14 @@ private Q_SLOTS: void apply(); private: - QList _pages; - bool _applied; - QString _currentCategory; - QString _currentPage; + QList m_pages; + bool m_applied; + QString m_currentCategory; + QString m_currentPage; - ExtensionSystem::IPluginManager *_plugMan; + ExtensionSystem::IPluginManager *m_plugMan; - Ui::CSettingsDialog _ui; + Ui::SettingsDialog m_ui; }; /* class CSettingsDialog */ } /* namespace Core */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.ui index e536f0dc2..8e9780c9a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/settings_dialog.ui @@ -1,7 +1,7 @@ - CSettingsDialog - + SettingsDialog + 0 @@ -93,7 +93,7 @@ buttonBox accepted() - CSettingsDialog + SettingsDialog accept() @@ -109,7 +109,7 @@ buttonBox rejected() - CSettingsDialog + SettingsDialog reject() From 7471561ba07620900f64a3a53024fc7d91ae4957 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Mon, 26 Sep 2011 01:36:02 +0300 Subject: [PATCH 121/215] Changed: #1193 Updated object viewer plugin with latest API changes to OVQT plugin system. Added the plugin-spec file. --- .../src/plugins/object_viewer/main_window.cpp | 4 +- .../object_viewer/object_viewer_constants.h | 124 +++++++++--------- .../object_viewer/object_viewer_plugin.cpp | 27 ---- .../object_viewer/object_viewer_plugin.h | 7 - .../ovqt_plugin_object_viewer.xml | 10 ++ .../object_viewer/particle_force_page.h | 2 +- .../src/plugins/object_viewer/particle_node.h | 2 +- .../src/plugins/object_viewer/ps_mover_page.h | 2 +- .../plugins/object_viewer/sound_system.cpp | 2 +- 9 files changed, 78 insertions(+), 102 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/ovqt_plugin_object_viewer.xml diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/main_window.cpp index e1962cf5b..a65f510c4 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/main_window.cpp @@ -52,7 +52,7 @@ #include "object_viewer_constants.h" #include "../core/icore.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../core/core_constants.h" using namespace std; @@ -259,7 +259,7 @@ void CMainWindow::createActions() void CMainWindow::createMenus() { - Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); + Core::MenuManager *menuManager = Core::ICore::instance()->menuManager(); _openAction = menuManager->action(Core::Constants::OPEN); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_constants.h index 30a9b4c45..deb0c84ce 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_constants.h @@ -22,76 +22,76 @@ namespace NLQT { namespace Constants { -const char * const OBJECT_VIEWER_PLUGIN = "ObjectViewer"; +const char *const OBJECT_VIEWER_PLUGIN = "ObjectViewer"; //mainwindow -const char * const MAIN_WINDOW = "ObjectViewer.MainWindow"; +const char *const MAIN_WINDOW = "ObjectViewer.MainWindow"; //settings -const char * const OBJECT_VIEWER_SECTION = "ObjectViewer"; -const char * const GRAPHICS_DRIVER = "GraphicsDriver"; -const char * const ENABLE_BLOOM = "EnableBloom"; -const char * const ENABLE_SQUARE_BLOOM = "EnableSquareBloom"; -const char * const BLOOM_DENSITY = "BloomDensity"; -const char * const QT_STYLE = "QtStyle"; -const char * const QT_PALETTE = "QtPalette"; -const char * const FONT = "Font"; +const char *const OBJECT_VIEWER_SECTION = "ObjectViewer"; +const char *const GRAPHICS_DRIVER = "GraphicsDriver"; +const char *const ENABLE_BLOOM = "EnableBloom"; +const char *const ENABLE_SQUARE_BLOOM = "EnableSquareBloom"; +const char *const BLOOM_DENSITY = "BloomDensity"; +const char *const QT_STYLE = "QtStyle"; +const char *const QT_PALETTE = "QtPalette"; +const char *const FONT = "Font"; -const char * const SOUND_ENABLE = "SoundEnable"; -const char * const SOUND_DRIVER = "SoundDriver"; -const char * const SOUND_DEVICE = "SoundDevice"; -const char * const SOUND_AUTO_LOAD_SAMPLE = "SoundAutoLoadSample"; -const char * const SOUND_ENABLE_OCCLUDE_OBSTRUCT = "SoundEnableOccludeObstruct"; -const char * const SOUND_ENABLE_REVERB = "SoundEnableReverb"; -const char * const SOUND_MANUAL_ROLL_OFF = "SoundManualRolloff"; -const char * const SOUND_FORCE_SOFTWARE = "SoundForceSoftware"; -const char * const SOUND_USE_ADCPM = "SoundUseADPCM"; -const char * const SOUND_MAX_TRACK = "SoundMaxTrack"; -const char * const SOUND_PACKED_SHEET_PATH = "SoundPackedSheetPath"; -const char * const SOUND_SAMPLE_PATH = "SoundSamplePath"; +const char *const SOUND_ENABLE = "SoundEnable"; +const char *const SOUND_DRIVER = "SoundDriver"; +const char *const SOUND_DEVICE = "SoundDevice"; +const char *const SOUND_AUTO_LOAD_SAMPLE = "SoundAutoLoadSample"; +const char *const SOUND_ENABLE_OCCLUDE_OBSTRUCT = "SoundEnableOccludeObstruct"; +const char *const SOUND_ENABLE_REVERB = "SoundEnableReverb"; +const char *const SOUND_MANUAL_ROLL_OFF = "SoundManualRolloff"; +const char *const SOUND_FORCE_SOFTWARE = "SoundForceSoftware"; +const char *const SOUND_USE_ADCPM = "SoundUseADPCM"; +const char *const SOUND_MAX_TRACK = "SoundMaxTrack"; +const char *const SOUND_PACKED_SHEET_PATH = "SoundPackedSheetPath"; +const char *const SOUND_SAMPLE_PATH = "SoundSamplePath"; -const char * const VEGET_TILE_BANK = "VegetTileBank"; -const char * const VEGET_TILE_FAR_BANK = "VegetTileFarBank"; -const char * const VEGET_TEXTURE = "VegetTexture"; -const char * const VEGET_LANDSCAPE_ZONES = "VegetLandscapeZones"; -const char * const COARSE_MESH_TEXTURE = "CoarseMeshTexture"; +const char *const VEGET_TILE_BANK = "VegetTileBank"; +const char *const VEGET_TILE_FAR_BANK = "VegetTileFarBank"; +const char *const VEGET_TEXTURE = "VegetTexture"; +const char *const VEGET_LANDSCAPE_ZONES = "VegetLandscapeZones"; +const char *const COARSE_MESH_TEXTURE = "CoarseMeshTexture"; -const char * const ICON_ADD_ITEM = ":/icons/ic_nel_add_item.png"; -const char * const ICON_INSERT_ITEM = ":/icons/ic_nel_insert_item.png"; -const char * const ICON_DELETE_ITEM = ":/icons/ic_nel_delete_item.png"; -const char * const ICON_DOWN_ITEM = ":/icons/ic_nel_down_item.png"; -const char * const ICON_UP_ITEM = ":/icons/ic_nel_up_item.png"; -const char * const ICON_CAMERA_ADD = ":/icons/ic_nel_camera_add.png"; -const char * const ICON_CAMERA_DEL = ":/icons/ic_nel_camera_del.png"; -const char * const ICON_CAMERA_3DEDIT = ":/icons/ic_nel_camera_3dedit.png"; -const char * const ICON_CAMERA_FPS = ":/icons/ic_nel_camera_fps.png"; -const char * const ICON_RESET_CAMERA = ":/icons/ic_nel_reset_camera.png"; -const char * const ICON_ANIM = ":/icons/ic_nel_anim.png"; -const char * const ICON_ANIMSET = ":/icons/ic_nel_animset.png"; -const char * const ICON_BGCOLOR = ":/icons/ic_nel_bgcolor.png"; -const char * const ICON_DAYNIGHT = ":/icons/ic_nel_daynight.png"; -const char * const ICON_FRAMEDELAY = ":/icons/ic_nel_framedelay.png"; -const char * const ICON_MIXER = ":/icons/ic_nel_mixer.png"; -const char * const ICON_MRM_MESH = ":/icons/ic_nel_mrm_mesh.png"; -const char * const ICON_PARTICLES = ":/icons/ic_nel_particles.png"; -const char * const ICON_SKELSCALE = ":/icons/ic_nel_skelscale.png"; -const char * const ICON_VEGET = ":/icons/ic_nel_veget.png"; -const char * const ICON_VEGETSET = ":/icons/ic_nel_vegetset.png"; -const char * const ICON_WATER = ":/icons/ic_nel_water.png"; -const char * const ICON_WIND = ":/icons/ic_nel_wind.png"; +const char *const ICON_ADD_ITEM = ":/icons/ic_nel_add_item.png"; +const char *const ICON_INSERT_ITEM = ":/icons/ic_nel_insert_item.png"; +const char *const ICON_DELETE_ITEM = ":/icons/ic_nel_delete_item.png"; +const char *const ICON_DOWN_ITEM = ":/icons/ic_nel_down_item.png"; +const char *const ICON_UP_ITEM = ":/icons/ic_nel_up_item.png"; +const char *const ICON_CAMERA_ADD = ":/icons/ic_nel_camera_add.png"; +const char *const ICON_CAMERA_DEL = ":/icons/ic_nel_camera_del.png"; +const char *const ICON_CAMERA_3DEDIT = ":/icons/ic_nel_camera_3dedit.png"; +const char *const ICON_CAMERA_FPS = ":/icons/ic_nel_camera_fps.png"; +const char *const ICON_RESET_CAMERA = ":/icons/ic_nel_reset_camera.png"; +const char *const ICON_ANIM = ":/icons/ic_nel_anim.png"; +const char *const ICON_ANIMSET = ":/icons/ic_nel_animset.png"; +const char *const ICON_BGCOLOR = ":/icons/ic_nel_bgcolor.png"; +const char *const ICON_DAYNIGHT = ":/icons/ic_nel_daynight.png"; +const char *const ICON_FRAMEDELAY = ":/icons/ic_nel_framedelay.png"; +const char *const ICON_MIXER = ":/icons/ic_nel_mixer.png"; +const char *const ICON_MRM_MESH = ":/icons/ic_nel_mrm_mesh.png"; +const char *const ICON_PARTICLES = ":/icons/ic_nel_particles.png"; +const char *const ICON_SKELSCALE = ":/icons/ic_nel_skelscale.png"; +const char *const ICON_VEGET = ":/icons/ic_nel_veget.png"; +const char *const ICON_VEGETSET = ":/icons/ic_nel_vegetset.png"; +const char *const ICON_WATER = ":/icons/ic_nel_water.png"; +const char *const ICON_WIND = ":/icons/ic_nel_wind.png"; -const char * const ICON_COLLISION_ZONE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_collision_zone_item_24.png"; -const char * const ICON_EMITTER_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_emitter_item_24.png"; -const char * const ICON_FORCE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_force_item_24.png"; -const char * const ICON_INSTANCE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_instance_item_24.png"; -const char * const ICON_LIGHT_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_light_item_24.png"; -const char * const ICON_LOCATED_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_located_item_24.png"; -const char * const ICON_PARTICLE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_particle_item_24.png"; -const char * const ICON_PARTICLE_SYSTEM_SMALL = ":/icons/particles_system_24/ic_nel_particle_system_24.png"; -const char * const ICON_PARTICLE_SYSTEM_CLOSE_SMALL = ":/icons/particles_system_24/ic_nel_particle_system_close_24.png"; -const char * const ICON_PARTICLES_SMALL = ":/icons/particles_system_24/ic_nel_particles_24.png"; -const char * const ICON_SOUND_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_sound_item_24.png"; -const char * const ICON_WORKSPACE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_workspace_item_24.png"; +const char *const ICON_COLLISION_ZONE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_collision_zone_item_24.png"; +const char *const ICON_EMITTER_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_emitter_item_24.png"; +const char *const ICON_FORCE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_force_item_24.png"; +const char *const ICON_INSTANCE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_instance_item_24.png"; +const char *const ICON_LIGHT_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_light_item_24.png"; +const char *const ICON_LOCATED_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_located_item_24.png"; +const char *const ICON_PARTICLE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_particle_item_24.png"; +const char *const ICON_PARTICLE_SYSTEM_SMALL = ":/icons/particles_system_24/ic_nel_particle_system_24.png"; +const char *const ICON_PARTICLE_SYSTEM_CLOSE_SMALL = ":/icons/particles_system_24/ic_nel_particle_system_close_24.png"; +const char *const ICON_PARTICLES_SMALL = ":/icons/particles_system_24/ic_nel_particles_24.png"; +const char *const ICON_SOUND_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_sound_item_24.png"; +const char *const ICON_WORKSPACE_ITEM_SMALL = ":/icons/particles_system_24/ic_nel_workspace_item_24.png"; } // namespace Constants } // namespace NLQT diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.cpp index f82789e66..ec21cef33 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.cpp @@ -56,33 +56,6 @@ void ObjectViewerPlugin::setNelContext(NLMISC::INelContext *nelContext) _LibContext = new NLMISC::CLibraryContext(*nelContext); } -QString ObjectViewerPlugin::name() const -{ - return "ObjectViewer"; -} - -QString ObjectViewerPlugin::version() const -{ - return "0.8"; -} - -QString ObjectViewerPlugin::vendor() const -{ - return Core::Constants::OVQT_VENDOR; -} - -QString ObjectViewerPlugin::description() const -{ - return "Object Viewer plugin."; -} - -QStringList ObjectViewerPlugin::dependencies() const -{ - QStringList list; - list.append(Core::Constants::OVQT_CORE_PLUGIN); - return list; -} - void ObjectViewerPlugin::addAutoReleasedObject(QObject *obj) { _plugMan->addObject(obj); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.h index f0f23d398..5018501bf 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/object_viewer_plugin.h @@ -36,15 +36,8 @@ public: bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); void extensionsInitialized(); void shutdown(); - void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - void addAutoReleasedObject(QObject *obj); protected: diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/ovqt_plugin_object_viewer.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/ovqt_plugin_object_viewer.xml new file mode 100644 index 000000000..cc951cdcb --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/ovqt_plugin_object_viewer.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_object_viewer + ObjectViewer + 0.8 + Ryzom Core + Object Viewer plugin. + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_force_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_force_page.h index 1bc23224f..1518bdd63 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_force_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_force_page.h @@ -43,7 +43,7 @@ namespace NLQT class CLocatedItem: public QListWidgetItem { public: - CLocatedItem ( const QString & text, QListWidget *parent = 0, int type = UserType ): + CLocatedItem ( const QString &text, QListWidget *parent = 0, int type = UserType ): QListWidgetItem(text, parent, type), _loc(NULL) {} void setUserData(NL3D::CPSLocated *loc) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_node.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_node.h index acf7dfcd6..e9da65b5c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_node.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_node.h @@ -332,7 +332,7 @@ public: /// Restick all objects, useful after loading void restickAllObjects(); - TNodeVect& getNodeList() + TNodeVect &getNodeList() { return _Nodes; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/ps_mover_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/ps_mover_page.h index a548b789a..74caadc5c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/ps_mover_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/ps_mover_page.h @@ -44,7 +44,7 @@ namespace NLQT class CLocatedBindableItem: public QListWidgetItem { public: - CLocatedBindableItem ( const QString & text, QListWidget * parent = 0, int type = UserType ): + CLocatedBindableItem ( const QString &text, QListWidget *parent = 0, int type = UserType ): QListWidgetItem(text, parent, type), _lb(NULL) {} void setUserData(NL3D::CPSLocatedBindable *loc) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.cpp index 07101aa85..a8a204c96 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/sound_system.cpp @@ -190,7 +190,7 @@ NLSOUND::USource *CSoundSystem::create(const std::string &soundName) return NULL; } -void CSoundSystem::playAnimation(std::string& name, float lastTime, float curTime, NLSOUND::CSoundContext &context) +void CSoundSystem::playAnimation(std::string &name, float lastTime, float curTime, NLSOUND::CSoundContext &context) { if (_AnimManager == NULL) { From 5f8789e98fa739951851d654f42fa5d1702bf2ff Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Mon, 26 Sep 2011 02:32:14 +0300 Subject: [PATCH 122/215] Changed: #1193 Updated other plugins with latest API changes to OVQT plugin system. Added the plugin-spec files for each plugin. --- .../src/extension_system/plugin_spec.cpp | 2 +- .../src/plugins/disp_sheet_id/CMakeLists.txt | 2 +- .../disp_sheet_id/disp_sheet_id_plugin.cpp | 35 +---- .../disp_sheet_id/disp_sheet_id_plugin.h | 16 +-- .../ovqt_plugin_disp_sheet_id.xml | 10 ++ .../plugins/disp_sheet_id/sheet_id_view.cpp | 4 +- .../src/plugins/example/CMakeLists.txt | 8 +- .../src/plugins/example/example_plugin.cpp | 83 ++++++++++++ .../example/{plugin1.h => example_plugin.h} | 37 ++---- .../plugins/example/example_settings_page.cpp | 28 ++-- .../plugins/example/example_settings_page.h | 14 +- .../plugins/example/example_settings_page.ui | 4 +- .../plugins/example/ovqt_plugin_example.xml | 10 ++ .../src/plugins/example/plugin1.cpp | 120 ------------------ .../src/plugins/example/qnel_widget.h | 2 +- .../src/plugins/example/simple_viewer.cpp | 8 +- .../src/plugins/example/simple_viewer.h | 12 +- .../georges_editor/georges_editor_form.cpp | 4 +- .../georges_editor/georges_editor_plugin.cpp | 29 ----- .../georges_editor/georges_editor_plugin.h | 7 - .../ovqt_plugin_georges_editor.xml | 10 ++ .../src/plugins/log/ovqt_plugin_log.xml | 10 ++ .../mission_compiler_main_window.cpp | 2 +- .../mission_compiler_plugin.cpp | 75 ++--------- .../mission_compiler_plugin.h | 16 +-- .../ovqt_plugin_mission_compiler.xml | 10 ++ .../ovqt_plugin_sheet_builder.xml | 10 ++ .../ovqt_sheet_builder/ovqt_sheet_builder.cpp | 35 +---- .../ovqt_sheet_builder/ovqt_sheet_builder.h | 16 +-- .../plugins/ovqt_sheet_builder/sheetbuilder.h | 8 +- .../ovqt_sheet_builder/sheetbuilderdialog.cpp | 2 +- .../zone_painter/ovqt_plugin_zone_painter.xml | 10 ++ .../zone_painter/zone_painter_main_window.cpp | 3 +- .../zone_painter/zone_painter_plugin.cpp | 78 ++---------- .../zone_painter/zone_painter_plugin.h | 22 +--- 35 files changed, 256 insertions(+), 486 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/ovqt_plugin_disp_sheet_id.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_plugin.cpp rename code/nel/tools/3d/object_viewer_qt/src/plugins/example/{plugin1.h => example_plugin.h} (59%) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/example/ovqt_plugin_example.xml delete mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/example/plugin1.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/ovqt_plugin_georges_editor.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/log/ovqt_plugin_log.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/ovqt_plugin_mission_compiler.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_plugin_sheet_builder.xml create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/ovqt_plugin_zone_painter.xml diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp index 030f9507b..5c4e04ecb 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp @@ -223,7 +223,7 @@ void PluginSpec::parseDependency(QXmlStreamReader &reader) .arg(PLUGIN_SPEC_DEPENDENCY_NAME)); return; } - // Read version dependency plugin + // TODO: Read version dependency plugin QString dependencyVersion = reader.attributes().value(PLUGIN_SPEC_DEPENDENCY_VERSION).toString(); m_dependencies.push_back(dependencyName); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/CMakeLists.txt index 34c65c178..6152b8fdd 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/CMakeLists.txt @@ -28,7 +28,7 @@ SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) ADD_LIBRARY(ovqt_plugin_disp_sheet_id MODULE ${SRC} ${OVQT_DISP_SHEET_ID_PLUGIN_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_DISP_SHEET_ID_PLUGIN_UI_HDRS}) -TARGET_LINK_LIBRARIES(ovqt_plugin_disp_sheet_id ovqt_plugin_core nelmisc nel3d ${QT_LIBRARIES}) +TARGET_LINK_LIBRARIES(ovqt_plugin_disp_sheet_id ovqt_plugin_core nelmisc ${QT_LIBRARIES}) IF(WITH_STLPORT) TARGET_LINK_LIBRARIES(ovqt_plugin_disp_sheet_id ${CMAKE_THREAD_LIBS_INIT}) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/disp_sheet_id_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/disp_sheet_id_plugin.cpp index 54cba53a6..9e37b803b 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/disp_sheet_id_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/disp_sheet_id_plugin.cpp @@ -18,7 +18,7 @@ #include "disp_sheet_id_plugin.h" #include "sheet_id_view.h" #include "../core/icore.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../core/core_constants.h" // Qt includes @@ -37,13 +37,13 @@ using namespace SheetIdViewPlugin; bool DispSheetIdPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) { Q_UNUSED(errorString); - _plugMan = pluginManager; + m_plugMan = pluginManager; return true; } void DispSheetIdPlugin::extensionsInitialized() { - Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); + Core::MenuManager *menuManager = Core::ICore::instance()->menuManager(); QMenu *sheetMenu = menuManager->menu(Core::Constants::M_SHEET); QAction *sheetIdViewAction = sheetMenu->addAction(tr("Sheet id view")); @@ -67,34 +67,7 @@ void DispSheetIdPlugin::setNelContext(NLMISC::INelContext *nelContext) // This only applies to platforms without PIC, e.g. Windows. nlassert(!NLMISC::INelContext::isContextInitialised()); #endif // NL_OS_WINDOWS - _LibContext = new NLMISC::CLibraryContext(*nelContext); -} - -QString DispSheetIdPlugin::name() const -{ - return "Display sheet id"; -} - -QString DispSheetIdPlugin::version() const -{ - return "1.0"; -} - -QString DispSheetIdPlugin::vendor() const -{ - return "pemeon"; -} - -QString DispSheetIdPlugin::description() const -{ - return "Display sheet id"; -} - -QStringList DispSheetIdPlugin::dependencies() const -{ - QStringList list; - list.append(Core::Constants::OVQT_CORE_PLUGIN); - return list; + m_LibContext = new NLMISC::CLibraryContext(*nelContext); } Q_EXPORT_PLUGIN(DispSheetIdPlugin) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/disp_sheet_id_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/disp_sheet_id_plugin.h index 75a25b724..8eaf3ead9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/disp_sheet_id_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/disp_sheet_id_plugin.h @@ -28,11 +28,6 @@ namespace NLMISC class CLibraryContext; } -namespace NLQT -{ -class IPluginSpec; -} - namespace SheetIdViewPlugin { @@ -44,23 +39,16 @@ public: bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); void extensionsInitialized(); - void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - private Q_SLOTS: void execMessageBox(); protected: - NLMISC::CLibraryContext *_LibContext; + NLMISC::CLibraryContext *m_LibContext; private: - ExtensionSystem::IPluginManager *_plugMan; + ExtensionSystem::IPluginManager *m_plugMan; }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/ovqt_plugin_disp_sheet_id.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/ovqt_plugin_disp_sheet_id.xml new file mode 100644 index 000000000..012029810 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/ovqt_plugin_disp_sheet_id.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_disp_sheet_id + DisplaySheetId + 1.0 + pemeon + Display sheet id. + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/sheet_id_view.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/sheet_id_view.cpp index 64fb05701..f05060a9c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/sheet_id_view.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/disp_sheet_id/sheet_id_view.cpp @@ -65,8 +65,8 @@ void SheetIdView::pushToTable() m_ui.table->setColumnCount(2); for (size_t i = 0; i < m_sheetList.size(); i++) { - QTableWidgetItem* item1 = new QTableWidgetItem(QString(m_sheetList[i].toString().c_str())); - QTableWidgetItem* item2 = new QTableWidgetItem(QString("%1").arg(m_sheetList[i].asInt())); + QTableWidgetItem *item1 = new QTableWidgetItem(QString(m_sheetList[i].toString().c_str())); + QTableWidgetItem *item2 = new QTableWidgetItem(QString("%1").arg(m_sheetList[i].asInt())); m_ui.table->setItem(i,1,item1); m_ui.table->setItem(i,2,item2); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/CMakeLists.txt index 41d22804c..fbe617c43 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/CMakeLists.txt @@ -9,10 +9,10 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin. ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_manager.h ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_spec.h) -SET(OVQT_PLUG_EXAMPLE_HDR plugin1.h - qnel_widget.h - simple_viewer.h - example_settings_page.h) +SET(OVQT_PLUG_EXAMPLE_HDR example_plugin.h + qnel_widget.h + simple_viewer.h + example_settings_page.h) SET(OVQT_PLUG_EXAMPLE_UIS example_settings_page.ui) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_plugin.cpp new file mode 100644 index 000000000..5e442a5ee --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_plugin.cpp @@ -0,0 +1,83 @@ +// Project includes +#include "example_plugin.h" +#include "example_settings_page.h" +#include "simple_viewer.h" + +#include "../core/icore.h" +#include "../core/core_constants.h" +#include "../core/menu_manager.h" + +#include "../../extension_system/iplugin_spec.h" + +// NeL includes +#include "nel/misc/debug.h" + +// Qt includes +#include +#include +#include +#include +#include +#include + +namespace Plugin +{ + +ExamplePlugin::ExamplePlugin() +{ +} + +ExamplePlugin::~ExamplePlugin() +{ + Q_FOREACH(QObject *obj, m_autoReleaseObjects) + { + m_plugMan->removeObject(obj); + } + qDeleteAll(m_autoReleaseObjects); + m_autoReleaseObjects.clear(); +} + +bool ExamplePlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) +{ + Q_UNUSED(errorString); + m_plugMan = pluginManager; + + addAutoReleasedObject(new ExampleSettingsPage(this)); + addAutoReleasedObject(new ExampleContext(this)); + addAutoReleasedObject(new ExampleCoreListener(this)); + return true; +} + +void ExamplePlugin::extensionsInitialized() +{ + Core::ICore *core = Core::ICore::instance(); + Core::MenuManager *menuManager = core->menuManager(); + QAction *exampleAction1 = new QAction("Example1", this); + QAction *exampleAction2 = new QAction("Example2", this); + QAction *aboutQtAction = menuManager->action(Core::Constants::ABOUT_QT); + QMenu *helpMenu = menuManager->menu(Core::Constants::M_HELP); + helpMenu->insertAction(aboutQtAction, exampleAction1); + helpMenu->addSeparator(); + helpMenu->addAction(exampleAction2); + menuManager->menuBar()->addMenu("ExampleMenu"); +} + +void ExamplePlugin::setNelContext(NLMISC::INelContext *nelContext) +{ +#ifdef NL_OS_WINDOWS + // Ensure that a context doesn't exist yet. + // This only applies to platforms without PIC, e.g. Windows. + nlassert(!NLMISC::INelContext::isContextInitialised()); +#endif // NL_OS_WINDOWS + m_LibContext = new NLMISC::CLibraryContext(*nelContext); +} + +void ExamplePlugin::addAutoReleasedObject(QObject *obj) +{ + m_plugMan->addObject(obj); + m_autoReleaseObjects.prepend(obj); +} + +} + +Q_EXPORT_PLUGIN(Plugin::ExamplePlugin) \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/plugin1.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_plugin.h similarity index 59% rename from code/nel/tools/3d/object_viewer_qt/src/plugins/example/plugin1.h rename to code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_plugin.h index 5077ff59e..bfb400651 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/plugin1.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_plugin.h @@ -18,56 +18,41 @@ namespace NLMISC class CLibraryContext; } -namespace ExtensionSystem -{ -class IPluginSpec; -} - namespace Plugin { -class MyPlugin : public QObject, public ExtensionSystem::IPlugin +class ExamplePlugin : public QObject, public ExtensionSystem::IPlugin { Q_OBJECT Q_INTERFACES(ExtensionSystem::IPlugin) public: - - virtual ~MyPlugin(); + ExamplePlugin(); + virtual ~ExamplePlugin(); bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); void extensionsInitialized(); - void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - void addAutoReleasedObject(QObject *obj); - QObject *objectByName(const QString &name) const; - ExtensionSystem::IPluginSpec *pluginByName(const QString &name) const; - protected: - NLMISC::CLibraryContext *_LibContext; + NLMISC::CLibraryContext *m_LibContext; private: - ExtensionSystem::IPluginManager *_plugMan; - QList _autoReleaseObjects; + ExtensionSystem::IPluginManager *m_plugMan; + QList m_autoReleaseObjects; }; -class CExampleContext: public Core::IContext +class ExampleContext: public Core::IContext { Q_OBJECT public: - CExampleContext(QObject *parent = 0): IContext(parent) + ExampleContext(QObject *parent = 0): IContext(parent) { - m_simpleViewer = new CSimpleViewer(); + m_simpleViewer = new SimpleViewer(); } - virtual ~CExampleContext() {} + virtual ~ExampleContext() {} virtual QString id() const { @@ -95,7 +80,7 @@ public: { } - CSimpleViewer *m_simpleViewer; + SimpleViewer *m_simpleViewer; }; } // namespace Plugin diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.cpp index 50e7c9db5..6cea967db 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.cpp @@ -27,45 +27,45 @@ namespace Plugin { -CExampleSettingsPage::CExampleSettingsPage(QObject *parent) +ExampleSettingsPage::ExampleSettingsPage(QObject *parent) : IOptionsPage(parent), - _currentPage(NULL) + m_currentPage(0) { } -QString CExampleSettingsPage::id() const +QString ExampleSettingsPage::id() const { return QLatin1String("ExamplePage"); } -QString CExampleSettingsPage::trName() const +QString ExampleSettingsPage::trName() const { return tr("Example page"); } -QString CExampleSettingsPage::category() const +QString ExampleSettingsPage::category() const { - return QLatin1String("General"); + return QLatin1String("Example"); } -QString CExampleSettingsPage::trCategory() const +QString ExampleSettingsPage::trCategory() const { - return tr("General"); + return tr("Example"); } -QIcon CExampleSettingsPage::categoryIcon() const +QIcon ExampleSettingsPage::categoryIcon() const { return QIcon(); } -QWidget *CExampleSettingsPage::createPage(QWidget *parent) +QWidget *ExampleSettingsPage::createPage(QWidget *parent) { - _currentPage = new QWidget(parent); - _ui.setupUi(_currentPage); - return _currentPage; + m_currentPage = new QWidget(parent); + m_ui.setupUi(m_currentPage); + return m_currentPage; } -void CExampleSettingsPage::apply() +void ExampleSettingsPage::apply() { } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.h index 3475f843f..8a1a7cef1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.h @@ -29,15 +29,13 @@ class QWidget; namespace Plugin { -/** -@class CExampleSettingsPage -*/ -class CExampleSettingsPage : public Core::IOptionsPage + +class ExampleSettingsPage : public Core::IOptionsPage { Q_OBJECT public: - CExampleSettingsPage(QObject *parent = 0); - virtual ~CExampleSettingsPage() {} + ExampleSettingsPage(QObject *parent = 0); + virtual ~ExampleSettingsPage() {} virtual QString id() const; virtual QString trName() const; @@ -50,8 +48,8 @@ public: virtual void finish() {} private: - QWidget *_currentPage; - Ui::CExampleSettingsPage _ui; + QWidget *m_currentPage; + Ui::ExampleSettingsPage m_ui; }; } // namespace Plugin diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.ui index b839d98ce..0d98ad6ab 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/example_settings_page.ui @@ -1,7 +1,7 @@ - CExampleSettingsPage - + ExampleSettingsPage + 0 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/ovqt_plugin_example.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/ovqt_plugin_example.xml new file mode 100644 index 000000000..43656a87b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/ovqt_plugin_example.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_example + ExamplePlugin + 0.2 + dnk-88 + Example ovqt plugin. + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/plugin1.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/plugin1.cpp deleted file mode 100644 index f218c3230..000000000 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/plugin1.cpp +++ /dev/null @@ -1,120 +0,0 @@ -// Project includes -#include "plugin1.h" -#include "example_settings_page.h" -#include "simple_viewer.h" -#include "../core/icore.h" -#include "../core/core_constants.h" -#include "../core/imenu_manager.h" -#include "../../extension_system/iplugin_spec.h" - -// NeL includes -#include "nel/misc/debug.h" - -// Qt includes -#include -#include -#include -#include -#include -#include - -namespace Plugin -{ -MyPlugin::~MyPlugin() -{ - Q_FOREACH(QObject *obj, _autoReleaseObjects) - { - _plugMan->removeObject(obj); - } - qDeleteAll(_autoReleaseObjects); - _autoReleaseObjects.clear(); -} - -bool MyPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) -{ - Q_UNUSED(errorString); - _plugMan = pluginManager; - - addAutoReleasedObject(new CExampleSettingsPage(this)); - addAutoReleasedObject(new CExampleContext(this)); - addAutoReleasedObject(new CCoreListener(this)); - return true; -} - -void MyPlugin::extensionsInitialized() -{ - Core::ICore *core = Core::ICore::instance(); - Core::IMenuManager *menuManager = core->menuManager(); - //menuManager = _plugMan->getObject(); - QAction *exampleAction1 = new QAction("Example1", this); - QAction *exampleAction2 = new QAction("Example2", this); - QAction *aboutQtAction = menuManager->action(Core::Constants::ABOUT_QT); - QMenu *helpMenu = menuManager->menu(Core::Constants::M_HELP); - helpMenu->insertAction(aboutQtAction, exampleAction1); - helpMenu->addSeparator(); - helpMenu->addAction(exampleAction2); - menuManager->menuBar()->addMenu("ExampleMenu"); -} - -void MyPlugin::setNelContext(NLMISC::INelContext *nelContext) -{ -#ifdef NL_OS_WINDOWS - // Ensure that a context doesn't exist yet. - // This only applies to platforms without PIC, e.g. Windows. - nlassert(!NLMISC::INelContext::isContextInitialised()); -#endif // NL_OS_WINDOWS - _LibContext = new NLMISC::CLibraryContext(*nelContext); -} - -QString MyPlugin::name() const -{ - return "ExamplePlugin"; -} - -QString MyPlugin::version() const -{ - return "0.2"; -} - -QString MyPlugin::vendor() const -{ - return "dnk-88"; -} - -QString MyPlugin::description() const -{ - return "Example ovqt plugin."; -} - -QStringList MyPlugin::dependencies() const -{ - QStringList list; - list.append(Core::Constants::OVQT_CORE_PLUGIN); - return list; -} - -void MyPlugin::addAutoReleasedObject(QObject *obj) -{ - _plugMan->addObject(obj); - _autoReleaseObjects.prepend(obj); -} - -QObject* MyPlugin::objectByName(const QString &name) const -{ - Q_FOREACH (QObject *qobj, _plugMan->allObjects()) - if (qobj->objectName() == name) - return qobj; - return 0; -} - -ExtensionSystem::IPluginSpec *MyPlugin::pluginByName(const QString &name) const -{ - Q_FOREACH (ExtensionSystem::IPluginSpec *spec, _plugMan->plugins()) - if (spec->name() == name) - return spec; - return 0; -} - -} - -Q_EXPORT_PLUGIN(Plugin::MyPlugin) \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/qnel_widget.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/qnel_widget.h index a54e6bb8a..59bb9ecda 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/qnel_widget.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/qnel_widget.h @@ -82,7 +82,7 @@ public: return m_driver; } - virtual QPaintEngine* paintEngine() const + virtual QPaintEngine *paintEngine() const { return NULL; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/simple_viewer.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/simple_viewer.cpp index 1f6df9117..601909047 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/simple_viewer.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/simple_viewer.cpp @@ -29,19 +29,19 @@ namespace Plugin { -CSimpleViewer::CSimpleViewer(QWidget *parent) +SimpleViewer::SimpleViewer(QWidget *parent) : QWidget(parent) { QGridLayout *gridLayout = new QGridLayout(this); gridLayout->setObjectName(QString::fromUtf8("gridLayoutSimpleViewer")); gridLayout->setContentsMargins(0, 0, 0, 0); - NLQT::QNLWidget *_nelWidget = new NLQT::QNLWidget(this); - gridLayout->addWidget(_nelWidget, 0, 0, 1, 1); + NLQT::QNLWidget *m_nelWidget = new NLQT::QNLWidget(this); + gridLayout->addWidget(m_nelWidget, 0, 0, 1, 1); m_undoStack = new QUndoStack(this); } -bool CCoreListener::closeMainWindow() const +bool ExampleCoreListener::closeMainWindow() const { int ret = QMessageBox::question(0, tr("Example close event hook"), tr("Do you want to close window?"), diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/simple_viewer.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/simple_viewer.h index 14b782c22..793b0745e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/example/simple_viewer.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/example/simple_viewer.h @@ -31,22 +31,22 @@ class QWidget; namespace Plugin { -class CSimpleViewer : public QWidget +class SimpleViewer : public QWidget { Q_OBJECT public: - CSimpleViewer(QWidget *parent = 0); - virtual ~CSimpleViewer() {} + SimpleViewer(QWidget *parent = 0); + virtual ~SimpleViewer() {} QUndoStack *m_undoStack; }; -class CCoreListener : public Core::ICoreListener +class ExampleCoreListener : public Core::ICoreListener { Q_OBJECT public: - CCoreListener(QObject *parent = 0): ICoreListener(parent) {} - virtual ~CCoreListener() {} + ExampleCoreListener(QObject *parent = 0): ICoreListener(parent) {} + virtual ~ExampleCoreListener() {} virtual bool closeMainWindow() const; }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp index f67be6471..252d7fd7e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_form.cpp @@ -21,7 +21,7 @@ #include "georges_treeview_dialog.h" #include "../core/icore.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../core/core_constants.h" // NeL includes @@ -63,7 +63,7 @@ namespace Plugin m_undoStack = new QUndoStack(this); - Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); + Core::MenuManager *menuManager = Core::ICore::instance()->menuManager(); m_openAction = menuManager->action(Core::Constants::OPEN); m_newAction = new QAction(tr("&New..."), this); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp index 425db7841..199bc20ca 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.cpp @@ -67,35 +67,6 @@ void GeorgesEditorPlugin::setNelContext(NLMISC::INelContext *nelContext) m_libContext = new NLMISC::CLibraryContext(*nelContext); } -QString GeorgesEditorPlugin::name() const -{ - return tr("Georges Editor"); -} - -QString GeorgesEditorPlugin::version() const -{ - return "0.4"; -} - -QString GeorgesEditorPlugin::vendor() const -{ - return "aquiles"; -} - -QString GeorgesEditorPlugin::description() const -{ - return tr("Tool to create & edit sheets or forms."); -} - -QStringList GeorgesEditorPlugin::dependencies() const -{ - QStringList list; - // TODO - //list.append(Core::Constants::OVQT_CORE_PLUGIN); - //list.append("ObjectViewer"); - return list; -} - void GeorgesEditorPlugin::addAutoReleasedObject(QObject *obj) { m_plugMan->addObject(obj); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.h index 7cc93dfb4..fabdd600c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/georges_editor_plugin.h @@ -52,15 +52,8 @@ public: bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); void extensionsInitialized(); void shutdown(); - void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - void addAutoReleasedObject(QObject *obj); protected: diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/ovqt_plugin_georges_editor.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/ovqt_plugin_georges_editor.xml new file mode 100644 index 000000000..2b9b163da --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/georges_editor/ovqt_plugin_georges_editor.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_georges_editor + GeorgesEditor + 0.4 + aquiles + Tool to create and edit sheets or forms. + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/ovqt_plugin_log.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/ovqt_plugin_log.xml new file mode 100644 index 000000000..f1fbd7ad0 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/ovqt_plugin_log.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_log + LogPlugin + 1.1 + aquiles + DockWidget to display all log messages from NeL. + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index bb4e783a7..f72c5fcea 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -16,7 +16,7 @@ #include #include "../core/icore.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../core/core_constants.h" #include diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp index 3b9eb3ead..9e92cb90c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.cpp @@ -2,7 +2,7 @@ #include "mission_compiler_plugin.h" #include "../core/icore.h" #include "../core/core_constants.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../../extension_system/iplugin_spec.h" // NeL includes @@ -24,18 +24,18 @@ namespace MissionCompiler MissionCompilerPlugin::~MissionCompilerPlugin() { - Q_FOREACH(QObject *obj, _autoReleaseObjects) + Q_FOREACH(QObject *obj, m_autoReleaseObjects) { - _plugMan->removeObject(obj); + m_plugMan->removeObject(obj); } - qDeleteAll(_autoReleaseObjects); - _autoReleaseObjects.clear(); + qDeleteAll(m_autoReleaseObjects); + m_autoReleaseObjects.clear(); } bool MissionCompilerPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) { Q_UNUSED(errorString); - _plugMan = pluginManager; + m_plugMan = pluginManager; addAutoReleasedObject(new MissionCompilerSettingsPage(this)); addAutoReleasedObject(new CMissionCompilerContext(this)); @@ -47,18 +47,7 @@ void MissionCompilerPlugin::extensionsInitialized() { Core::ICore *core = Core::ICore::instance(); QSettings *settings = Core::ICore::instance()->settings(); - Core::IMenuManager *menuManager = core->menuManager(); - //menuManager = _plugMan->getObject(); - //QAction *exampleAction1 = new QAction("Zone1", this); - //QAction *exampleAction2 = new QAction("Zone2", this); - //QMenu *toolsMenu = menuManager->menu(Core::Constants::M_TOOLS); - //helpMenu->insertAction(aboutQtAction, exampleAction1); - //helpMenu->addSeparator(); - //helpMenu->addAction(exampleAction2); - //QMenu *zoneMenu = menuManager->menuBar()->addMenu("ZoneMenu"); - //zoneMenu->insertAction(aboutQtAction, exampleAction1); - //zoneMenu->addSeparator(); - //zoneMenu->addAction(exampleAction2); + Core::MenuManager *menuManager = core->menuManager(); // Initialize Ligo. //settings->beginGroup(Core::Constants::DATA_PATH_SECTION); @@ -73,57 +62,13 @@ void MissionCompilerPlugin::setNelContext(NLMISC::INelContext *nelContext) // This only applies to platforms without PIC, e.g. Windows. nlassert(!NLMISC::INelContext::isContextInitialised()); #endif // NL_OS_WINDOWS - _LibContext = new NLMISC::CLibraryContext(*nelContext); -} - -QString MissionCompilerPlugin::name() const -{ - return "MissionCompilerPlugin"; -} - -QString MissionCompilerPlugin::version() const -{ - return "0.1"; -} - -QString MissionCompilerPlugin::vendor() const -{ - return "Ryzom Core"; -} - -QString MissionCompilerPlugin::description() const -{ - return "Mission Compiler Plugin"; -} - -QStringList MissionCompilerPlugin::dependencies() const -{ - QStringList list; - list.append(Core::Constants::OVQT_CORE_PLUGIN); - //list.append("ObjectViewer"); - return list; + m_LibContext = new NLMISC::CLibraryContext(*nelContext); } void MissionCompilerPlugin::addAutoReleasedObject(QObject *obj) { - _plugMan->addObject(obj); - _autoReleaseObjects.prepend(obj); -} - -QObject* MissionCompilerPlugin::objectByName(const QString &name) const -{ - Q_FOREACH (QObject *qobj, _plugMan->allObjects()) - if (qobj->objectName() == name) - return qobj; - return 0; -} - -ExtensionSystem::IPluginSpec *MissionCompilerPlugin::pluginByName(const QString &name) const -{ - Q_FOREACH (ExtensionSystem::IPluginSpec *spec, _plugMan->plugins()) - if (spec->name() == name) - return spec; - return 0; + m_plugMan->addObject(obj); + m_autoReleaseObjects.prepend(obj); } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h index 7f64465e7..2ad92b40f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_plugin.h @@ -37,26 +37,16 @@ public: bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); void extensionsInitialized(); - void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - void addAutoReleasedObject(QObject *obj); - QObject *objectByName(const QString &name) const; - ExtensionSystem::IPluginSpec *pluginByName(const QString &name) const; - protected: - NLMISC::CLibraryContext *_LibContext; + NLMISC::CLibraryContext *m_LibContext; private: - ExtensionSystem::IPluginManager *_plugMan; - QList _autoReleaseObjects; + ExtensionSystem::IPluginManager *m_plugMan; + QList m_autoReleaseObjects; }; class CMissionCompilerContext: public Core::IContext diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/ovqt_plugin_mission_compiler.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/ovqt_plugin_mission_compiler.xml new file mode 100644 index 000000000..f6ef5b5c6 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/ovqt_plugin_mission_compiler.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_mission_compiler + MissionCompiler + 0.1 + Ryzom Core + Mission Compiler Plugin + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_plugin_sheet_builder.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_plugin_sheet_builder.xml new file mode 100644 index 000000000..14965e86e --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_plugin_sheet_builder.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_sheet_builder + SheetBuilder + 1.0 + kharvd + make_sheet_id equivalent + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_sheet_builder.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_sheet_builder.cpp index 18e77fbcc..4fd7dfb11 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_sheet_builder.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_sheet_builder.cpp @@ -19,7 +19,7 @@ #include "sheetbuilderdialog.h" #include "sheetbuilderconfgdialog.h" #include "../core/icore.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../core/core_constants.h" // NeL includes @@ -38,14 +38,14 @@ using namespace Plugin; bool SheetBuilderPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) { Q_UNUSED(errorString); - _plugMan = pluginManager; + m_plugMan = pluginManager; return true; } void SheetBuilderPlugin::extensionsInitialized() { - Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); + Core::MenuManager *menuManager = Core::ICore::instance()->menuManager(); QMenu *sheetMenu = menuManager->menu(Core::Constants::M_SHEET); QAction *sheetBuilderAction = sheetMenu->addAction(tr("Sheet builder")); @@ -69,34 +69,7 @@ void SheetBuilderPlugin::setNelContext(NLMISC::INelContext *nelContext) // This only applies to platforms without PIC, e.g. Windows. nlassert(!NLMISC::INelContext::isContextInitialised()); #endif // NL_OS_WINDOWS - _LibContext = new NLMISC::CLibraryContext(*nelContext); -} - -QString SheetBuilderPlugin::name() const -{ - return "Sheet builder"; -} - -QString SheetBuilderPlugin::version() const -{ - return "1.0"; -} - -QString SheetBuilderPlugin::vendor() const -{ - return "kharvd"; -} - -QString SheetBuilderPlugin::description() const -{ - return "make_sheet_id equivalent"; -} - -QStringList SheetBuilderPlugin::dependencies() const -{ - QStringList list; - list.append(Core::Constants::OVQT_CORE_PLUGIN); - return list; + m_LibContext = new NLMISC::CLibraryContext(*nelContext); } Q_EXPORT_PLUGIN(SheetBuilderPlugin) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_sheet_builder.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_sheet_builder.h index f1299dfba..504d0914d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_sheet_builder.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/ovqt_sheet_builder.h @@ -28,11 +28,6 @@ namespace NLMISC class CLibraryContext; } -namespace ExtensionSystem -{ -class IPluginSpec; -} - namespace Plugin { @@ -43,25 +38,18 @@ class SheetBuilderPlugin : public QObject, public ExtensionSystem::IPlugin public: bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); void extensionsInitialized(); - void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - void buildSheet(bool clean); private Q_SLOTS: void execBuilderDialog(); protected: - NLMISC::CLibraryContext *_LibContext; + NLMISC::CLibraryContext *m_LibContext; private: - ExtensionSystem::IPluginManager *_plugMan; + ExtensionSystem::IPluginManager *m_plugMan; }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/sheetbuilder.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/sheetbuilder.h index aac097c9b..eb9b3bc22 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/sheetbuilder.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/ovqt_sheet_builder/sheetbuilder.h @@ -54,7 +54,7 @@ union TFormId } }; -bool operator<(const TFormId& fid1, const TFormId& fid2) +bool operator<(const TFormId &fid1, const TFormId &fid2) { return fid1.Id(outputLine.data())),(uint)outputLine.size()); + output.serialBuffer((uint8 *)(const_cast(outputLine.data())),(uint)outputLine.size()); } displayInfo (tr("------------- results ----------------")); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/ovqt_plugin_zone_painter.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/ovqt_plugin_zone_painter.xml new file mode 100644 index 000000000..cf9e8afde --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/ovqt_plugin_zone_painter.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_zone_painter + ZonePainter + 0.0 + Ryzom Core + Zone Painter Plugin + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_main_window.cpp index 01086f231..e0c2dab0c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_main_window.cpp @@ -11,7 +11,7 @@ #include "painter_dock_widget.h" #include "../core/icore.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../core/core_constants.h" ZonePainterMainWindow::ZonePainterMainWindow(QWidget *parent) : @@ -145,6 +145,7 @@ void ZonePainterMainWindow::loadConfig() { QColor color; color = settings->value("BackgroundColor", QColor(80, 80, 80)).value(); + settings->endGroup(); m_nelWidget->setBackgroundColor(NLMISC::CRGBA(color.red(), color.green(), color.blue(), color.alpha())); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.cpp index 9ccfb9053..ed7d601b9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.cpp @@ -3,7 +3,7 @@ #include "zone_painter_settings_page.h" #include "../core/icore.h" #include "../core/core_constants.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../../extension_system/iplugin_spec.h" // NeL includes @@ -19,22 +19,22 @@ namespace Plugin { - NLMISC_SAFE_SINGLETON_IMPL(CZoneManager) +// NLMISC_SAFE_SINGLETON_IMPL(CZoneManager) ZonePainterPlugin::~ZonePainterPlugin() { - Q_FOREACH(QObject *obj, _autoReleaseObjects) + Q_FOREACH(QObject *obj, m_autoReleaseObjects) { - _plugMan->removeObject(obj); + m_plugMan->removeObject(obj); } - qDeleteAll(_autoReleaseObjects); - _autoReleaseObjects.clear(); + qDeleteAll(m_autoReleaseObjects); + m_autoReleaseObjects.clear(); } bool ZonePainterPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) { Q_UNUSED(errorString); - _plugMan = pluginManager; + m_plugMan = pluginManager; addAutoReleasedObject(new CZonePainterSettingsPage(this)); addAutoReleasedObject(new CZonePainterContext(this)); @@ -45,19 +45,7 @@ bool ZonePainterPlugin::initialize(ExtensionSystem::IPluginManager *pluginManage void ZonePainterPlugin::extensionsInitialized() { Core::ICore *core = Core::ICore::instance(); - Core::IMenuManager *menuManager = core->menuManager(); - //menuManager = _plugMan->getObject(); - QAction *exampleAction1 = new QAction("Zone1", this); - QAction *exampleAction2 = new QAction("Zone2", this); - QAction *aboutQtAction = menuManager->action(Core::Constants::ABOUT_QT); - QMenu *helpMenu = menuManager->menu(Core::Constants::M_HELP); - helpMenu->insertAction(aboutQtAction, exampleAction1); - helpMenu->addSeparator(); - helpMenu->addAction(exampleAction2); - QMenu *zoneMenu = menuManager->menuBar()->addMenu("ZoneMenu"); - zoneMenu->insertAction(aboutQtAction, exampleAction1); - zoneMenu->addSeparator(); - zoneMenu->addAction(exampleAction2); + Core::MenuManager *menuManager = core->menuManager(); } void ZonePainterPlugin::setNelContext(NLMISC::INelContext *nelContext) @@ -67,57 +55,13 @@ void ZonePainterPlugin::setNelContext(NLMISC::INelContext *nelContext) // This only applies to platforms without PIC, e.g. Windows. nlassert(!NLMISC::INelContext::isContextInitialised()); #endif // NL_OS_WINDOWS - _LibContext = new NLMISC::CLibraryContext(*nelContext); -} - -QString ZonePainterPlugin::name() const -{ - return "ZonePainterPlugin"; -} - -QString ZonePainterPlugin::version() const -{ - return "0.2"; -} - -QString ZonePainterPlugin::vendor() const -{ - return "Ryzom Core"; -} - -QString ZonePainterPlugin::description() const -{ - return "Zone Painter Plugin"; -} - -QStringList ZonePainterPlugin::dependencies() const -{ - QStringList list; - list.append(Core::Constants::OVQT_CORE_PLUGIN); - //list.append("ObjectViewer"); - return list; + m_LibContext = new NLMISC::CLibraryContext(*nelContext); } void ZonePainterPlugin::addAutoReleasedObject(QObject *obj) { - _plugMan->addObject(obj); - _autoReleaseObjects.prepend(obj); -} - -QObject* ZonePainterPlugin::objectByName(const QString &name) const -{ - Q_FOREACH (QObject *qobj, _plugMan->allObjects()) - if (qobj->objectName() == name) - return qobj; - return 0; -} - -ExtensionSystem::IPluginSpec *ZonePainterPlugin::pluginByName(const QString &name) const -{ - Q_FOREACH (ExtensionSystem::IPluginSpec *spec, _plugMan->plugins()) - if (spec->name() == name) - return spec; - return 0; + m_plugMan->addObject(obj); + m_autoReleaseObjects.prepend(obj); } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.h index 2c1dff53e..dee4f3124 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/zone_painter/zone_painter_plugin.h @@ -31,7 +31,7 @@ class IPluginSpec; namespace Plugin { - class CZoneManager +/* class CZoneManager { NLMISC_SAFE_SINGLETON_DECL(CZoneManager) public: @@ -41,7 +41,7 @@ namespace Plugin NL3D::CLandscapeModel *m_painterLandscape; NL3D::CZone *m_currentZone; }; - +*/ class ZonePainterPlugin : public QObject, public ExtensionSystem::IPlugin { Q_OBJECT @@ -52,28 +52,16 @@ public: bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); void extensionsInitialized(); - void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - void addAutoReleasedObject(QObject *obj); - QObject *objectByName(const QString &name) const; - ExtensionSystem::IPluginSpec *pluginByName(const QString &name) const; - - - protected: - NLMISC::CLibraryContext *_LibContext; + NLMISC::CLibraryContext *m_LibContext; private: - ExtensionSystem::IPluginManager *_plugMan; - QList _autoReleaseObjects; + ExtensionSystem::IPluginManager *m_plugMan; + QList m_autoReleaseObjects; }; class CZonePainterContext: public Core::IContext From 6d572275a1ff002b880a19ff5535015e71b9cfc4 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Mon, 26 Sep 2011 12:36:04 +0300 Subject: [PATCH 123/215] Fixed: #1193 Plugin system checks dependency before loading plugins. --- .../src/extension_system/plugin_manager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp index bcd90e351..8be6fcd93 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_manager.cpp @@ -214,9 +214,6 @@ void PluginManager::setPluginState(PluginSpec *spec, int destState) switch (destState) { - case State::Loaded: - spec->loadLibrary(); - return; case State::Resolved: spec->resolveDependencies(m_pluginSpecs); return; @@ -234,13 +231,16 @@ void PluginManager::setPluginState(PluginSpec *spec, int destState) if (depSpec->state() != destState) { spec->m_hasError = true; - spec->m_errorString = tr("Cannot initializing plugin because dependency failed to load: %1\nReason: %2") - .arg(depSpec->name()).arg(depSpec->errorString()); + spec->m_errorString = tr("Cannot load plugin because dependency failed to load: %1") + .arg(depSpec->name()); return; } } switch (destState) { + case State::Loaded: + spec->loadLibrary(); + return; case State::Initialized: spec->initializePlugin(); break; From 4040f5669144038c326414051fcca03b312439f6 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 26 Sep 2011 21:47:56 +0200 Subject: [PATCH 124/215] Removed: Visual slot file should not be in repository. It is built by the sheets packer. --- code/ryzom/common/data_common/visual_slot.tab | Bin 9804 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 code/ryzom/common/data_common/visual_slot.tab diff --git a/code/ryzom/common/data_common/visual_slot.tab b/code/ryzom/common/data_common/visual_slot.tab deleted file mode 100644 index 0bedd735ddc7913db3f0114c5e1b178df8324cbb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9804 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 OUE7_(ck$!qG5!xVHvW Date: Mon, 26 Sep 2011 21:50:16 +0200 Subject: [PATCH 125/215] Changed: #1093 Also use mirror sheets directory in sheet id bin build. --- code/nel/tools/build_gamedata/processes/sheet_id/2_build.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/nel/tools/build_gamedata/processes/sheet_id/2_build.py b/code/nel/tools/build_gamedata/processes/sheet_id/2_build.py index f935a4500..64b46c91f 100644 --- a/code/nel/tools/build_gamedata/processes/sheet_id/2_build.py +++ b/code/nel/tools/build_gamedata/processes/sheet_id/2_build.py @@ -54,7 +54,7 @@ if MakeSheetId == "": else: mkPath(log, LeveldesignDirectory) mkPath(log, LeveldesignWorldDirectory) - subprocess.call([ MakeSheetId, "-o" + LeveldesignDirectory + "/game_elem/sheet_id.bin", LeveldesignDirectory + "/game_elem", LeveldesignDirectory + "/game_element", LeveldesignWorldDirectory ]) + subprocess.call([ MakeSheetId, "-o" + LeveldesignDirectory + "/game_elem/sheet_id.bin", LeveldesignDirectory + "/game_elem", LeveldesignDirectory + "/game_element", LeveldesignWorldDirectory, DataShardDirectory + "mirror_sheets" ]) printLog(log, "") log.close() From 83e79fa761ed57d7c1fdbd0d01843f85d1a2dd17 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 26 Sep 2011 22:10:48 +0200 Subject: [PATCH 126/215] Changed: #1093 Move the visual_slot.tab to the right place after running sheets packer. --- code/nel/tools/build_gamedata/processes/sheets/2_build.py | 2 ++ code/ryzom/tools/build_gamedata/workspace/projects.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/code/nel/tools/build_gamedata/processes/sheets/2_build.py b/code/nel/tools/build_gamedata/processes/sheets/2_build.py index 511b05004..40d7774c3 100644 --- a/code/nel/tools/build_gamedata/processes/sheets/2_build.py +++ b/code/nel/tools/build_gamedata/processes/sheets/2_build.py @@ -77,6 +77,8 @@ else: cf.write("\n") cf.close() subprocess.call([ SheetsPacker ]) + copyFileIfNeeded(log, "visual_slot.tab", DataCommonDirectory + "/visual_slot.tab") + os.remove("visual_slot.tab") printLog(log, "") log.close() diff --git a/code/ryzom/tools/build_gamedata/workspace/projects.py b/code/ryzom/tools/build_gamedata/workspace/projects.py index b1f93eabd..d5c000732 100644 --- a/code/ryzom/tools/build_gamedata/workspace/projects.py +++ b/code/ryzom/tools/build_gamedata/workspace/projects.py @@ -44,8 +44,8 @@ ProjectsToProcess += [ "common/characters_maps_hr" ] # Common client data and leveldesign projects ProjectsToProcess += [ "common/fonts" ] ProjectsToProcess += [ "common/gamedev" ] -ProjectsToProcess += [ "common/data_common" ] ProjectsToProcess += [ "common/leveldesign" ] +ProjectsToProcess += [ "common/data_common" ] ProjectsToProcess += [ "common/exedll" ] ProjectsToProcess += [ "common/cfg" ] From 8c92768c985f56f15ba03251e1e392b53f1b609f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 28 Sep 2011 09:49:16 +0200 Subject: [PATCH 127/215] Changed: #1093 Build pacs prim list to correct location. --- .../generators/max_exporter_scripts/pacs_prim.py | 11 +---------- .../build_gamedata/processes/pacs_prim/1_export.py | 13 ++----------- .../tools/build_gamedata/processes/skel/1_export.py | 2 +- .../tools/build_gamedata/processes/swt/1_export.py | 2 +- .../tools/build_gamedata/processes/zone/1_export.py | 2 +- .../workspace/common/data_common/directories.py | 12 +++++++++++- .../workspace/common/data_common/process.py | 4 ++++ 7 files changed, 21 insertions(+), 25 deletions(-) diff --git a/code/nel/tools/build_gamedata/generators/max_exporter_scripts/pacs_prim.py b/code/nel/tools/build_gamedata/generators/max_exporter_scripts/pacs_prim.py index b1c3e5966..2e3f07ec9 100644 --- a/code/nel/tools/build_gamedata/generators/max_exporter_scripts/pacs_prim.py +++ b/code/nel/tools/build_gamedata/generators/max_exporter_scripts/pacs_prim.py @@ -1,16 +1,7 @@ -printLog(log, ">>> List %PreGenFileExtension% <<<") -outDirPacsPrim = ExportBuildDirectory + "/" + %PreGenExportDirectoryVariable% -mkPath(log, outDirPacsPrim) +# Remove bad file from previous script version listPath = ExportBuildDirectory + "/" + %PreGenExportDirectoryVariable% + "/landscape_col_prim_pacs_list.txt" if os.path.isfile(listPath): os.remove(listPath) -if WantLandscapeColPrimPacsList: - exportedPacsPrims = findFiles(log, outDirPacsPrim, "", ".%PreGenFileExtension%") - printLog(log, "WRITE " + listPath) - listFile = open(listPath, "w") - for exported in exportedPacsPrims: - listFile.write(exported + "\n") - listFile.close() diff --git a/code/nel/tools/build_gamedata/processes/pacs_prim/1_export.py b/code/nel/tools/build_gamedata/processes/pacs_prim/1_export.py index 5779a8cd7..e21fb4fde 100644 --- a/code/nel/tools/build_gamedata/processes/pacs_prim/1_export.py +++ b/code/nel/tools/build_gamedata/processes/pacs_prim/1_export.py @@ -6,7 +6,7 @@ # # \file 1_export.py # \brief Export pacs_prim -# \date 2010-09-19-14-19-GMT +# \date 2011-09-28-07-42-GMT # \author Jan Boon (Kaetemi) # Python port of game data build pipeline. # Export pacs_prim @@ -92,19 +92,10 @@ if MaxAvailable: -printLog(log, ">>> List pacs_prim <<<") -outDirPacsPrim = ExportBuildDirectory + "/" + PacsPrimExportDirectory -mkPath(log, outDirPacsPrim) +# Remove bad file from previous script version listPath = ExportBuildDirectory + "/" + PacsPrimExportDirectory + "/landscape_col_prim_pacs_list.txt" if os.path.isfile(listPath): os.remove(listPath) -if WantLandscapeColPrimPacsList: - exportedPacsPrims = findFiles(log, outDirPacsPrim, "", ".pacs_prim") - printLog(log, "WRITE " + listPath) - listFile = open(listPath, "w") - for exported in exportedPacsPrims: - listFile.write(exported + "\n") - listFile.close() diff --git a/code/nel/tools/build_gamedata/processes/skel/1_export.py b/code/nel/tools/build_gamedata/processes/skel/1_export.py index af73024c5..84e9ce931 100644 --- a/code/nel/tools/build_gamedata/processes/skel/1_export.py +++ b/code/nel/tools/build_gamedata/processes/skel/1_export.py @@ -6,7 +6,7 @@ # # \file 1_export.py # \brief Export skel -# \date 2010-09-19-14-19-GMT +# \date 2011-09-28-07-42-GMT # \author Jan Boon (Kaetemi) # Python port of game data build pipeline. # Export skel diff --git a/code/nel/tools/build_gamedata/processes/swt/1_export.py b/code/nel/tools/build_gamedata/processes/swt/1_export.py index 3b8da1d0f..6ad3c8f13 100644 --- a/code/nel/tools/build_gamedata/processes/swt/1_export.py +++ b/code/nel/tools/build_gamedata/processes/swt/1_export.py @@ -6,7 +6,7 @@ # # \file 1_export.py # \brief Export swt -# \date 2010-09-19-14-19-GMT +# \date 2011-09-28-07-42-GMT # \author Jan Boon (Kaetemi) # Python port of game data build pipeline. # Export swt diff --git a/code/nel/tools/build_gamedata/processes/zone/1_export.py b/code/nel/tools/build_gamedata/processes/zone/1_export.py index 8b34753c9..d464e3f31 100644 --- a/code/nel/tools/build_gamedata/processes/zone/1_export.py +++ b/code/nel/tools/build_gamedata/processes/zone/1_export.py @@ -6,7 +6,7 @@ # # \file 1_export.py # \brief Export zone -# \date 2010-09-19-14-19-GMT +# \date 2011-09-28-07-42-GMT # \author Jan Boon (Kaetemi) # Python port of game data build pipeline. # Export zone diff --git a/code/ryzom/tools/build_gamedata/workspace/common/data_common/directories.py b/code/ryzom/tools/build_gamedata/workspace/common/data_common/directories.py index 4cf2384e9..c2399a190 100644 --- a/code/ryzom/tools/build_gamedata/workspace/common/data_common/directories.py +++ b/code/ryzom/tools/build_gamedata/workspace/common/data_common/directories.py @@ -57,7 +57,17 @@ CopyDatabaseSourceDirectories = [ ] CopyDatabaseSourceFiles = [ ] +# *** SOURCE DIRECTORIES IN THE EXPORT/BUILD *** + +# Pacs prim list source directories +PacsPrimExportSourceDirectories = [ ] +PacsPrimExportSourceDirectories += [ "ecosystems/desert/pacs_prim" ] +PacsPrimExportSourceDirectories += [ "ecosystems/jungle/pacs_prim" ] +PacsPrimExportSourceDirectories += [ "ecosystems/lacustre/pacs_prim" ] +PacsPrimExportSourceDirectories += [ "ecosystems/primes_racines/pacs_prim" ] + + # *** INSTALL DIRECTORIES IN THE CLIENT DATA *** -# Particule system directory +# Common data install directory CopyInstallDirectory = CommonName diff --git a/code/ryzom/tools/build_gamedata/workspace/common/data_common/process.py b/code/ryzom/tools/build_gamedata/workspace/common/data_common/process.py index f868e1f94..d00d44e8a 100644 --- a/code/ryzom/tools/build_gamedata/workspace/common/data_common/process.py +++ b/code/ryzom/tools/build_gamedata/workspace/common/data_common/process.py @@ -28,6 +28,7 @@ # *** PROCESS CONFIG *** ProcessToComplete = [ ] +ProcessToComplete += [ "pacs_prim_list" ] ProcessToComplete += [ "copy" ] @@ -36,6 +37,9 @@ CommonName = "data_common" CommonPath = "common/" + CommonName +# *** PACS PRIM LIST OPTIONS *** + + # *** COPY DIR OPTIONS *** From 61945f3f11cf79d61265c943c3e26ddc20fe620f Mon Sep 17 00:00:00 2001 From: kaetemi Date: Wed, 28 Sep 2011 09:55:59 +0200 Subject: [PATCH 128/215] Changed: #1093 Build pacs prim list to correct location. --- .../tools/build_gamedata/interface_dev.bat | 4 ++ .../tools/build_gamedata/leveldesign_dev.bat | 4 ++ .../processes/pacs_prim_list/0_setup.py | 58 +++++++++++++++++ .../processes/pacs_prim_list/1_export.py | 53 ++++++++++++++++ .../processes/pacs_prim_list/2_build.py | 63 +++++++++++++++++++ .../processes/pacs_prim_list/3_install.py | 52 +++++++++++++++ 6 files changed, 234 insertions(+) create mode 100644 code/nel/tools/build_gamedata/interface_dev.bat create mode 100644 code/nel/tools/build_gamedata/leveldesign_dev.bat create mode 100644 code/nel/tools/build_gamedata/processes/pacs_prim_list/0_setup.py create mode 100644 code/nel/tools/build_gamedata/processes/pacs_prim_list/1_export.py create mode 100644 code/nel/tools/build_gamedata/processes/pacs_prim_list/2_build.py create mode 100644 code/nel/tools/build_gamedata/processes/pacs_prim_list/3_install.py diff --git a/code/nel/tools/build_gamedata/interface_dev.bat b/code/nel/tools/build_gamedata/interface_dev.bat new file mode 100644 index 000000000..1c5fa04eb --- /dev/null +++ b/code/nel/tools/build_gamedata/interface_dev.bat @@ -0,0 +1,4 @@ +1_export.py -ipj common/gamedev common/data_common common/exedll common/cfg common/interface common/sfx common/fonts common/outgame +2_build.py -ipj common/gamedev common/data_common common/exedll common/cfg common/interface common/sfx common/fonts common/outgame +3_install.py -ipj common/gamedev common/data_common common/exedll common/cfg common/interface common/sfx common/fonts common/outgame +5_client_dev.py diff --git a/code/nel/tools/build_gamedata/leveldesign_dev.bat b/code/nel/tools/build_gamedata/leveldesign_dev.bat new file mode 100644 index 000000000..fca3c1561 --- /dev/null +++ b/code/nel/tools/build_gamedata/leveldesign_dev.bat @@ -0,0 +1,4 @@ +1_export.py -ipj common/gamedev common/data_common common/leveldesign common/exedll common/cfg +2_build.py -ipj common/gamedev common/data_common common/leveldesign common/exedll common/cfg +3_install.py -ipj common/gamedev common/data_common common/leveldesign common/exedll common/cfg +5_client_dev.py diff --git a/code/nel/tools/build_gamedata/processes/pacs_prim_list/0_setup.py b/code/nel/tools/build_gamedata/processes/pacs_prim_list/0_setup.py new file mode 100644 index 000000000..0fad037bb --- /dev/null +++ b/code/nel/tools/build_gamedata/processes/pacs_prim_list/0_setup.py @@ -0,0 +1,58 @@ +#!/usr/bin/python +# +# \file 0_setup.py +# \brief setup pacs_prim_list +# \date 2011-09-28 7:22GMT +# \author Jan Boon (Kaetemi) +# Python port of game data build pipeline. +# Setup pacs_prim_list +# +# NeL - MMORPG Framework +# Copyright (C) 2010 Winch Gate Property Limited +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +import time, sys, os, shutil, subprocess, distutils.dir_util +sys.path.append("../../configuration") + +if os.path.isfile("log.log"): + os.remove("log.log") +log = open("log.log", "w") +from scripts import * +from buildsite import * +from process import * +from tools import * +from directories import * + +printLog(log, "") +printLog(log, "-------") +printLog(log, "--- Setup pacs_prim_list") +printLog(log, "-------") +printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) +printLog(log, "") + +# Setup source directories +printLog(log, ">>> Setup source directories <<<") +for dir in PacsPrimExportSourceDirectories: + mkPath(log, ExportBuildDirectory + "/" + dir) + +# Setup build directories +printLog(log, ">>> Setup build directories <<<") +mkPath(log, DataCommonDirectory) # no choice + +log.close() + + +# end of file diff --git a/code/nel/tools/build_gamedata/processes/pacs_prim_list/1_export.py b/code/nel/tools/build_gamedata/processes/pacs_prim_list/1_export.py new file mode 100644 index 000000000..1126f23a1 --- /dev/null +++ b/code/nel/tools/build_gamedata/processes/pacs_prim_list/1_export.py @@ -0,0 +1,53 @@ +#!/usr/bin/python +# +# \file 1_export.py +# \brief Export pacs_prim_list +# \date 2011-09-28 7:22GMT +# \author Jan Boon (Kaetemi) +# Python port of game data build pipeline. +# Export pacs_prim_list +# +# NeL - MMORPG Framework +# Copyright (C) 2010 Winch Gate Property Limited +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +import time, sys, os, shutil, subprocess, distutils.dir_util +sys.path.append("../../configuration") + +if os.path.isfile("log.log"): + os.remove("log.log") +log = open("log.log", "w") +from scripts import * +from buildsite import * +from process import * +from tools import * +from directories import * + +printLog(log, "") +printLog(log, "-------") +printLog(log, "--- Export pacs_prim_list") +printLog(log, "-------") +printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) +printLog(log, "") + +printLog(log, ">>> Nothing to do! <<<") + +printLog(log, "") + +log.close() + + +# end of file diff --git a/code/nel/tools/build_gamedata/processes/pacs_prim_list/2_build.py b/code/nel/tools/build_gamedata/processes/pacs_prim_list/2_build.py new file mode 100644 index 000000000..9b5d52541 --- /dev/null +++ b/code/nel/tools/build_gamedata/processes/pacs_prim_list/2_build.py @@ -0,0 +1,63 @@ +#!/usr/bin/python +# +# \file 2_build.py +# \brief Build pacs_prim_list +# \date 2011-09-28 7:22GMT +# \author Jan Boon (Kaetemi) +# Python port of game data build pipeline. +# Build pacs_prim_list +# +# NeL - MMORPG Framework +# Copyright (C) 2010 Winch Gate Property Limited +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +import time, sys, os, shutil, subprocess, distutils.dir_util +sys.path.append("../../configuration") + +if os.path.isfile("log.log"): + os.remove("log.log") +log = open("log.log", "w") +from scripts import * +from buildsite import * +from process import * +from tools import * +from directories import * + +printLog(log, "") +printLog(log, "-------") +printLog(log, "--- Build pacs_prim_list") +printLog(log, "-------") +printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) +printLog(log, "") + +printLog(log, ">>> List pacs_prim <<<") +listPath = DataCommonDirectory + "/landscape_col_prim_pacs_list.txt" +if os.path.isfile(listPath): + os.remove(listPath) +listFile = open(listPath, "w") +printLog(log, "WRITE " + listPath) +for dir in PacsPrimExportSourceDirectories: + outDirPacsPrim = ExportBuildDirectory + "/" + dir + mkPath(log, outDirPacsPrim) + exportedPacsPrims = findFiles(log, outDirPacsPrim, "", ".pacs_prim") + for exported in exportedPacsPrims: + listFile.write(exported + "\n") +listFile.close() + +log.close() + + +# end of file diff --git a/code/nel/tools/build_gamedata/processes/pacs_prim_list/3_install.py b/code/nel/tools/build_gamedata/processes/pacs_prim_list/3_install.py new file mode 100644 index 000000000..076f92efa --- /dev/null +++ b/code/nel/tools/build_gamedata/processes/pacs_prim_list/3_install.py @@ -0,0 +1,52 @@ +#!/usr/bin/python +# +# \file 3_install.py +# \brief Install pacs_prim_list +# \date 2011-09-28 7:22GMT +# \author Jan Boon (Kaetemi) +# Python port of game data build pipeline. +# Install pacs_prim_list +# +# NeL - MMORPG Framework +# Copyright (C) 2010 Winch Gate Property Limited +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +import time, sys, os, shutil, subprocess, distutils.dir_util +sys.path.append("../../configuration") + +if os.path.isfile("log.log"): + os.remove("log.log") +log = open("log.log", "w") +from scripts import * +from buildsite import * +from process import * +from tools import * +from directories import * + +printLog(log, "") +printLog(log, "-------") +printLog(log, "--- Install pacs_prim_list") +printLog(log, "-------") +printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) +printLog(log, "") + +printLog(log, ">>> Nothing to do! <<<") + +printLog(log, "") +log.close() + + +# end of file From 5fcea27aa1b1e7c6037f1babab10a68666cc4c7f Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 8 Oct 2011 11:01:10 +0200 Subject: [PATCH 129/215] Fixed: #1366 No Ryzom client icon under Ubuntu with Unity + Compiz --- .../3d/driver/opengl/driver_opengl_window.cpp | 72 +++++++++++++++++-- 1 file changed, 66 insertions(+), 6 deletions(-) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp index 6501d70c4..768528f91 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp @@ -188,6 +188,11 @@ bool GlWndProc(CDriverGL *driver, const void* e) static Atom XA_WM_STATE = 0; static Atom XA_WM_STATE_FULLSCREEN = 0; static Atom XA_WM_ICON = 0; +static Atom XA_WM_WINDOW_TYPE = 0; +static Atom XA_WM_WINDOW_TYPE_NORMAL = 0; +static Atom XA_UTF8_STRING = 0; +static Atom XA_WM_NAME = 0; +static Atom XA_WM_ICON_NAME = 0; sint nelXErrorsHandler(Display *dpy, XErrorEvent *e) { @@ -233,7 +238,7 @@ bool GlWndProc(CDriverGL *driver, XEvent &e) break; case Expose: - nlwarning("Expose event"); +// nlwarning("Expose event"); break; case ConfigureNotify: @@ -246,7 +251,7 @@ bool GlWndProc(CDriverGL *driver, XEvent &e) driver->_DecorationWidth = e.xconfigure.x - driver->_WindowX; driver->_DecorationHeight = e.xconfigure.y - driver->_WindowY; - nlwarning("Decoration size x = %d, y = %d", driver->_DecorationWidth, driver->_DecorationHeight); +// nlwarning("Decoration size x = %d, y = %d", driver->_DecorationWidth, driver->_DecorationHeight); } driver->_CurrentMode.Width = e.xconfigure.width; @@ -256,7 +261,7 @@ bool GlWndProc(CDriverGL *driver, XEvent &e) XConfigureEvent event = e.xconfigure; - nlwarning("Configure x = %d, y = %d, width = %d, height = %d, send event = %d", event.x, event.y, event.width, event.height, event.send_event); +// nlwarning("Configure x = %d, y = %d, width = %d, height = %d, send event = %d", event.x, event.y, event.width, event.height, event.send_event); } break; @@ -401,6 +406,11 @@ bool CDriverGL::init (uint windowIcon, emptyProc exitFunc) XA_WM_STATE = XInternAtom(_dpy, "_NET_WM_STATE", False); XA_WM_STATE_FULLSCREEN = XInternAtom(_dpy, "_NET_WM_STATE_FULLSCREEN", False); XA_WM_ICON = XInternAtom(_dpy, "_NET_WM_ICON", False); + XA_WM_WINDOW_TYPE = XInternalAtom(_dpy, "_NET_WM_WINDOW_TYPE", False); + XA_WM_WINDOW_TYPE_NORMAL = XInternalAtom(_dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); + XA_UTF8_STRING = XInternAtom(_dpy, "UTF8_STRING", False); + XA_WM_NAME = XInternAtom(_dpy, "_NET_WM_NAME", False); + XA_WM_ICON_NAME = XInternAtom(_dpy, "_NET_WM_ICON_NAME", False); #endif @@ -1528,6 +1538,42 @@ bool CDriverGL::createWindow(const GfxMode &mode) return false; } + // normal window type + XChangeProperty(_dpy, window, XA_WM_WINDOW_TYPE, XA_ATOM, 32, PropModeReplace, (const unsigned char*)&XA_WM_WINDOW_TYPE_NORMAL, 1); + + // set WM hints + XWMHints *wm_hints = XAllocWMHints(); + + if (wm_hints) + { + wm_hints->flags = StateHint | InputHint; + wm_hints->initial_state = NormalState; + wm_hints->input = True; + + XSetWMHints(_dpy, window, wm_hints); + XFree(wm_hints); + } + else + { + nlwarning("3D: Couldn't allocate XWMHints"); + } + + // set class hints + XClassHint *class_hints = XAllocClassHint(); + + if (class_hints) + { + class_hints->res_name = (char*)"NeL"; + class_hints->res_class = (char*)"nel"; + + XSetClassHint(_dpy, window, class_hints); + XFree(class_hints); + } + else + { + nlwarning("3D: Couldn't allocate XClassHint"); + } + #endif // NL_OS_UNIX _win = window; @@ -2199,15 +2245,29 @@ void CDriverGL::setWindowTitle(const ucstring &title) [NSString stringWithUTF8String:title.toUtf8().c_str()]]; #elif defined (NL_OS_UNIX) + char *utf8_title = (char*)title.toUtf8().c_str(); + size_t utf8_title_length = title.toUtf8().length(); #ifdef X_HAVE_UTF8_STRING - Xutf8SetWMProperties (_dpy, _win, (char*)title.toUtf8().c_str(), (char*)title.toUtf8().c_str(), NULL, 0, NULL, NULL, NULL); + // UTF8 properties + Xutf8SetWMProperties (_dpy, _win, utf8_title, utf8_title, NULL, 0, NULL, NULL, NULL); #else + // standard properties XTextProperty text_property; - XStringListToTextProperty((char**)&title.toUtf8().c_str(), 1, &text_property); - XSetWMProperties (_dpy, _win, &text_property, &text_property, 0, 0, NULL, 0, 0); + if (XStringListToTextProperty(&utf8_title, 1, &text_property) != 0) + { + XSetWMProperties (_dpy, _win, &text_property, &text_property, NULL, 0, NULL, NULL, NULL); + } + else + { + nlwarning("3D: Can't convert title to TextProperty"); + } #endif + // new UTF8 properties + XChangeProperty(_dpy, _win, XA_WM_NAME, XA_UTF8_STRING, 8, PropModeReplace, (const unsigned char*)utf8_title, utf8_title_length); + XChangeProperty(_dpy, _win, XA_WM_ICON_NAME, XA_UTF8_STRING, 8, PropModeReplace, (const unsigned char*)utf8_title, utf8_title_length); + #endif // NL_OS_WINDOWS } From 2e1a4591b241d048e0182bb2aec9c3cc4a7c3df2 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 8 Oct 2011 11:13:38 +0200 Subject: [PATCH 130/215] Changed: Use all existing icons for Ryzom --- code/ryzom/client/src/init.cpp | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/code/ryzom/client/src/init.cpp b/code/ryzom/client/src/init.cpp index 55d09c69e..a43dd97c0 100644 --- a/code/ryzom/client/src/init.cpp +++ b/code/ryzom/client/src/init.cpp @@ -921,20 +921,32 @@ void prelogInit() Driver->setWindowTitle(CI18N::get("TheSagaOfRyzom")); #if defined(NL_OS_UNIX) && !defined(NL_OS_MAC) - vector bitmaps; - - string fileName = "/usr/share/pixmaps/ryzom.png"; + // add all existing icons + vector filenames; + filenames.push_back("/usr/share/icons/hicolor/128x128/apps/ryzom.png"); + filenames.push_back("/usr/share/icons/hicolor/48x48/apps/ryzom.png"); + filenames.push_back("/usr/share/icons/hicolor/32x32/apps/ryzom.png"); + filenames.push_back("/usr/share/icons/hicolor/24x24/apps/ryzom.png"); + filenames.push_back("/usr/share/icons/hicolor/22x22/apps/ryzom.png"); + filenames.push_back("/usr/share/icons/hicolor/16x16/apps/ryzom.png"); + filenames.push_back("/usr/share/pixmaps/ryzom.png"); + // check if an icon is present in registered paths if(CPath::exists("ryzom.png")) - fileName = CPath::lookup("ryzom.png"); + filenames.push_back(CPath::lookup("ryzom.png")); - CIFile file; - - if (file.open(fileName)) + vector bitmaps; + + for(size_t i = 0; i < filenames.size(); ++i) { - CBitmap bitmap; - if (bitmap.load(file)) - bitmaps.push_back(bitmap); + CIFile file; + + if (CFile::fileExists(filenames[i]) && file.open(filenames[i])) + { + CBitmap bitmap; + if (bitmap.load(file)) + bitmaps.push_back(bitmap); + } } Driver->setWindowIcon(bitmaps); From d5e97e9729993f4f3e7896d0061d3f917e5b639f Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 8 Oct 2011 12:16:47 +0200 Subject: [PATCH 131/215] Changed: #1366 No Ryzom client icon under Ubuntu with Unity + Compiz --- .../3d/driver/opengl/driver_opengl_window.cpp | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp index 768528f91..0eceb55c9 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp @@ -190,9 +190,6 @@ static Atom XA_WM_STATE_FULLSCREEN = 0; static Atom XA_WM_ICON = 0; static Atom XA_WM_WINDOW_TYPE = 0; static Atom XA_WM_WINDOW_TYPE_NORMAL = 0; -static Atom XA_UTF8_STRING = 0; -static Atom XA_WM_NAME = 0; -static Atom XA_WM_ICON_NAME = 0; sint nelXErrorsHandler(Display *dpy, XErrorEvent *e) { @@ -406,11 +403,8 @@ bool CDriverGL::init (uint windowIcon, emptyProc exitFunc) XA_WM_STATE = XInternAtom(_dpy, "_NET_WM_STATE", False); XA_WM_STATE_FULLSCREEN = XInternAtom(_dpy, "_NET_WM_STATE_FULLSCREEN", False); XA_WM_ICON = XInternAtom(_dpy, "_NET_WM_ICON", False); - XA_WM_WINDOW_TYPE = XInternalAtom(_dpy, "_NET_WM_WINDOW_TYPE", False); - XA_WM_WINDOW_TYPE_NORMAL = XInternalAtom(_dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); - XA_UTF8_STRING = XInternAtom(_dpy, "UTF8_STRING", False); - XA_WM_NAME = XInternAtom(_dpy, "_NET_WM_NAME", False); - XA_WM_ICON_NAME = XInternAtom(_dpy, "_NET_WM_ICON_NAME", False); + XA_WM_WINDOW_TYPE = XInternAtom(_dpy, "_NET_WM_WINDOW_TYPE", False); + XA_WM_WINDOW_TYPE_NORMAL = XInternAtom(_dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); #endif @@ -2245,16 +2239,14 @@ void CDriverGL::setWindowTitle(const ucstring &title) [NSString stringWithUTF8String:title.toUtf8().c_str()]]; #elif defined (NL_OS_UNIX) - char *utf8_title = (char*)title.toUtf8().c_str(); - size_t utf8_title_length = title.toUtf8().length(); #ifdef X_HAVE_UTF8_STRING // UTF8 properties - Xutf8SetWMProperties (_dpy, _win, utf8_title, utf8_title, NULL, 0, NULL, NULL, NULL); + Xutf8SetWMProperties (_dpy, _win, (char*)title.toUtf8().c_str(), (char*)title.toUtf8().c_str(), NULL, 0, NULL, NULL, NULL); #else // standard properties XTextProperty text_property; - if (XStringListToTextProperty(&utf8_title, 1, &text_property) != 0) + if (XStringListToTextProperty((char**)&title.toUtf8().c_str(), 1, &text_property) != 0) { XSetWMProperties (_dpy, _win, &text_property, &text_property, NULL, 0, NULL, NULL, NULL); } @@ -2264,10 +2256,6 @@ void CDriverGL::setWindowTitle(const ucstring &title) } #endif - // new UTF8 properties - XChangeProperty(_dpy, _win, XA_WM_NAME, XA_UTF8_STRING, 8, PropModeReplace, (const unsigned char*)utf8_title, utf8_title_length); - XChangeProperty(_dpy, _win, XA_WM_ICON_NAME, XA_UTF8_STRING, 8, PropModeReplace, (const unsigned char*)utf8_title, utf8_title_length); - #endif // NL_OS_WINDOWS } From c26369a04cdba9e651ca9bd0442deac039d16ea9 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 8 Oct 2011 22:28:19 +0200 Subject: [PATCH 132/215] Added: #1093 Script for uploading data to servers --- code/nel/tools/build_gamedata/8_upload.py | 177 ++++++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 code/nel/tools/build_gamedata/8_upload.py diff --git a/code/nel/tools/build_gamedata/8_upload.py b/code/nel/tools/build_gamedata/8_upload.py new file mode 100644 index 000000000..3cfbd77ef --- /dev/null +++ b/code/nel/tools/build_gamedata/8_upload.py @@ -0,0 +1,177 @@ +#!/usr/bin/python +# +# \file 8_upload.py +# \brief Upload data to servers +# \date 2009-02-18 16:19GMT +# \author Jan Boon (Kaetemi) +# Game data build pipeline. +# Upload data to servers +# +# NeL - MMORPG Framework +# Copyright (C) 2011 Kaetemi +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# + +import time, sys, os, shutil, subprocess, distutils.dir_util +sys.path.append("configuration") + +if os.path.isfile("log.log"): + os.remove("log.log") +log = open("log.log", "w") +from scripts import * +from buildsite import * +from tools import * + +try: + from upload import * +except ImportError: + # Not documenting this. Because we can. + printLog(log, "ERROR Upload not configured, bye.") + exit() + +sys.path.append(WorkspaceDirectory) +from projects import * + +# Log error +printLog(log, "") +printLog(log, "-------") +printLog(log, "--- Upload data to servers") +printLog(log, "-------") +printLog(log, time.strftime("%Y-%m-%d %H:%MGMT", time.gmtime(time.time()))) +printLog(log, "") + +# Find tools +# Not documenting this. Because we can. +Psftp = findFileMultiDir(log, ToolDirectories + WindowsExeDllCfgDirectories, UploadPsftpTool) +printLog(log, "PSFTP " + Psftp) + +def downloadVersionTag(server, user, dir): + if os.path.isfile("upload.tag"): + os.remove("upload.tag") + if os.path.isfile("upload.batch"): + os.remove("upload.batch") + ub = open("upload.batch", "w") + ub.write("cd " + dir + "\n") + ub.write("get upload.tag upload.tag\n") + ub.write("quit\n") + ub.close() + subprocess.call([ Psftp, "-b", "upload.batch", user + "@" + server ]) + os.remove("upload.batch") + if os.path.isfile("upload.tag"): + ft = open("upload.tag") + result = float(ft.read()) # float, really + ft.close() + os.remove("upload.tag") + printLog(log, "INFO Upload tag is " + str(result)) + return result + else: + printLog(log, "WARNING Upload tag not found, uploading everything") + return 0 + +def isDirectoryNeeded(ft, dir): + files = os.listdir(dir) + for fileName in files: + if isLegalFileName(fileName): + fileFull = dir + "/" + fileName + if os.path.isfile(fileFull): + nftf = os.stat(fileFull).st_mtime + if nftf > ft: + return True + elif os.path.isdir(fileFull): + if isDirectoryNeeded(ft, fileFull): + return True + elif not os.path.isdir(fileFull): + printLog(log, "isDirectoryNeeded: file not dir or file?!" + fileFull) + return False + +def listDirectoryUpload(ft, ub, udb, dir): + nft = 0 + files = os.listdir(dir) + for fileName in files: + if isLegalFileName(fileName): + fileFull = dir + "/" + fileName + if os.path.isfile(fileFull): + nftf = os.stat(fileFull).st_mtime + if nftf > ft: + ub.write("put " + fileFull + " " + fileName + "\n") + if nftf > nft: + nft = nftf + elif os.path.isdir(fileFull): + if isDirectoryNeeded(ft, fileFull): + udb.write("mkdir " + fileName + "\n") + ub.write("cd " + fileName + "\n") + udb.write("cd " + fileName + "\n") + nft2 = listDirectoryUpload(ft, ub, udb, fileFull) + if (nft2 > nft): + nft = nft2 + ub.write("cd ..\n") + udb.write("cd ..\n") + elif not os.path.isdir(fileFull): + printLog(log, "listDirectoryUpload: file not dir or file?!" + fileFull) + return nft + +def uploadSftp(server, user, dir_to, dir_from, addcmd): + ft = downloadVersionTag(server, user, dir_to) + if isDirectoryNeeded(ft, dir_from): + if os.path.isfile("upload_dir.batch"): + os.remove("upload_dir.batch") + if os.path.isfile("upload.batch"): + os.remove("upload.batch") + udb = open("upload_dir.batch", "w") + udb.write("cd " + dir_to + "\n") + ub = open("upload.batch", "w") + ub.write("cd " + dir_to + "\n") + for ac in addcmd: + ub.write(ac + "\n") + ftn = listDirectoryUpload(ft, ub, udb, dir_from) + if (ft > ftn): + ftn = ft + nft = open("upload.tag", "w") + nft.write(str(ftn)) + nft.close() + ub.write("put upload.tag upload.tag\n") + ub.write("quit\n") + ub.close() + udb.write("quit\n") + udb.close() + subprocess.call([ Psftp, "-be", "-b", "upload_dir.batch", user + "@" + server ]) + subprocess.call([ Psftp, "-b", "upload.batch", user + "@" + server ]) + os.remove("upload_dir.batch") + os.remove("upload.batch") + os.remove("upload.tag") + else: + printLog(log, "SKIP " + dir_to) + +printLog(log, ">>> Upload patch <<<") +for target in UploadPatch: + uploadSftp(target[0], target[1], target[3], ClientPatchDirectory + "/patch", [ ]) + +printLog(log, ">>> Upload data_shard <<<") +for target in UploadShard: + uploadSftp(target[0], target[1], target[3], DataShardDirectory, [ "rm *.packed_sheets", "rm primitive_cache/*.binprim" ]) + +printLog(log, ">>> Upload data_common <<<") +for target in UploadCommon: + uploadSftp(target[0], target[1], target[3], DataCommonDirectory, [ ]) + +printLog(log, ">>> Upload data_leveldesign <<<") +for target in UploadLeveldesign: + uploadSftp(target[0], target[1], target[3], LeveldesignDirectory, [ ]) + +log.close() +if os.path.isfile("8_upload.log"): + os.remove("8_upload.log") +shutil.copy("log.log", time.strftime("%Y-%m-%d-%H-%M-GMT", time.gmtime(time.time())) + "_upload.log") +shutil.move("log.log", "8_upload.log") From 426c781b1ddd9104f7889e6c595f4d322040a96a Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Tue, 11 Oct 2011 13:48:20 +0300 Subject: [PATCH 133/215] Fixed: #1193 Clean up code. Removed imenu_manager fro cmake rules. --- .../tools/3d/object_viewer_qt/src/plugins/core/CMakeLists.txt | 1 - code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h | 2 +- .../object_viewer_qt/src/plugins/core/general_settings_page.h | 2 +- .../tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/CMakeLists.txt index 3140f02fb..927e35eaa 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/CMakeLists.txt @@ -11,7 +11,6 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin. SET(OVQT_CORE_PLUGIN_HDR icore.h icontext.h - imenu_manager.h icore_listener.h ioptions_page.h core_plugin.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h index c62fac593..f34dd6ae6 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/core.h @@ -30,7 +30,7 @@ class CoreImpl : public ICore Q_OBJECT public: - CoreImpl(MainWindow *mainWindow); + explicit CoreImpl(MainWindow *mainWindow); virtual ~CoreImpl(); virtual bool showOptionsDialog(const QString &group = QString(), diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.h index 22ab30e45..66eacd189 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/general_settings_page.h @@ -37,7 +37,7 @@ class GeneralSettingsPage : public Core::IOptionsPage Q_OBJECT public: - GeneralSettingsPage(QObject *parent = 0); + explicit GeneralSettingsPage(QObject *parent = 0); ~GeneralSettingsPage(); QString id() const; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h index 3a5e2c710..fd6af8f3a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/core/menu_manager.h @@ -44,7 +44,7 @@ class CORE_EXPORT MenuManager: public QObject Q_OBJECT public: - explicit MenuManager(QMenuBar *menuBar, QObject *parent = 0); + MenuManager(QMenuBar *menuBar, QObject *parent = 0); virtual ~MenuManager(); void registerMenu(QMenu *menu, const QString &id); From 57c18d7e95eb3f6cfa5126057c6320382f635a84 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Wed, 12 Oct 2011 00:23:50 +0300 Subject: [PATCH 134/215] Fixed: #1193 Clean up code. --- .../src/plugins/landscape_editor/landscape_editor_window.cpp | 2 -- .../tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/landscape_editor/landscape_editor_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/landscape_editor/landscape_editor_window.cpp index 4b075adfc..0bb482c72 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/landscape_editor/landscape_editor_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/landscape_editor/landscape_editor_window.cpp @@ -20,7 +20,6 @@ #include "landscape_editor_constants.h" #include "../core/icore.h" -#include "../core/imenu_manager.h" #include "../core/core_constants.h" // NeL includes @@ -72,7 +71,6 @@ void LandscapeEditorWindow::open() void LandscapeEditorWindow::createMenus() { - Core::IMenuManager *menuManager = Core::ICore::instance()->menuManager(); } void LandscapeEditorWindow::readSettings() diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp index 4eb02bd31..5a8c64f93 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/log/log_plugin.cpp @@ -21,7 +21,7 @@ #include "../core/icore.h" #include "../core/core_constants.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../../extension_system/iplugin_spec.h" // Qt includes @@ -78,7 +78,7 @@ namespace Plugin setDisplayers(); Core::ICore *core = Core::ICore::instance(); - Core::IMenuManager *menuManager = core->menuManager(); + Core::MenuManager *menuManager = core->menuManager(); QMenu *viewMenu = menuManager->menu(Core::Constants::M_VIEW); QMainWindow *wnd = Core::ICore::instance()->mainWindow(); From 88dbd944e4bf24c7cb00e42e903f644c04e041ec Mon Sep 17 00:00:00 2001 From: sfb Date: Thu, 13 Oct 2011 10:19:15 -0500 Subject: [PATCH 135/215] Fixed: #1373 the Mission Compiler plugin build for Linux and pre-4.7 Qt. --- .../plugins/mission_compiler/mission_compiler_main_window.cpp | 2 +- .../plugins/mission_compiler/mission_compiler_main_window.ui | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index f72c5fcea..769c09e40 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -379,7 +379,7 @@ bool MissionCompilerMainWindow::parsePrimForMissions(NLLIGO::IPrimitive const *p { std::string value; // if the node is a mission parse it - if (prim->getPropertyByName("class",value) && !stricmp(value.c_str(),"mission") ) + if (prim->getPropertyByName("class",value) && !NLMISC::stricmp(value.c_str(),"mission") ) { std::string name; prim->getPropertyByName("name",name); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui index d68b1a76d..dace315ab 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.ui @@ -234,9 +234,10 @@
+ From 71cd03fc96823530cac0c7ea92115fc88aac0631 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 15 Oct 2011 16:42:03 +0200 Subject: [PATCH 136/215] Fixed: #1376 Wrong NeL window position with Compiz --- .../3d/driver/opengl/driver_opengl_window.cpp | 32 +++++++++++++++---- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp index 0eceb55c9..6ebcff4c7 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_window.cpp @@ -190,6 +190,7 @@ static Atom XA_WM_STATE_FULLSCREEN = 0; static Atom XA_WM_ICON = 0; static Atom XA_WM_WINDOW_TYPE = 0; static Atom XA_WM_WINDOW_TYPE_NORMAL = 0; +static Atom XA_FRAME_EXTENTS = 0; sint nelXErrorsHandler(Display *dpy, XErrorEvent *e) { @@ -245,20 +246,36 @@ bool GlWndProc(CDriverGL *driver, XEvent &e) // first time setting decoration sizes if ((driver->_DecorationWidth == -1) || (driver->_DecorationWidth == 0)) { - driver->_DecorationWidth = e.xconfigure.x - driver->_WindowX; - driver->_DecorationHeight = e.xconfigure.y - driver->_WindowY; + Atom type_return = 0; + int format_return = 0; + unsigned long nitems_return = 0; + unsigned long bytes_after_return = 0; + long *data = NULL; + + int status = XGetWindowProperty(driver->_dpy, driver->_win, XA_FRAME_EXTENTS, 0, 4, False, XA_CARDINAL, &type_return, &format_return, &nitems_return, &bytes_after_return, (unsigned char**)&data); -// nlwarning("Decoration size x = %d, y = %d", driver->_DecorationWidth, driver->_DecorationHeight); + // succeeded to retrieve decoration size + if (status == Success && type_return == XA_CARDINAL && format_return == 32 && nitems_return == 4 && data) + { + driver->_DecorationWidth = data[0]; + driver->_DecorationHeight = data[2]; + } + else + { + // use difference between current position and previous one (set by application) + driver->_DecorationWidth = e.xconfigure.x - driver->_WindowX; + driver->_DecorationHeight = e.xconfigure.y - driver->_WindowY; + } + + // don't allow negative decoration sizes + if (driver->_DecorationWidth < 0) driver->_DecorationWidth = 0; + if (driver->_DecorationHeight < 0) driver->_DecorationHeight = 0; } driver->_CurrentMode.Width = e.xconfigure.width; driver->_CurrentMode.Height = e.xconfigure.height; driver->_WindowX = e.xconfigure.x - driver->_DecorationWidth; driver->_WindowY = e.xconfigure.y - driver->_DecorationHeight; - - XConfigureEvent event = e.xconfigure; - -// nlwarning("Configure x = %d, y = %d, width = %d, height = %d, send event = %d", event.x, event.y, event.width, event.height, event.send_event); } break; @@ -405,6 +422,7 @@ bool CDriverGL::init (uint windowIcon, emptyProc exitFunc) XA_WM_ICON = XInternAtom(_dpy, "_NET_WM_ICON", False); XA_WM_WINDOW_TYPE = XInternAtom(_dpy, "_NET_WM_WINDOW_TYPE", False); XA_WM_WINDOW_TYPE_NORMAL = XInternAtom(_dpy, "_NET_WM_WINDOW_TYPE_NORMAL", False); + XA_FRAME_EXTENTS = XInternAtom(_dpy, "_NET_FRAME_EXTENTS", False); #endif From fbf81fc0b94b3319d1f159a16a7b87e4090d88af Mon Sep 17 00:00:00 2001 From: kervala Date: Fri, 21 Oct 2011 11:34:58 +0200 Subject: [PATCH 137/215] Fixed: #1380 pdr_util, -o command line option (patch provided by nimetu) --- code/ryzom/tools/pdr_util/pdr_util.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ryzom/tools/pdr_util/pdr_util.cpp b/code/ryzom/tools/pdr_util/pdr_util.cpp index 771a0c5eb..96a6653a1 100644 --- a/code/ryzom/tools/pdr_util/pdr_util.cpp +++ b/code/ryzom/tools/pdr_util/pdr_util.cpp @@ -86,6 +86,7 @@ int main(int argc, char *argv[]) break; case 'o': outputFileName = paramValue; + break; default: fprintf(stderr, "Unknown parameter '%s'", args[i].c_str()); return -1; From 76b8fbad674d015dedb119a26fdc6e7c7d6da4b4 Mon Sep 17 00:00:00 2001 From: sfb Date: Sat, 22 Oct 2011 14:23:54 -0500 Subject: [PATCH 138/215] Changed: Fixed an error when cancelling a merge window, added double click support on merge source. Fixed window scaling on merge windows. --HG-- branch : gsoc2011-translationovqt --- .../translation_manager/ftp_selection.ui | 219 ++++++++---------- .../translation_manager/source_selection.cpp | 16 +- .../translation_manager/source_selection.h | 1 + .../translation_manager/source_selection.ui | 78 +++---- .../translation_manager_main_window.cpp | 20 +- 5 files changed, 159 insertions(+), 175 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui index 444a7d97e..7b3041c04 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.ui @@ -6,133 +6,112 @@ 0 0 - 446 - 560 + 640 + 576 Dialog - - - - 10 - 10 - 421 - 541 - - - - FTP Server informations - - - - - 10 - 110 - 401 - 381 - - - - Content - - - - - 10 - 20 - 141 - 21 - - - - Please select the file + + + + + FTP Server informations + + + + + + + Ftp server + + + + + + + + + + + + + + :/translationManager/images/cdtoparent.png:/translationManager/images/cdtoparent.png + + + + + + + Connect + + + + + + + + + Content + + + + + + Please select the file + + + + + + + + 1 + + + + + + + + Qt::Vertical + + + + 20 + 378 + + + + + + + + + + + + + Done + + + + + + + Cancel + + + + + + - - - - 10 - 40 - 381 - 311 - - - - - 1 - - - - - - - - 10 - 500 - 401 - 33 - - - - - - - Done - - - - - - - Cancel - - - - - - - - - 10 - 20 - 411 - 81 - - - - - - - Ftp server - - - - - - - - - - - - - - :/translationManager/images/cdtoparent.png:/translationManager/images/cdtoparent.png - - - - - - - Connect - - - - - - + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp index b4733c7b9..3015b447d 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp @@ -14,7 +14,9 @@ CSourceDialog::CSourceDialog(QWidget *parent): QDialog(parent) connect(_ui.ok_button, SIGNAL(clicked()), this, SLOT(OkButtonClicked())); // Set signal and slot for "Cancel Button" connect(_ui.cancel_button, SIGNAL(clicked()), this, SLOT(reject())); - _ui.listWidget->setSortingEnabled(false); + _ui.sourceSelectionListWidget->setSortingEnabled(false); + connect(_ui.sourceSelectionListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem *)), + this, SLOT(itemDoubleClicked(QListWidgetItem *))); } // Insert options in the source dialog. Options like: from FTP Server, from Local directory etc. @@ -24,14 +26,20 @@ void CSourceDialog::setSourceOptions(map options) for(it = options.begin(); it != options.end(); ++it) { - _ui.listWidget->addItem((*it).first); + _ui.sourceSelectionListWidget->addItem((*it).first); } } void CSourceDialog::OkButtonClicked() { - selected_item = _ui.listWidget->currentItem(); - reject(); + selected_item = _ui.sourceSelectionListWidget->currentItem(); + accept(); +} + +void CSourceDialog::itemDoubleClicked(QListWidgetItem *item) +{ + selected_item = item; + accept(); } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h index 99636d410..d32bfcb02 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h @@ -22,6 +22,7 @@ private: Ui::SourceSelectionDialog _ui; private Q_SLOTS: void OkButtonClicked(); + void itemDoubleClicked(QListWidgetItem *item); public: CSourceDialog(QWidget *parent = 0); ~CSourceDialog(){} diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.ui index 6e6f822fe..e64ba78c4 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.ui @@ -12,8 +12,8 @@ 0 0 - 396 - 197 + 316 + 155 @@ -22,48 +22,38 @@ true - - - - 10 - 10 - 381 - 181 - - - - Select source for merge operation - - - - - 10 - 30 - 361 - 141 - - - - - - - - - - OK - - - - - - - Cancel - - - - - - + + + + + Select source for merge operation + + + + + + + + + + + OK + + + + + + + Cancel + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 8151ea234..651050835 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -472,17 +472,23 @@ void CMainWindow::mergeSingleFile() if(dialog->selected_item == local_item) // Local directory { file_name = QFileDialog::getOpenFileName(this); - } else if(dialog->selected_item == ftp_item) { // Ftp directory + } + else if(dialog->selected_item == ftp_item) // Ftp directory + { CFtpSelection* ftp_dialog = new CFtpSelection(this); ftp_dialog->show(); - ftp_dialog->exec(); - if(ftp_dialog->status == true) - { + + if(ftp_dialog->exec() && ftp_dialog->status == true) file_name = ftp_dialog->file->fileName(); - } - } else { + + delete ftp_dialog; + } + else return; - } + + // Make sure we retrieved a file name + if(file_name.isEmpty()) + return; editor_window->activateWindow(); CEditorWorksheet* current_window = qobject_cast(editor_window); From fda7bee7586cb39d79937601ad2a62906230d8d8 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Sun, 23 Oct 2011 00:30:28 +0300 Subject: [PATCH 139/215] Fixed: #1193 In debug mode ovqt trying to load plugins witj suffix _d.dll instead _r.dll. --- .../src/extension_system/plugin_spec.cpp | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp index 5c4e04ecb..c76e19298 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp @@ -61,14 +61,19 @@ PluginSpec::PluginSpec() m_plugin(0), m_pluginManager(0) { -#ifdef Q_OS_WIN -# ifdef DEBUG - m_suffix = "_d.dll"; +// Compilation mode specific suffixes +#ifdef NL_OS_WINDOWS +# if defined(NL_DEBUG) +m_suffix = "_d.dll"; +# elif defined(NL_RELEASE) +m_suffix = "_r.dll"; # else - m_suffix = "_r.dll"; +# error "Unknown compilation mode, can't build suffix" # endif +#elif defined (NL_OS_UNIX) +m_suffix = ".so"; #else - m_suffix = ".so"; +# error "You must define the lib suffix for your platform" #endif } From 843ab1b4e7ac47e5f9665a0ec76a86af7d15a652 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Sun, 23 Oct 2011 00:32:49 +0300 Subject: [PATCH 140/215] Fixed: #1193 Clean up code. --- .../object_viewer_qt/src/extension_system/plugin_spec.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp index c76e19298..1e3ec0182 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/extension_system/plugin_spec.cpp @@ -64,14 +64,14 @@ PluginSpec::PluginSpec() // Compilation mode specific suffixes #ifdef NL_OS_WINDOWS # if defined(NL_DEBUG) -m_suffix = "_d.dll"; + m_suffix = "_d.dll"; # elif defined(NL_RELEASE) -m_suffix = "_r.dll"; + m_suffix = "_r.dll"; # else # error "Unknown compilation mode, can't build suffix" # endif #elif defined (NL_OS_UNIX) -m_suffix = ".so"; + m_suffix = ".so"; #else # error "You must define the lib suffix for your platform" #endif From f658be4ab9ac9bb508c41286c677b5a57d82d655 Mon Sep 17 00:00:00 2001 From: sfb Date: Sat, 22 Oct 2011 16:58:07 -0500 Subject: [PATCH 141/215] Changed: Added header to fix mission compiler. --HG-- branch : gsoc2011-translationovqt --- .../plugins/mission_compiler/mission_compiler_main_window.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp index 769c09e40..416a418f1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/mission_compiler/mission_compiler_main_window.cpp @@ -26,6 +26,8 @@ #include #include +#include + using namespace MissionCompiler::Constants; MissionCompilerMainWindow::MissionCompilerMainWindow(QWidget *parent) : @@ -379,7 +381,7 @@ bool MissionCompilerMainWindow::parsePrimForMissions(NLLIGO::IPrimitive const *p { std::string value; // if the node is a mission parse it - if (prim->getPropertyByName("class",value) && !NLMISC::stricmp(value.c_str(),"mission") ) + if (prim->getPropertyByName("class",value) && !stricmp(value.c_str(),"mission") ) { std::string name; prim->getPropertyByName("name",name); From b967f41eab9731a1d5ddd0c32caa53d1f3672589 Mon Sep 17 00:00:00 2001 From: sfb Date: Sat, 22 Oct 2011 16:58:55 -0500 Subject: [PATCH 142/215] Changed: Converted to new plugin spec. --HG-- branch : gsoc2011-translationovqt --- .../ovqt_plugin_translation_manager.xml | 10 ++++++ .../translation_manager_main_window.cpp | 4 +-- .../translation_manager_plugin.cpp | 32 ++----------------- .../translation_manager_plugin.h | 6 ---- 4 files changed, 14 insertions(+), 38 deletions(-) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ovqt_plugin_translation_manager.xml diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ovqt_plugin_translation_manager.xml b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ovqt_plugin_translation_manager.xml new file mode 100644 index 000000000..93a7f19bf --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ovqt_plugin_translation_manager.xml @@ -0,0 +1,10 @@ + + ovqt_plugin_translation_manager + TranslationManager + 0.8 + Ryzom Core + Translation Manager plugin. + + + + \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 651050835..23509bd20 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -19,7 +19,7 @@ // Project system includes #include "../core/icore.h" #include "../core/core_constants.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../../extension_system/iplugin_spec.h" // Qt includes @@ -124,7 +124,7 @@ void CMainWindow::createToolbar() connect(mergeSingleFileAct, SIGNAL(triggered()), this, SLOT(mergeSingleFile())); // Windows menu Core::ICore *core = Core::ICore::instance(); - Core::IMenuManager *menuManager = core->menuManager(); + Core::MenuManager *menuManager = core->menuManager(); windowMenu = menuManager->menuBar()->addMenu("Window"); updateWindowsList(); connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowsList())); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index 3efbdd0e8..a40c8e969 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -22,7 +22,7 @@ // Project system includes #include "../core/icore.h" #include "../core/core_constants.h" -#include "../core/imenu_manager.h" +#include "../core/menu_manager.h" #include "../../extension_system/iplugin_spec.h" // NeL includes @@ -66,7 +66,7 @@ bool TranslationManagerPlugin::initialize(ExtensionSystem::IPluginManager *plugi void TranslationManagerPlugin::extensionsInitialized() { Core::ICore *core = Core::ICore::instance(); - Core::IMenuManager *menuManager = core->menuManager(); + Core::MenuManager *menuManager = core->menuManager(); //menuManager = _plugMan->getObject(); // Menu Actions for plugin QAction *aboutTManPlugin = new QAction("Translation Manager", this); @@ -89,34 +89,6 @@ void TranslationManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) _LibContext = new NLMISC::CLibraryContext(*nelContext); } -QString TranslationManagerPlugin::name() const -{ - return "Translation Manager"; -} - -QString TranslationManagerPlugin::version() const -{ - return "0.1"; -} - -QString TranslationManagerPlugin::vendor() const -{ - return "cemycc"; -} - -QString TranslationManagerPlugin::description() const -{ - return "OVQT plugin for translation files."; -} - -QStringList TranslationManagerPlugin::dependencies() const -{ - QStringList list; - list.append(Core::Constants::OVQT_CORE_PLUGIN); - //list.append("ObjectViewer"); - return list; -} - void TranslationManagerPlugin::addAutoReleasedObject(QObject *obj) { _plugMan->addObject(obj); diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h index 94c75d628..f9cb6798c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -59,12 +59,6 @@ public: void setNelContext(NLMISC::INelContext *nelContext); - QString name() const; - QString version() const; - QString vendor() const; - QString description() const; - QStringList dependencies() const; - void addAutoReleasedObject(QObject *obj); QObject *objectByName(const QString &name) const; From 35bd31a2db398355ba8272c3b2ba5d8c9812da34 Mon Sep 17 00:00:00 2001 From: sfb Date: Sat, 22 Oct 2011 17:09:55 -0500 Subject: [PATCH 143/215] Changed: Removed unnecessary help menu entry. --HG-- branch : gsoc2011-translationovqt --- .../translation_manager_plugin.cpp | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index a40c8e969..a43311506 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -65,16 +65,7 @@ bool TranslationManagerPlugin::initialize(ExtensionSystem::IPluginManager *plugi void TranslationManagerPlugin::extensionsInitialized() { - Core::ICore *core = Core::ICore::instance(); - Core::MenuManager *menuManager = core->menuManager(); - //menuManager = _plugMan->getObject(); - // Menu Actions for plugin - QAction *aboutTManPlugin = new QAction("Translation Manager", this); - // Locations - QMenu *helpMenu = menuManager->menu(Core::Constants::M_HELP); - QAction *aboutQtAction = menuManager->action(Core::Constants::ABOUT_QT); - helpMenu->addSeparator(); - helpMenu->insertAction(aboutQtAction, aboutTManPlugin); + } From 0a4b19c6a72ff8b881fa0f77845c70ae0c7d8ebc Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 29 Oct 2011 15:34:04 +0200 Subject: [PATCH 144/215] Changed: #1328 Map time and weather Issue (patch provided by Sywindt) --- code/nel/src/misc/noise_value.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/code/nel/src/misc/noise_value.cpp b/code/nel/src/misc/noise_value.cpp index 89b9a8ad4..4b7cade1f 100644 --- a/code/nel/src/misc/noise_value.cpp +++ b/code/nel/src/misc/noise_value.cpp @@ -18,7 +18,7 @@ #include "nel/misc/noise_value.h" #include "nel/misc/fast_floor.h" - +#include "nel/misc/random.h" namespace NLMISC @@ -45,7 +45,8 @@ public: CRandomGrid3D() { //seed - srand(0); + CRandom Random; + Random.srand(0); // init the grid for(uint z=0; z> 5; + uint v= Random.rand() >> 5; _Texture3d[id]= v&255; } } @@ -80,9 +81,9 @@ public: // init LevelPhases. for(i=0; i Date: Mon, 31 Oct 2011 13:48:43 +0100 Subject: [PATCH 145/215] Fixed: Errors when using the database xslt using msxsl. --- code/ryzom/common/src/game_share/generate_client_db.xslt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/ryzom/common/src/game_share/generate_client_db.xslt b/code/ryzom/common/src/game_share/generate_client_db.xslt index d14903519..0680a3e39 100644 --- a/code/ryzom/common/src/game_share/generate_client_db.xslt +++ b/code/ryzom/common/src/game_share/generate_client_db.xslt @@ -297,13 +297,13 @@ inline void _getProp(const CCDBSynchronised &db, ICDBStructNode *node, NLMISC::C - + class { public: - + private: ICDBStructNode *_BranchNode; From 8555ab7d4396f29716ecb4b647e4474e7411479b Mon Sep 17 00:00:00 2001 From: kaetemi Date: Mon, 31 Oct 2011 13:54:22 +0100 Subject: [PATCH 146/215] Changed: #1093 Handle leveldesign and primitives directories seperately for the server upload script. --- code/nel/tools/build_gamedata/8_upload.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/nel/tools/build_gamedata/8_upload.py b/code/nel/tools/build_gamedata/8_upload.py index 3cfbd77ef..0b51a3683 100644 --- a/code/nel/tools/build_gamedata/8_upload.py +++ b/code/nel/tools/build_gamedata/8_upload.py @@ -166,10 +166,14 @@ printLog(log, ">>> Upload data_common <<<") for target in UploadCommon: uploadSftp(target[0], target[1], target[3], DataCommonDirectory, [ ]) -printLog(log, ">>> Upload data_leveldesign <<<") +printLog(log, ">>> Upload data_leveldesign/leveldesign <<<") for target in UploadLeveldesign: uploadSftp(target[0], target[1], target[3], LeveldesignDirectory, [ ]) +printLog(log, ">>> Upload data_leveldesign/primitives <<<") +for target in UploadPrimitives: + uploadSftp(target[0], target[1], target[3], PrimitivesDirectory, [ ]) + log.close() if os.path.isfile("8_upload.log"): os.remove("8_upload.log") From 8d57e269b8b94aef8567aa43c21c51d1546a1f6f Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Tue, 8 Nov 2011 00:22:32 +0200 Subject: [PATCH 147/215] Fixed: #1193 Correct deleting particle system from workspace --- .../src/plugins/object_viewer/particle_editor.cpp | 2 ++ .../src/plugins/object_viewer/particle_node.cpp | 1 + .../plugins/object_viewer/particle_workspace_dialog.cpp | 7 +++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_editor.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_editor.cpp index d03cdab3d..768576755 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_editor.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_editor.cpp @@ -78,6 +78,8 @@ void CParticleEditor::release() void CParticleEditor::setActiveNode(CWorkspaceNode *node) { if (node == _ActiveNode) return; + if (node == 0) + _ActiveNode->getPSModel()->hide(); _ActiveNode = node; bool wasRunning = _State == State::RunningSingle; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_node.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_node.cpp index 3dfc1ce2c..15761c505 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_node.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_node.cpp @@ -378,6 +378,7 @@ void CParticleWorkspace::removeNode(uint index) { nlassert(index < _Nodes.size()); _Nodes[index] = NULL; // delete the smart-ptr target + delete _Nodes[index]; _Nodes.erase(_Nodes.begin() + index); touch(); } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.cpp index cb49def05..74182120c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_workspace_dialog.cpp @@ -305,6 +305,8 @@ void CParticleWorkspaceDialog::customContextMenu() _instanciateAction->setEnabled(stopped); _savePSAction->setEnabled(stopped); _saveAsPSAction->setEnabled(stopped); + _removeFromWSAction->setEnabled(stopped); + _clearContentAction->setEnabled(stopped); popurMenu->exec(QCursor::pos()); delete popurMenu; @@ -369,13 +371,14 @@ void CParticleWorkspaceDialog::clearContent() void CParticleWorkspaceDialog::removePS() { - if (_treeModel->getOwnerNode(_currentItem) == Modules::psEdit().getActiveNode()) + CWorkspaceNode *node = _currentItem->getNode(); + if (node == Modules::psEdit().getActiveNode()) Modules::psEdit().setActiveNode(NULL); QModelIndex index = _ui.treeView->currentIndex(); _ui.treeView->setCurrentIndex(index.parent()); clickedItem(index.parent()); - Modules::psEdit().getParticleWorkspace()->removeNode(static_cast(index.internalPointer())->getNode()); + Modules::psEdit().getParticleWorkspace()->removeNode(node); _treeModel->removeRows(index.row(), index.parent()); } From 3fc7b8abb9e876f12517ce836f88d8f6d84a7302 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Sat, 12 Nov 2011 13:40:44 +0100 Subject: [PATCH 148/215] Added: Default cfg values for textures interfaces. --- code/ryzom/client/client_default.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/ryzom/client/client_default.cfg b/code/ryzom/client/client_default.cfg index c29ca9b93..14885fa01 100644 --- a/code/ryzom/client/client_default.cfg +++ b/code/ryzom/client/client_default.cfg @@ -80,6 +80,9 @@ XMLOutGameInterfaceFiles = { "out_v2_keys.xml", }; +TexturesInterface = "texture_interfaces_v3"; +TexturesInterfaceDXTC = "texture_interfaces_dxtc"; + // The ligo primitive class file LigoPrimitiveClass = "world_editor_classes.xml"; From 0e27ff37f333f5709f70c7d2162c41fb71164ae0 Mon Sep 17 00:00:00 2001 From: kaetemi Date: Tue, 15 Nov 2011 14:55:57 +0100 Subject: [PATCH 149/215] Added: Use ssh key for 8_upload script. --- code/nel/tools/build_gamedata/8_upload.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/code/nel/tools/build_gamedata/8_upload.py b/code/nel/tools/build_gamedata/8_upload.py index 0b51a3683..1b60aad68 100644 --- a/code/nel/tools/build_gamedata/8_upload.py +++ b/code/nel/tools/build_gamedata/8_upload.py @@ -57,7 +57,7 @@ printLog(log, "") Psftp = findFileMultiDir(log, ToolDirectories + WindowsExeDllCfgDirectories, UploadPsftpTool) printLog(log, "PSFTP " + Psftp) -def downloadVersionTag(server, user, dir): +def downloadVersionTag(server, user, sshkey, dir): if os.path.isfile("upload.tag"): os.remove("upload.tag") if os.path.isfile("upload.batch"): @@ -67,7 +67,7 @@ def downloadVersionTag(server, user, dir): ub.write("get upload.tag upload.tag\n") ub.write("quit\n") ub.close() - subprocess.call([ Psftp, "-b", "upload.batch", user + "@" + server ]) + subprocess.call([ Psftp, "-b", "upload.batch", "-i", sshkey, user + "@" + server ]) os.remove("upload.batch") if os.path.isfile("upload.tag"): ft = open("upload.tag") @@ -122,8 +122,8 @@ def listDirectoryUpload(ft, ub, udb, dir): printLog(log, "listDirectoryUpload: file not dir or file?!" + fileFull) return nft -def uploadSftp(server, user, dir_to, dir_from, addcmd): - ft = downloadVersionTag(server, user, dir_to) +def uploadSftp(server, user, sshkey, dir_to, dir_from, addcmd): + ft = downloadVersionTag(server, user, sshkey, dir_to) if isDirectoryNeeded(ft, dir_from): if os.path.isfile("upload_dir.batch"): os.remove("upload_dir.batch") @@ -146,8 +146,8 @@ def uploadSftp(server, user, dir_to, dir_from, addcmd): ub.close() udb.write("quit\n") udb.close() - subprocess.call([ Psftp, "-be", "-b", "upload_dir.batch", user + "@" + server ]) - subprocess.call([ Psftp, "-b", "upload.batch", user + "@" + server ]) + subprocess.call([ Psftp, "-be", "-b", "upload_dir.batch", "-i", sshkey, user + "@" + server ]) + subprocess.call([ Psftp, "-b", "upload.batch", "-i", sshkey, user + "@" + server ]) os.remove("upload_dir.batch") os.remove("upload.batch") os.remove("upload.tag") @@ -156,23 +156,23 @@ def uploadSftp(server, user, dir_to, dir_from, addcmd): printLog(log, ">>> Upload patch <<<") for target in UploadPatch: - uploadSftp(target[0], target[1], target[3], ClientPatchDirectory + "/patch", [ ]) + uploadSftp(target[0], target[1], target[2], target[3], ClientPatchDirectory + "/patch", [ ]) printLog(log, ">>> Upload data_shard <<<") for target in UploadShard: - uploadSftp(target[0], target[1], target[3], DataShardDirectory, [ "rm *.packed_sheets", "rm primitive_cache/*.binprim" ]) + uploadSftp(target[0], target[1], target[2], target[3], DataShardDirectory, [ "rm *.packed_sheets", "rm primitive_cache/*.binprim" ]) printLog(log, ">>> Upload data_common <<<") for target in UploadCommon: - uploadSftp(target[0], target[1], target[3], DataCommonDirectory, [ ]) + uploadSftp(target[0], target[1], target[2], target[3], DataCommonDirectory, [ ]) printLog(log, ">>> Upload data_leveldesign/leveldesign <<<") for target in UploadLeveldesign: - uploadSftp(target[0], target[1], target[3], LeveldesignDirectory, [ ]) + uploadSftp(target[0], target[1], target[2], target[3], LeveldesignDirectory, [ ]) printLog(log, ">>> Upload data_leveldesign/primitives <<<") for target in UploadPrimitives: - uploadSftp(target[0], target[1], target[3], PrimitivesDirectory, [ ]) + uploadSftp(target[0], target[1], target[2], target[3], PrimitivesDirectory, [ ]) log.close() if os.path.isfile("8_upload.log"): From 5cf3e1f4d76f56c48a15076146d5a82655d6b71a Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 7 Dec 2011 07:16:37 -0600 Subject: [PATCH 150/215] Fixed: #1367 Added ifdef for OSX 10.7 GL API changes. Thanks GelluleX. --- code/nel/src/3d/driver/opengl/mac/glext.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/code/nel/src/3d/driver/opengl/mac/glext.h b/code/nel/src/3d/driver/opengl/mac/glext.h index 7d222aa19..6bcd8c238 100644 --- a/code/nel/src/3d/driver/opengl/mac/glext.h +++ b/code/nel/src/3d/driver/opengl/mac/glext.h @@ -4851,8 +4851,10 @@ typedef ptrdiff_t GLsizeiptrARB; #ifndef GL_ARB_shader_objects /* GL types for program/shader text and shader object handles */ typedef char GLcharARB; +#if !defined(MAC_OS_X_VERSION_10_7) typedef unsigned int GLhandleARB; #endif +#endif /* GL type for "half" precision (s10e5) float data in host memory */ #ifndef GL_ARB_half_float_pixel From 5106a12702fcd5a480ef167d4a2236620a34a702 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 7 Dec 2011 07:20:05 -0600 Subject: [PATCH 151/215] Fixed: #1359 Applied patch from GelluleX adding cut/paste functionality to Ryzom for OSX. --- .../driver/opengl/mac/cocoa_event_emitter.cpp | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.cpp b/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.cpp index 95c713021..15bb40d28 100644 --- a/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.cpp +++ b/code/nel/src/3d/driver/opengl/mac/cocoa_event_emitter.cpp @@ -149,14 +149,28 @@ static NLMISC::TKey virtualKeycodeToNelKey(unsigned short keycode) bool CCocoaEventEmitter::pasteTextFromClipboard(ucstring &text) { -#warning "OpenGL Driver: Missing Mac Implementation for pasteTextFromClipboard" + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + NSArray *classArray = [NSArray arrayWithObject:[NSString class]]; + NSDictionary *options = [NSDictionary dictionary]; + + BOOL ok = [pasteboard canReadObjectForClasses:classArray options:options]; + if (ok) + { + NSArray *objectsToPaste = [pasteboard readObjectsForClasses:classArray options:options]; + NSString *nstext = [objectsToPaste objectAtIndex:0]; + text.fromUtf8([nstext UTF8String]); + return true; + } return false; } bool CCocoaEventEmitter::copyTextToClipboard(const ucstring &text) { -#warning "OpenGL Driver: Missing Mac Implementation for copyTextToClipboard" - return false; + NSPasteboard *pasteboard = [NSPasteboard generalPasteboard]; + [pasteboard clearContents]; + NSArray *copiedObjects = [NSArray arrayWithObject:[NSString stringWithUTF8String:text.toUtf8().c_str()]]; + [pasteboard writeObjects:copiedObjects]; + return true; } /// convert modifier key state to nel internal modifier key state From b7c3319d28b1f1d708ad5ce1b88dda44125b8da5 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 7 Dec 2011 07:30:30 -0600 Subject: [PATCH 152/215] Changed: #1323 Applied patch for Snowballs finding its config file on Windows. Thanks Molator. --- code/snowballs2/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/snowballs2/CMakeLists.txt b/code/snowballs2/CMakeLists.txt index 0007259ee..3107726e0 100644 --- a/code/snowballs2/CMakeLists.txt +++ b/code/snowballs2/CMakeLists.txt @@ -1,4 +1,8 @@ -SET(SNOWBALLS_CONFIG_FILE "${NL_ETC_PREFIX}/snowballs" CACHE FILEPATH "Snowballs config file location") +IF(WIN32) + SET(SNOWBALLS_CONFIG_FILE "." CACHE FILEPATH "Snowballs config file location") +ELSE(WIN32) + SET(SNOWBALLS_CONFIG_FILE "${NL_ETC_PREFIX}/snowballs" CACHE FILEPATH "Snowballs config file location") +ENDIF(WIN32) SET(SNOWBALLS_DATA_FILE "${NL_SHARE_PREFIX}/games/snowballs" CACHE FILEPATH "Snowballs data file location") SET(SNOWBALLS_LOG_FILE "${NL_LOG_PREFIX}/snowballs" CACHE FILEPATH "Snowballs log file location") From 7c3522da9eb5f248d6ecf4074c0ff578350ea691 Mon Sep 17 00:00:00 2001 From: sfb Date: Mon, 12 Dec 2011 14:38:41 -0600 Subject: [PATCH 153/215] Fixed: #1385 patch_gen uses tmpnam function instead of 'nul' for files with no pre-existing version. Note - used tmpnam instead of mkstemp due to cross-platform compatibility. --- code/ryzom/tools/patch_gen/patch_gen_common.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/code/ryzom/tools/patch_gen/patch_gen_common.cpp b/code/ryzom/tools/patch_gen/patch_gen_common.cpp index fc39e9fcf..c746fcd00 100644 --- a/code/ryzom/tools/patch_gen/patch_gen_common.cpp +++ b/code/ryzom/tools/patch_gen/patch_gen_common.cpp @@ -18,8 +18,11 @@ // includes //----------------------------------------------------------------------------- +#include + #include "game_share/bnp_patch.h" #include "nel/misc/path.h" +#include "nel/misc/file.h" #include "nel/misc/command.h" #include "nel/misc/sstring.h" #include "game_share/singleton_registry.h" @@ -320,7 +323,6 @@ void CPackageDescription::generatePatches(CBNPFileSet& packageIndex) const for (uint32 i=packageIndex.fileCount();i--;) { - bool deleteRefAfterDelta= true; // generate file name root std::string bnpFileName= _BnpDirectory+packageIndex.getFile(i).getFileName(); std::string refNameRoot= _RefDirectory+NLMISC::CFile::getFilenameWithoutExtension(bnpFileName); @@ -340,8 +342,9 @@ void CPackageDescription::generatePatches(CBNPFileSet& packageIndex) const std::string prevVersionFileName; if (packageIndex.getFile(i).versionCount()==1) { - prevVersionFileName= "nul"; - deleteRefAfterDelta= false; + prevVersionFileName= tmpnam(NULL); + NLMISC::COFile tmpFile(prevVersionFileName); + tmpFile.close(); } else { @@ -400,7 +403,7 @@ void CPackageDescription::generatePatches(CBNPFileSet& packageIndex) const } // if we have a ref file still hanging about from the previous patch then delete it - if (prevVersionFileName!= "nul" && NLMISC::CFile::fileExists(prevVersionFileName)) + if (NLMISC::CFile::fileExists(prevVersionFileName)) { NLMISC::CFile::deleteFile(prevVersionFileName); } From c89980472271d2f77da772c6ca077fad035d5de6 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 14 Dec 2011 14:51:29 -0600 Subject: [PATCH 154/215] Fixed: #1412 Added build_sound, build_samplebank and build_soundbank to CMake. --- code/nel/tools/sound/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/nel/tools/sound/CMakeLists.txt b/code/nel/tools/sound/CMakeLists.txt index d3f5a12fa..a5d44bb50 100644 --- a/code/nel/tools/sound/CMakeLists.txt +++ b/code/nel/tools/sound/CMakeLists.txt @@ -1 +1,6 @@ +ADD_SUBDIRECTORY(build_samplebank) +ADD_SUBDIRECTORY(build_sound) +ADD_SUBDIRECTORY(build_soundbank) +# Deprecated tool - no longer useful, valid or buildable. +#ADD_SUBDIRECTORY(source_sounds_builder) From e70e1811a0f3eacb5a3dcde493a39a58725a4393 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 14 Dec 2011 14:51:42 -0600 Subject: [PATCH 155/215] Fixed: #1412 Added build_sound, build_samplebank and build_soundbank to CMake. --- .../nel/tools/sound/build_samplebank/CMakeLists.txt | 13 +++++++++++++ code/nel/tools/sound/build_sound/CMakeLists.txt | 13 +++++++++++++ code/nel/tools/sound/build_soundbank/CMakeLists.txt | 13 +++++++++++++ 3 files changed, 39 insertions(+) create mode 100644 code/nel/tools/sound/build_samplebank/CMakeLists.txt create mode 100644 code/nel/tools/sound/build_sound/CMakeLists.txt create mode 100644 code/nel/tools/sound/build_soundbank/CMakeLists.txt diff --git a/code/nel/tools/sound/build_samplebank/CMakeLists.txt b/code/nel/tools/sound/build_samplebank/CMakeLists.txt new file mode 100644 index 000000000..b94d4d1bf --- /dev/null +++ b/code/nel/tools/sound/build_samplebank/CMakeLists.txt @@ -0,0 +1,13 @@ +FILE(GLOB SRC *.cpp *.h) + +ADD_EXECUTABLE(build_samplebank ${SRC}) + +ADD_DEFINITIONS( ${LIBXML2_DEFINITIONS}) + +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) + +TARGET_LINK_LIBRARIES(build_samplebank nelmisc nelsound) +NL_DEFAULT_PROPS(build_samplebank "NeL, Tools, Sound: build_samplebank") +NL_ADD_RUNTIME_FLAGS(build_samplebank) + +INSTALL(TARGETS build_samplebank RUNTIME DESTINATION bin COMPONENT toolssound) diff --git a/code/nel/tools/sound/build_sound/CMakeLists.txt b/code/nel/tools/sound/build_sound/CMakeLists.txt new file mode 100644 index 000000000..5cf651e42 --- /dev/null +++ b/code/nel/tools/sound/build_sound/CMakeLists.txt @@ -0,0 +1,13 @@ +FILE(GLOB SRC *.cpp *.h) + +ADD_EXECUTABLE(build_sound ${SRC}) + +ADD_DEFINITIONS( ${LIBXML2_DEFINITIONS}) + +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) + +TARGET_LINK_LIBRARIES(build_sound nelmisc nelsound) +NL_DEFAULT_PROPS(build_sound "NeL, Tools, Sound: build_sound") +NL_ADD_RUNTIME_FLAGS(build_sound) + +INSTALL(TARGETS build_sound RUNTIME DESTINATION bin COMPONENT toolssound) diff --git a/code/nel/tools/sound/build_soundbank/CMakeLists.txt b/code/nel/tools/sound/build_soundbank/CMakeLists.txt new file mode 100644 index 000000000..a9f556280 --- /dev/null +++ b/code/nel/tools/sound/build_soundbank/CMakeLists.txt @@ -0,0 +1,13 @@ +FILE(GLOB SRC *.cpp *.h) + +ADD_EXECUTABLE(build_soundbank ${SRC}) + +ADD_DEFINITIONS( ${LIBXML2_DEFINITIONS}) + +INCLUDE_DIRECTORIES(${LIBXML2_INCLUDE_DIR}) + +TARGET_LINK_LIBRARIES(build_soundbank nelmisc nelsound) +NL_DEFAULT_PROPS(build_soundbank "NeL, Tools, Sound: build_soundbank") +NL_ADD_RUNTIME_FLAGS(build_soundbank) + +INSTALL(TARGETS build_soundbank RUNTIME DESTINATION bin COMPONENT toolssound) From 45c720b8749af844059eef8675ddd2be8a4840a3 Mon Sep 17 00:00:00 2001 From: sfb Date: Wed, 21 Dec 2011 09:49:30 -0600 Subject: [PATCH 156/215] Fixed: #1334 Applied Ulukyn's patch. Adds two new config variables - WebIgMainDomain and WebIgTrustedDomains. --- code/ryzom/client/src/client_cfg.cpp | 13 +++++- code/ryzom/client/src/client_cfg.h | 4 ++ .../src/interface_v3/action_handler_ui.cpp | 2 +- .../client/src/interface_v3/group_html.cpp | 44 ++++++++++++------- .../client/src/interface_v3/group_html.h | 4 ++ .../src/interface_v3/group_html_webig.cpp | 3 +- code/ryzom/client/src/libwww.cpp | 3 +- code/ryzom/client/src/libwww.h | 2 +- code/ryzom/client/src/net_manager.cpp | 2 +- 9 files changed, 54 insertions(+), 23 deletions(-) diff --git a/code/ryzom/client/src/client_cfg.cpp b/code/ryzom/client/src/client_cfg.cpp index 9b60a4c5f..ff270ad1a 100644 --- a/code/ryzom/client/src/client_cfg.cpp +++ b/code/ryzom/client/src/client_cfg.cpp @@ -426,8 +426,12 @@ CClientConfig::CClientConfig() PatchUrl = ""; PatchVersion = ""; PatchServer = ""; - RingReleaseNotePath = "http://atys.ryzom.com/releasenotes_ring/index.php"; - ReleaseNotePath = "http://atys.ryzom.com/releasenotes/index.php"; + + WebIgMainDomain = "atys.ryzom.com"; + WebIgTrustedDomains.push_back(WebIgMainDomain); + + RingReleaseNotePath = "http://"+WebIgMainDomain+"/releasenotes_ring/index.php"; + ReleaseNotePath = "http://"+WebIgMainDomain+"/releasenotes/index.php"; /////////////// // ANIMATION // @@ -1040,6 +1044,11 @@ void CClientConfig::setValues() READ_STRING_DEV(ReleaseNotePath) READ_STRING_FV(PatchServer) + //////////////////////// + // WEBIG // + READ_STRING_DEV(WebIgMainDomain); + READ_STRINGVECTOR_FV(WebIgTrustedDomains); + /////////////// // ANIMATION // // AnimatedAngleThreshold diff --git a/code/ryzom/client/src/client_cfg.h b/code/ryzom/client/src/client_cfg.h index 7b7c3f8ab..667606814 100644 --- a/code/ryzom/client/src/client_cfg.h +++ b/code/ryzom/client/src/client_cfg.h @@ -292,6 +292,10 @@ struct CClientConfig std::string RingReleaseNotePath; std::string ReleaseNotePath; + //////////////////////// + // WEBIG // + std::string WebIgMainDomain; + std::vector WebIgTrustedDomains; /////////////// // ANIMATION // diff --git a/code/ryzom/client/src/interface_v3/action_handler_ui.cpp b/code/ryzom/client/src/interface_v3/action_handler_ui.cpp index 58ab060d5..8503f2738 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_ui.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_ui.cpp @@ -416,7 +416,7 @@ class CAHUIShowHide : public IActionHandler nlwarning("%s is not a group html", window.c_str()); return; } - pGH->setURL("http://atys.ryzom.com/start/index.php?app="+webapp); + pGH->setURL("http://"+ClientCfg.WebIgMainDomain+"/start/index.php?app="+webapp); } } else diff --git a/code/ryzom/client/src/interface_v3/group_html.cpp b/code/ryzom/client/src/interface_v3/group_html.cpp index 487344508..8ce18fb77 100644 --- a/code/ryzom/client/src/interface_v3/group_html.cpp +++ b/code/ryzom/client/src/interface_v3/group_html.cpp @@ -63,6 +63,13 @@ using namespace NLMISC; CGroupHTML *CGroupHTML::_ConnectingLock = NULL; extern CActionsContext ActionsContext; +// Check if domain is on TrustedDomain +bool CGroupHTML::isTrustedDomain(const string &domain) { + vector::iterator it; + it = find (ClientCfg.WebIgTrustedDomains.begin(), ClientCfg.WebIgTrustedDomains.end(), domain); + return it != ClientCfg.WebIgTrustedDomains.end(); +} + // Get an url and return the local filename with the path where the url image should be string CGroupHTML::localImageName(const string &url) { @@ -215,6 +222,9 @@ bool CGroupHTML::addBnpDownload(const string &url, const string &action, const s void CGroupHTML::initBnpDownload() { + if (!_TrustedDomain) + return; + #ifdef LOG_DL nlwarning("Init Bnp Download"); #endif @@ -452,7 +462,7 @@ void CGroupHTML::addText (const char * buf, int len) // for (i=0; i<(uint)len; i++) // inputString[i] = buf[i]; - if (_ParsingLua) + if (_ParsingLua && _TrustedDomain) { // we are parsing a lua script _LuaScript += inputString; @@ -523,12 +533,12 @@ void CGroupHTML::addLink (uint element_number, uint /* attribute_number */, HTCh if (present[MY_HTML_A_HREF] && value[MY_HTML_A_HREF]) { string suri = value[MY_HTML_A_HREF]; - if(suri.find("ah:") == 0) + if(_TrustedDomain && suri.find("ah:") == 0) { // in ah: command we don't respect the uri standard so the HTAnchor_address doesn't work correctly _Link.push_back (suri); } - else if (suri[0] == '#') + else if (_TrustedDomain && suri[0] == '#') { // Direct url (hack for lua beginElement) _Link.push_back (suri.substr(1)); @@ -822,7 +832,7 @@ void CGroupHTML::beginElement (uint element_number, const BOOL *present, const c _A.push_back(true); // Quick help - if (present[MY_HTML_A_Z_ACTION_SHORTCUT] && value[MY_HTML_A_Z_ACTION_SHORTCUT]) + if (_TrustedDomain && present[MY_HTML_A_Z_ACTION_SHORTCUT] && value[MY_HTML_A_Z_ACTION_SHORTCUT]) { // Get the action category string category; @@ -1532,19 +1542,23 @@ void CGroupHTML::endElement (uint element_number) _IgnoreText = false; break; case HTML_OBJECT: - if (_ObjectType=="application/ryzom-data") + if (_TrustedDomain) { - if (!_ObjectData.empty()) + if (_ObjectType=="application/ryzom-data") { - if (addBnpDownload(_ObjectData, _ObjectAction, _ObjectScript, _ObjectMD5Sum)) + if (!_ObjectData.empty()) { - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - pIM->executeLuaScript(_ObjectScript, true); + if (addBnpDownload(_ObjectData, _ObjectAction, _ObjectScript, _ObjectMD5Sum)) + { + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + pIM->executeLuaScript(_ObjectScript, true); + } + _ObjectScript = ""; } - _ObjectScript = ""; } + _Object = false; } - _Object = false; + break; } } } @@ -1556,7 +1570,7 @@ void CGroupHTML::beginUnparsedElement(const char *buffer, int length) if (stricmp(str.c_str(), "lua") == 0) { // we receive an embeded lua script - _ParsingLua = true; + _ParsingLua = _TrustedDomain; // Only parse lua if TrustedDomain _LuaScript = ""; } } @@ -1567,7 +1581,7 @@ void CGroupHTML::endUnparsedElement(const char *buffer, int length) string str(buffer, buffer+length); if (stricmp(str.c_str(), "lua") == 0) { - if (_ParsingLua) + if (_ParsingLua && _TrustedDomain) { _ParsingLua = false; // execute the embeded lua script @@ -2894,7 +2908,7 @@ void CGroupHTML::handle () // Init LibWWW initLibWWW(); - setCurrentDomain(finalUrl); + _TrustedDomain = isTrustedDomain(setCurrentDomain(finalUrl)); // Get the final URL C3WSmartPtr uri = HTParse(finalUrl.c_str(), NULL, PARSE_ALL); @@ -3045,7 +3059,7 @@ void CGroupHTML::handle () // Init LibWWW initLibWWW(); - setCurrentDomain(_URL); + _TrustedDomain = isTrustedDomain(setCurrentDomain(_URL)); // Get the final URL C3WSmartPtr uri = HTParse(_URL.c_str(), NULL, PARSE_ALL); diff --git a/code/ryzom/client/src/interface_v3/group_html.h b/code/ryzom/client/src/interface_v3/group_html.h index 92cb81e6e..d155c6d3f 100644 --- a/code/ryzom/client/src/interface_v3/group_html.h +++ b/code/ryzom/client/src/interface_v3/group_html.h @@ -281,6 +281,9 @@ protected : // Current URL std::string _URL; + // Current DOMAIN + bool _TrustedDomain; + // Title prefix ucstring _TitlePrefix; @@ -579,6 +582,7 @@ private: void checkImageDownload(); void addImageDownload(const std::string &url, CViewBase *img); std::string localImageName(const std::string &url); + bool isTrustedDomain(const std::string &domain); diff --git a/code/ryzom/client/src/interface_v3/group_html_webig.cpp b/code/ryzom/client/src/interface_v3/group_html_webig.cpp index 7e1781289..4a13d9806 100644 --- a/code/ryzom/client/src/interface_v3/group_html_webig.cpp +++ b/code/ryzom/client/src/interface_v3/group_html_webig.cpp @@ -203,8 +203,7 @@ struct CWebigNotificationThread : public NLMISC::IRunnable nlSleep(1*60*1000); while (true) { - string url = "http://atys.ryzom.com/start/index.php?app=notif&rnd="+randomString(); - //string url = "http://ryapp.bmsite.net/app_mail.php?page=ajax/inbox/unread&rnd="+randomString(); + string url = "http://"+ClientCfg.WebIgMainDomain+"/start/index.php?app=notif&rnd="+randomString(); addWebIGParams(url); get(url); nlSleep(10*60*1000); diff --git a/code/ryzom/client/src/libwww.cpp b/code/ryzom/client/src/libwww.cpp index c4695840d..966aeffa6 100644 --- a/code/ryzom/client/src/libwww.cpp +++ b/code/ryzom/client/src/libwww.cpp @@ -520,7 +520,7 @@ int HTMIME_location_custom (HTRequest * request, HTResponse * response, char * t // *************************************************************************** -void setCurrentDomain(const std::string &url) +const std::string &setCurrentDomain(const std::string &url) { if(url.find("http://") == 0) { @@ -532,6 +532,7 @@ void setCurrentDomain(const std::string &url) HTTPCurrentDomain.clear(); // nlinfo("****cd: clear the domain"); } + return HTTPCurrentDomain; } void initLibWWW() diff --git a/code/ryzom/client/src/libwww.h b/code/ryzom/client/src/libwww.h index 5c686e39b..41db90a6c 100644 --- a/code/ryzom/client/src/libwww.h +++ b/code/ryzom/client/src/libwww.h @@ -35,7 +35,7 @@ class CCtrlBaseButton; void initLibWWW(); // Get an url and setup a local domain -void setCurrentDomain(const std::string &url); +const std::string &setCurrentDomain(const std::string &url); extern std::string CurrentCookie; diff --git a/code/ryzom/client/src/net_manager.cpp b/code/ryzom/client/src/net_manager.cpp index 29a5a0e37..568081c81 100644 --- a/code/ryzom/client/src/net_manager.cpp +++ b/code/ryzom/client/src/net_manager.cpp @@ -3253,7 +3253,7 @@ private: if(i != digitMaxEnd) { ucstring web_app = contentStr.substr(digitStart, i-digitStart); - contentStr = ucstring("http://atys.ryzom.com/start/")+web_app+ucstring(".php?")+contentStr.substr(i+1); + contentStr = ucstring("http://"+ClientCfg.WebIgMainDomain+"/start/")+web_app+ucstring(".php?")+contentStr.substr(i+1); } else { From 687325f112419b765b6d0f7585613031925de871 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Thu, 22 Dec 2011 02:41:39 +0300 Subject: [PATCH 157/215] Fixed: #1307 Clean up code. --- .../translation_manager/CMakeLists.txt | 26 +- .../translation_manager/editor_phrase.cpp | 100 +- .../translation_manager/editor_phrase.h | 185 ++-- .../translation_manager/editor_worksheet.cpp | 734 +++++++-------- .../translation_manager/editor_worksheet.h | 93 +- .../translation_manager/extract_bot_names.cpp | 181 ++-- .../translation_manager/extract_bot_names.h | 64 +- .../extract_new_sheet_names.cpp | 206 ++--- .../extract_new_sheet_names.h | 29 +- .../translation_manager/ftp_selection.cpp | 373 ++++---- .../translation_manager/ftp_selection.h | 57 +- .../translation_manager/source_selection.cpp | 51 +- .../translation_manager/source_selection.h | 39 +- .../translation_manager_constants.h | 47 +- .../translation_manager_editor.h | 50 +- .../translation_manager_main_window.cpp | 869 +++++++++--------- .../translation_manager_main_window.h | 130 ++- .../translation_manager_plugin.cpp | 27 +- .../translation_manager_plugin.h | 16 +- .../translation_manager_settings_page.cpp | 114 ++- .../translation_manager_settings_page.h | 28 +- .../translation_manager_settings_page.ui | 308 +++---- 22 files changed, 1858 insertions(+), 1869 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt index 0f520c9d8..272c7b962 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/CMakeLists.txt @@ -10,25 +10,23 @@ SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin. ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_spec.h) SET(OVQT_PLUG_TRANSLATION_MANAGER_HDR translation_manager_plugin.h - translation_manager_main_window.h - translation_manager_settings_page.h - translation_manager_editor.h - source_selection.h - ftp_selection.h - editor_worksheet.h - editor_phrase.h - extract_new_sheet_names.h - extract_bot_names.h) + translation_manager_main_window.h + translation_manager_settings_page.h + translation_manager_editor.h + source_selection.h + ftp_selection.h + editor_worksheet.h + editor_phrase.h +) SET(OVQT_PLUG_TRANSLATION_MANAGER_UIS translation_manager_settings_page.ui - translation_manager_main_window.ui - source_selection.ui - ftp_selection.ui) + translation_manager_main_window.ui + source_selection.ui + ftp_selection.ui) SET(OVQT_PLUG_TRANSLATION_MANAGER_RCS ftp_selection.qrc) SET(QT_USE_QTGUI TRUE) -SET(QT_USE_QTOPENGL TRUE) SET(QT_USE_QTNETWORK TRUE) QT4_WRAP_CPP(OVQT_PLUG_TRANSLATION_MANAGER_MOC_SRC ${OVQT_PLUG_TRANSLATION_MANAGER_HDR}) @@ -42,7 +40,7 @@ SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) ADD_LIBRARY(ovqt_plugin_translation_manager MODULE ${SRC} ${OVQT_PLUG_TRANSLATION_MANAGER_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUG_TRANSLATION_MANAGER_UI_HDRS}) -TARGET_LINK_LIBRARIES(ovqt_plugin_translation_manager ovqt_plugin_core nelmisc nel3d nelligo nelgeorges ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY} ${QT_QTNETWORK_LIBRARY} ) +TARGET_LINK_LIBRARIES(ovqt_plugin_translation_manager ovqt_plugin_core nelmisc nelligo nelgeorges ${QT_LIBRARIES} ${QT_QTOPENGL_LIBRARY} ${QT_QTNETWORK_LIBRARY} ) NL_DEFAULT_PROPS(ovqt_plugin_translation_manager "NeL, Tools, 3D: Object Viewer Qt Plugin: Translation Manager") NL_ADD_RUNTIME_FLAGS(ovqt_plugin_translation_manager) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp index c1633c3be..a1c59a11f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.cpp @@ -1,5 +1,4 @@ // Translation Manager Plugin - OVQT Plugin -// Copyright (C) 2010 Winch Gate Property Limited // Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify @@ -15,32 +14,32 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +// Project includes +#include "editor_phrase.h" +#include "translation_manager_constants.h" + // Nel includes #include "nel/misc/path.h" #include "nel/misc/diff_tool.h" // Qt includes +#include +#include +#include +#include +#include #include -#include #include #include -#include -#include -#include -#include -#include - -// Project includes -#include "editor_phrase.h" -#include "translation_manager_constants.h" using namespace std; -namespace TranslationManager { +namespace TranslationManager +{ void CEditorPhrase::open(QString filename) { - vector phrases; + std::vector phrases; if(readPhraseFile(filename.toStdString(), phrases, false)) { text_edit = new CTextEdit(this); @@ -53,26 +52,28 @@ void CEditorPhrase::open(QString filename) QFile file(filename); file.open(QIODevice::ReadOnly | QIODevice::Text); QTextStream in(&file); - // set the file content to the text edit + // set the file content to the text edit QString data = in.readAll(); text_edit->append(data); // window settings setCurrentFile(filename); - setAttribute(Qt::WA_DeleteOnClose); + setAttribute(Qt::WA_DeleteOnClose); editor_type = Constants::ED_PHRASE; current_file = filename; connect(text_edit->document(), SIGNAL(contentsChanged()), this, SLOT(docContentsChanged())); connect(text_edit->document(), SIGNAL(undoCommandAdded()), this, SLOT(newUndoCommandAdded())); - } else { - QErrorMessage error; - error.showMessage("This file is not a phrase file."); - error.exec(); - } + } + else + { + QErrorMessage error; + error.showMessage("This file is not a phrase file."); + error.exec(); + } } void CEditorPhrase::newUndoCommandAdded() { - current_stack->push(new CUndoPhraseNewCommand(text_edit)); + current_stack->push(new CUndoPhraseNewCommand(text_edit)); } void CEditorPhrase::docContentsChanged() @@ -97,44 +98,37 @@ void CEditorPhrase::saveAs(QString filename) QTextStream out(&file); out.setCodec("UTF-8"); out.setGenerateByteOrderMark(true); - out<toPlainText(); + out << text_edit->toPlainText(); current_file = filename; setCurrentFile(current_file); } - - void CEditorPhrase::closeEvent(QCloseEvent *event) { - if(isWindowModified()) - { - QMessageBox msgBox; - msgBox.setText("The document has been modified."); - msgBox.setInformativeText("Do you want to save your changes?"); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - int ret = msgBox.exec(); - switch (ret) - { - case QMessageBox::Save: - save(); - event->accept(); - close(); - break; - case QMessageBox::Discard: - event->accept(); - close(); - break; - case QMessageBox::Cancel: - event->ignore(); - break; - default: - break; - } - } else { - event->accept(); - close(); - } + if(isWindowModified()) + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Question); + msgBox.setText(tr("The document has been modified.")); + msgBox.setInformativeText(tr("Do you want to save your changes?")); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Save: + save(); + break; + case QMessageBox::Discard: + break; + case QMessageBox::Cancel: + event->ignore(); + return; + } + } + event->accept(); + close(); } } \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h index f26dec73e..b9955fa48 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_phrase.h @@ -18,6 +18,9 @@ #ifndef EDITOR_PHRASE_H #define EDITOR_PHRASE_H +// Project includes +#include "translation_manager_editor.h" + // Qt includes #include #include @@ -32,23 +35,23 @@ #include #include -// Project includes -#include "translation_manager_editor.h" - -namespace TranslationManager { +namespace TranslationManager +{ class CTextEdit : public QTextEdit { Q_OBJECT + private: - QUndoStack* m_undoStack; + QUndoStack *m_undoStack; + public: - CTextEdit(QWidget* parent = 0) : QTextEdit(parent) - { + CTextEdit(QWidget *parent = 0) : QTextEdit(parent) + { setUndoRedoEnabled(true); } //void keyPressEvent(QKeyEvent *event); - void setUndoStack(QUndoStack* undoStack) + void setUndoStack(QUndoStack *undoStack) { m_undoStack = undoStack; } @@ -57,20 +60,22 @@ public: class CEditorPhrase : public CEditor { Q_OBJECT + public: - CTextEdit *text_edit; -public: - CEditorPhrase(QMdiArea* parent) : CEditor(parent) {} - CEditorPhrase() : CEditor() {} - void open(QString filename); - void save(); - void saveAs(QString filename); - void activateWindow(); + CEditorPhrase(QMdiArea *parent) : CEditor(parent) {} + CEditorPhrase() : CEditor() {} + void open(QString filename); + void save(); + void saveAs(QString filename); + void activateWindow(); void closeEvent(QCloseEvent *event); + public Q_SLOTS: void docContentsChanged(); void newUndoCommandAdded(); +private: + CTextEdit *text_edit; }; class CUndoPhraseNewCommand : public QUndoCommand @@ -79,7 +84,7 @@ public: CUndoPhraseNewCommand(CTextEdit *textEdit, QUndoCommand *parent = 0) : QUndoCommand("Inserting/Removing characters", parent), m_textEdit(textEdit) - { } + {} ~CUndoPhraseNewCommand() {} @@ -92,96 +97,102 @@ public: { m_textEdit->redo(); } + private: - CTextEdit* m_textEdit; + CTextEdit *m_textEdit; }; class SyntaxHighlighter : public QSyntaxHighlighter { public: SyntaxHighlighter(QTextEdit *parent) : QSyntaxHighlighter(parent) - { - HighlightingRule rule; + { + HighlightingRule rule; - translateStringFormat.setFontWeight(QFont::Bold); - translateStringFormat.setForeground(Qt::darkMagenta); - rule.pattern = QRegExp("\\[.+\\]"); - rule.format = translateStringFormat; - highlightingRules.append(rule); + translateStringFormat.setFontWeight(QFont::Bold); + translateStringFormat.setForeground(Qt::darkMagenta); + rule.pattern = QRegExp("\\[.+\\]"); + rule.format = translateStringFormat; + highlightingRules.append(rule); + singleLineCommentFormat.setForeground(Qt::red); + rule.pattern = QRegExp("//[^\n]*"); + rule.format = singleLineCommentFormat; + highlightingRules.append(rule); - singleLineCommentFormat.setForeground(Qt::red); - rule.pattern = QRegExp("//[^\n]*"); - rule.format = singleLineCommentFormat; - highlightingRules.append(rule); + multiLineCommentFormat.setForeground(Qt::red); - multiLineCommentFormat.setForeground(Qt::red); + quotationFormat.setForeground(Qt::darkGreen); + rule.pattern = QRegExp("\".*\""); + rule.format = quotationFormat; + highlightingRules.append(rule); - quotationFormat.setForeground(Qt::darkGreen); - rule.pattern = QRegExp("\".*\""); - rule.format = quotationFormat; - highlightingRules.append(rule); + functionFormat.setFontItalic(true); + functionFormat.setForeground(Qt::blue); + rule.pattern = QRegExp("\\(.+\\)"); + rule.format = functionFormat; + highlightingRules.append(rule); - functionFormat.setFontItalic(true); - functionFormat.setForeground(Qt::blue); - rule.pattern = QRegExp("\\(.+\\)"); - rule.format = functionFormat; - highlightingRules.append(rule); - - commentStartExpression = QRegExp("/\\*"); - commentEndExpression = QRegExp("\\*/"); + commentStartExpression = QRegExp("/\\*"); + commentEndExpression = QRegExp("\\*/"); } - void highlightBlock(const QString &text) - { - Q_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); + void highlightBlock(const QString &text) + { + Q_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); - int startIndex = 0; - if (previousBlockState() != 1) - startIndex = commentStartExpression.indexIn(text); + int startIndex = 0; + if (previousBlockState() != 1) + startIndex = commentStartExpression.indexIn(text); - while (startIndex >= 0) { - int endIndex = commentEndExpression.indexIn(text, startIndex); - int commentLength; - if (endIndex == -1) { - setCurrentBlockState(1); - commentLength = text.length() - startIndex; - } else { - commentLength = endIndex - startIndex - + commentEndExpression.matchedLength(); - } - setFormat(startIndex, commentLength, multiLineCommentFormat); - startIndex = commentStartExpression.indexIn(text, startIndex + commentLength); - } - } + while (startIndex >= 0) + { + int endIndex = commentEndExpression.indexIn(text, startIndex); + int commentLength; + if (endIndex == -1) + { + setCurrentBlockState(1); + commentLength = text.length() - startIndex; + } + else + { + commentLength = endIndex - startIndex + + commentEndExpression.matchedLength(); + } + setFormat(startIndex, commentLength, multiLineCommentFormat); + startIndex = commentStartExpression.indexIn(text, startIndex + commentLength); + } + } - private: - struct HighlightingRule - { - QRegExp pattern; - QTextCharFormat format; - }; - QVector highlightingRules; +private: + struct HighlightingRule + { + QRegExp pattern; + QTextCharFormat format; + }; + QVector highlightingRules; - QRegExp commentStartExpression; - QRegExp commentEndExpression; + QRegExp commentStartExpression; + QRegExp commentEndExpression; - QTextCharFormat keywordFormat; - QTextCharFormat classFormat; - QTextCharFormat singleLineCommentFormat; - QTextCharFormat multiLineCommentFormat; - QTextCharFormat quotationFormat; - QTextCharFormat functionFormat; - QTextCharFormat translateStringFormat; + QTextCharFormat keywordFormat; + QTextCharFormat classFormat; + QTextCharFormat singleLineCommentFormat; + QTextCharFormat multiLineCommentFormat; + QTextCharFormat quotationFormat; + QTextCharFormat functionFormat; + QTextCharFormat translateStringFormat; }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp index c8a101e0f..7041a5178 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.cpp @@ -15,6 +15,11 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +// Project includes +#include "editor_worksheet.h" +#include "extract_bot_names.h" +#include "translation_manager_constants.h" + // Qt includes #include #include @@ -24,97 +29,103 @@ #include #include -// Project includes -#include "editor_worksheet.h" -#include "extract_bot_names.h" -#include "translation_manager_constants.h" -#include - using namespace std; -namespace TranslationManager { +namespace TranslationManager +{ void CEditorWorksheet::open(QString filename) { - STRING_MANAGER::TWorksheet wk_file; - if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) - { - bool hasHashValue = false; - table_editor = new QTableWidget(); - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - table_editor->setColumnCount(wk_file.ColCount - 1); - hasHashValue = true; - } else { - table_editor->setColumnCount(wk_file.ColCount); - } - table_editor->setRowCount(wk_file.size() - 1); - - // read columns name - for(unsigned int i = 0; i < wk_file.ColCount; i++) - { - if(hasHashValue && i == 0) - { - // we don't show the column with hash value - } else { - QTableWidgetItem *col = new QTableWidgetItem(); - ucstring col_name = wk_file.getData(0, i); - col->setText(QString(col_name.toString().c_str())); - if(hasHashValue) - { - table_editor->setHorizontalHeaderItem(i - 1, col); - } else { - table_editor->setHorizontalHeaderItem(i, col); - } - } - } - - // read rows - for(unsigned int i = 1; i < wk_file.size(); i++) - { - for(unsigned int j = 0; j < wk_file.ColCount; j++) - { - if(hasHashValue && j == 0) - { - // we don't show the column with hash value - } else { - QTableWidgetItem *row = new QTableWidgetItem(); - ucstring row_value = wk_file.getData(i, j); - row->setText(QString::fromUtf8(row_value.toUtf8().c_str())); - if(hasHashValue) - { - table_editor->setItem(i - 1, j - 1, row); - } else { - table_editor->setItem(i - 1, j, row); - } - } - } - } - setCurrentFile(filename); - setAttribute(Qt::WA_DeleteOnClose); - setWidget(table_editor); - editor_type = Constants::ED_SHEET; - table_editor->resizeColumnsToContents(); - table_editor->resizeRowsToContents(); - // set editor signals - connect(table_editor, SIGNAL(itemChanged(QTableWidgetItem*) ), this, SLOT(worksheetEditorChanged(QTableWidgetItem*))); - connect(table_editor, SIGNAL(itemDoubleClicked(QTableWidgetItem*) ), this, SLOT(worksheetEditorCellEntered(QTableWidgetItem*))); - connect (table_editor,SIGNAL(customContextMenuRequested(const QPoint &)), this,SLOT(contextMenuEvent(QContextMenuEvent*))); - } else { - QErrorMessage error; - error.showMessage("This file is not a worksheet file."); - error.exec(); - } - + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + bool hasHashValue = false; + table_editor = new QTableWidget(); + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + table_editor->setColumnCount(wk_file.ColCount - 1); + hasHashValue = true; + } + else + { + table_editor->setColumnCount(wk_file.ColCount); + } + table_editor->setRowCount(wk_file.size() - 1); + + // read columns name + for(uint i = 0; i < wk_file.ColCount; i++) + { + if(hasHashValue && i == 0) + { + // we don't show the column with hash value + } + else + { + QTableWidgetItem *col = new QTableWidgetItem(); + ucstring col_name = wk_file.getData(0, i); + col->setText(QString(col_name.toString().c_str())); + if(hasHashValue) + { + table_editor->setHorizontalHeaderItem(i - 1, col); + } + else + { + table_editor->setHorizontalHeaderItem(i, col); + } + } + } + + // read rows + for(unsigned int i = 1; i < wk_file.size(); i++) + { + for(unsigned int j = 0; j < wk_file.ColCount; j++) + { + if(hasHashValue && j == 0) + { + // we don't show the column with hash value + } + else + { + QTableWidgetItem *row = new QTableWidgetItem(); + ucstring row_value = wk_file.getData(i, j); + row->setText(QString::fromUtf8(row_value.toUtf8().c_str())); + if(hasHashValue) + { + table_editor->setItem(i - 1, j - 1, row); + } + else + { + table_editor->setItem(i - 1, j, row); + } + } + } + } + setCurrentFile(filename); + setAttribute(Qt::WA_DeleteOnClose); + setWidget(table_editor); + editor_type = Constants::ED_SHEET; + table_editor->resizeColumnsToContents(); + table_editor->resizeRowsToContents(); + // set editor signals + connect(table_editor, SIGNAL(itemChanged(QTableWidgetItem *) ), this, SLOT(worksheetEditorChanged(QTableWidgetItem *))); + connect(table_editor, SIGNAL(itemDoubleClicked(QTableWidgetItem *) ), this, SLOT(worksheetEditorCellEntered(QTableWidgetItem *))); + connect(table_editor,SIGNAL(customContextMenuRequested(const QPoint &)), this,SLOT(contextMenuEvent(QContextMenuEvent *))); + } + else + { + QErrorMessage error; + error.showMessage(tr("This file is not a worksheet file.")); + error.exec(); + } } void CEditorWorksheet::contextMenuEvent(QContextMenuEvent *e) { - QAction *insertRowAct = new QAction("Insert new row", this); - connect(insertRowAct, SIGNAL(triggered()), this, SLOT(insertRow())); - QAction *deleteRowAct = new QAction("Delete row", this); - connect(deleteRowAct, SIGNAL(triggered()), this, SLOT(deleteRow())); + QAction *insertRowAct = new QAction(tr("Insert new row"), this); + connect(insertRowAct, SIGNAL(triggered()), this, SLOT(insertRow())); + QAction *deleteRowAct = new QAction(tr("Delete row"), this); + connect(deleteRowAct, SIGNAL(triggered()), this, SLOT(deleteRow())); QMenu *contextMenu = new QMenu(this); contextMenu->addAction(insertRowAct); @@ -127,7 +138,7 @@ void CEditorWorksheet::contextMenuEvent(QContextMenuEvent *e) void CEditorWorksheet::activateWindow() { - showMaximized(); + showMaximized(); } void CEditorWorksheet::save() @@ -137,372 +148,377 @@ void CEditorWorksheet::save() void CEditorWorksheet::saveAs(QString filename) { - STRING_MANAGER::TWorksheet new_file, wk_file; - loadExcelSheet(current_file.toStdString(), wk_file, true); - // set columns - new_file.resize(new_file.size() + 1); - for(unsigned int i = 0; i < wk_file.ColCount; i++) - { - ucstring col_name = wk_file.getData(0, i); - new_file.insertColumn(new_file.ColCount); - new_file.setData(0, new_file.ColCount - 1, col_name); - } - // read all the rows from table - uint rowIdx; - uint colIdx = 0; - bool hasHashValue = false; - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - hasHashValue = true; - colIdx = 1; - } - for(int i = 0; i < table_editor->rowCount(); i++) - { - rowIdx = new_file.size(); - new_file.resize(new_file.size() + 1); - ucstring tvalue; - for(int j = 0; j < table_editor->columnCount(); j++) - { - QTableWidgetItem* item = table_editor->item(i, j); - tvalue.fromUtf8(std::string(item->text().toUtf8())); - new_file.setData(rowIdx, j + colIdx, tvalue); - } - } - if(hasHashValue) - { - // rewrite the hash codes - makeHashCode(wk_file, true); - } - ucstring s = prepareExcelSheet(new_file); - NLMISC::CI18N::writeTextFile(filename.toStdString(), s, false); - current_file = filename; - setCurrentFile(filename); + STRING_MANAGER::TWorksheet new_file, wk_file; + loadExcelSheet(current_file.toStdString(), wk_file, true); + // set columns + new_file.resize(new_file.size() + 1); + for(unsigned int i = 0; i < wk_file.ColCount; i++) + { + ucstring col_name = wk_file.getData(0, i); + new_file.insertColumn(new_file.ColCount); + new_file.setData(0, new_file.ColCount - 1, col_name); + } + // read all the rows from table + uint rowIdx; + uint colIdx = 0; + bool hasHashValue = false; + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + hasHashValue = true; + colIdx = 1; + } + for(int i = 0; i < table_editor->rowCount(); i++) + { + rowIdx = new_file.size(); + new_file.resize(new_file.size() + 1); + ucstring tvalue; + for(int j = 0; j < table_editor->columnCount(); j++) + { + QTableWidgetItem *item = table_editor->item(i, j); + tvalue.fromUtf8(std::string(item->text().toUtf8())); + new_file.setData(rowIdx, j + colIdx, tvalue); + } + } + if(hasHashValue) + { + // rewrite the hash codes + makeHashCode(wk_file, true); + } + ucstring s = prepareExcelSheet(new_file); + NLMISC::CI18N::writeTextFile(filename.toStdString(), s, false); + current_file = filename; + setCurrentFile(filename); } void CEditorWorksheet::insertRow() { - int last_row = table_editor->rowCount(); + int last_row = table_editor->rowCount(); current_stack->push(new CUndoWorksheetNewCommand(table_editor, last_row)); } void CEditorWorksheet::deleteRow() { - int selected_row = table_editor->currentRow(); - QMessageBox msgBox; - msgBox.setText(tr("The row will be deleted.")); - msgBox.setInformativeText(tr("Do you want to delete the selected row ?")); - msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); - msgBox.setDefaultButton(QMessageBox::No); - int ret = msgBox.exec(); - if(ret == QMessageBox::Yes) - { - current_stack->push(new CUndoWorksheetDeleteCommand(table_editor, selected_row)); - } + int selected_row = table_editor->currentRow(); + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Question); + msgBox.setText(tr("The row will be deleted.")); + msgBox.setInformativeText(tr("Do you want to delete the selected row ?")); + msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); + msgBox.setDefaultButton(QMessageBox::No); - table_editor->clearFocus(); - table_editor->clearSelection(); - return; + int ret = msgBox.exec(); + if(ret == QMessageBox::Yes) + { + current_stack->push(new CUndoWorksheetDeleteCommand(table_editor, selected_row)); + } + table_editor->clearFocus(); + table_editor->clearSelection(); + return; } -void CEditorWorksheet::worksheetEditorCellEntered(QTableWidgetItem * item) +void CEditorWorksheet::worksheetEditorCellEntered(QTableWidgetItem *item) { temp_content = item->text(); current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); } -void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem * item) +void CEditorWorksheet::worksheetEditorChanged(QTableWidgetItem *item) { if(temp_content != item->text()) { //current_stack->push(new CUndoWorksheetCommand(table_editor, item, temp_content)); } - if(!isWindowModified()) - setWindowModified(true); + if(!isWindowModified()) + setWindowModified(true); } void CEditorWorksheet::extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig) { - bool modified = false; + bool modified = false; QList new_items; - ExtractBotNames ebn; - ebn.setRequiredSettings(filters, level_design_path); - ebn.extractBotNamesFromPrimitives(ligoConfig); - // get SimpleNames - { - map SimpleNames = ebn.getSimpleNames(); - map::iterator it(SimpleNames.begin()), last(SimpleNames.end()); + ExtractBotNames ebn; + ebn.setRequiredSettings(filters, level_design_path); + ebn.extractBotNamesFromPrimitives(ligoConfig); + // get SimpleNames + { + map SimpleNames = ebn.getSimpleNames(); + map::iterator it(SimpleNames.begin()), last(SimpleNames.end()); + + for (; it != last; ++it) + { + QList search_results = table_editor->findItems(QString(it->first.c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + QList records; + records.push_back(QString(it->first.c_str())); + records.push_back(QString(it->first.c_str())); + records.push_back(QString(it->second.SheetName.c_str())); + insertTableRecords(records, new_items); + if(!modified) modified = true; + } + } + ebn.cleanSimpleNames(); + } + // get GenericNames + { + set GenericNames = ebn.getGenericNames(); + set::iterator it(GenericNames.begin()), last(GenericNames.end()); + for (; it != last; ++it) + { + string gnName = "gn_" + ebn.cleanupName(*it); + QList search_results = table_editor->findItems(QString((*it).c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + QList records; + records.push_back(QString((*it).c_str())); + records.push_back(QString(gnName.c_str())); + records.push_back(" "); + insertTableRecords(records, new_items); + if(!modified) modified = true; + } + } + ebn.cleanGenericNames(); + } + + current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); + if(modified) + { + setWindowModified(true); + table_editor->scrollToBottom(); + } - for (; it != last; ++it) - { - QList search_results = table_editor->findItems(QString(it->first.c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - QList records; - records.push_back(QString(it->first.c_str())); - records.push_back(QString(it->first.c_str())); - records.push_back(QString(it->second.SheetName.c_str())); - insertTableRecords(records, new_items); - if(!modified) modified = true; - } - } - ebn.cleanSimpleNames(); - } - // get GenericNames - { - set GenericNames = ebn.getGenericNames(); - set::iterator it(GenericNames.begin()), last(GenericNames.end()); - for (; it != last; ++it) - { - string gnName = "gn_" + ebn.cleanupName(*it); - QList search_results = table_editor->findItems(QString((*it).c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - QList records; - records.push_back(QString((*it).c_str())); - records.push_back(QString(gnName.c_str())); - records.push_back(" "); - insertTableRecords(records, new_items); - if(!modified) modified = true; - } - } - ebn.cleanGenericNames(); - } - - current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); - if(modified) - { - setWindowModified(true); - table_editor->scrollToBottom(); - } - } -void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordListBuilder& wordListBuilder) +void CEditorWorksheet::extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder) { - uint i; + uint i; // **** Load the excel sheet // load - TWorksheet workSheet; + STRING_MANAGER::TWorksheet workSheet; if(!loadExcelSheet(filename.toStdString(), workSheet, true)) { nlwarning("Error reading '%s'. Aborted", filename.toStdString().c_str()); return; } // get the key column index - uint keyColIndex = 0; + uint keyColIndex = 0; if(!workSheet.findCol(columnId.toStdString(), keyColIndex)) { nlwarning("Error: Don't find the column '%s'. '%s' Aborted", columnId.toStdString().c_str(), filename.toStdString().c_str()); return; } // get the name column index - uint nameColIndex; + uint nameColIndex; if(!workSheet.findCol(ucstring("name"), nameColIndex)) { nlwarning("Error: Don't find the column 'name'. '%s' Aborted", filename.toStdString().c_str()); return; - } - - // **** List all words with the builder given - std::vector allWords; - if(!wordListBuilder.buildWordList(allWords, filename.toStdString())) - { - return; - } - bool modified = false; - QList new_items; - for(i = 0; i < allWords.size(); i++) - { - string keyName = allWords[i]; - QList search_results = table_editor->findItems(QString(keyName.c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - int knPos = 0, nPos = 0; - if(workSheet.getData(0, 0) == ucstring("*HASH_VALUE")) - { - knPos = keyColIndex - 1; - nPos = nameColIndex - 1; - } else { - knPos = keyColIndex; - nPos = nameColIndex; - } + } - QList records; - records.push_back(QString(keyName.c_str())); - records.push_back(QString("") + QString(keyName.c_str())); - insertTableRecords(records, new_items); - if(!modified) modified = true; - } - } - current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); - if(modified) - { - setWindowModified(true); - table_editor->scrollToBottom(); - } + // **** List all words with the builder given + std::vector allWords; + if(!wordListBuilder.buildWordList(allWords, filename.toStdString())) + { + return; + } + bool modified = false; + QList new_items; + for(i = 0; i < allWords.size(); i++) + { + string keyName = allWords[i]; + QList search_results = table_editor->findItems(QString(keyName.c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + int knPos = 0, nPos = 0; + if(workSheet.getData(0, 0) == ucstring("*HASH_VALUE")) + { + knPos = keyColIndex - 1; + nPos = nameColIndex - 1; + } + else + { + knPos = keyColIndex; + nPos = nameColIndex; + } + + QList records; + records.push_back(QString(keyName.c_str())); + records.push_back(QString("") + QString(keyName.c_str())); + insertTableRecords(records, new_items); + if(!modified) modified = true; + } + } + current_stack->push(new CUndoWorksheetExtraction(new_items, table_editor)); + if(modified) + { + setWindowModified(true); + table_editor->scrollToBottom(); + } } void CEditorWorksheet::insertTableRecords(QList records, QList new_items) { - const int currentRow = table_editor->rowCount(); + const int currentRow = table_editor->rowCount(); table_editor->setRowCount(currentRow + 1); int n = 0; Q_FOREACH(QString record, records) { QTableWidgetItem *rec = new QTableWidgetItem(); rec->setBackgroundColor(QColor("#F75D59")); - table_editor ->setItem(currentRow, n, rec); + table_editor ->setItem(currentRow, n, rec); CTableWidgetItemStore rec_s(rec, currentRow, n); new_items.push_back(rec_s); n++; } - } bool CEditorWorksheet::compareWorksheetFile(QString filename) { - STRING_MANAGER::TWorksheet wk_file; - int colIndex = 0; - if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) - { - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - colIndex = 1; - } - if(wk_file.ColCount - colIndex != table_editor->columnCount()) - { - return false; - } - for(int i = 0; i < table_editor->columnCount(); i++) - { - QString item = table_editor->horizontalHeaderItem(i)->text(); - ucstring itemC = wk_file.getData(0, i+ colIndex); - if(item.toStdString() != itemC.toString()) - { - nlwarning(item.toStdString().c_str()); - nlwarning(itemC.toString().c_str()); - return false; - } - } - } else { - return false; - } - - return true; + STRING_MANAGER::TWorksheet wk_file; + int colIndex = 0; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + colIndex = 1; + } + if(wk_file.ColCount - colIndex != table_editor->columnCount()) + { + return false; + } + for(int i = 0; i < table_editor->columnCount(); i++) + { + QString item = table_editor->horizontalHeaderItem(i)->text(); + ucstring itemC = wk_file.getData(0, i+ colIndex); + if(item.toStdString() != itemC.toString()) + { + nlwarning(item.toStdString().c_str()); + nlwarning(itemC.toString().c_str()); + return false; + } + } + } + else + { + return false; + } + return true; } void CEditorWorksheet::mergeWorksheetFile(QString filename) { - STRING_MANAGER::TWorksheet wk_file; - if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) - { - bool hasHashValue = false; - int colIndex = 0; - if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) - { - hasHashValue = true; - colIndex = 1; - } - // read rows - for(unsigned int i = 1; i < wk_file.size(); i++) - { - // search with the first column - ucstring rowId = wk_file.getData(i,colIndex); - QList search_results = table_editor->findItems(QString(rowId.toString().c_str()), Qt::MatchExactly); - if(search_results.size() == 0) - { - const int lastRow = table_editor->rowCount(); - table_editor->setRowCount(lastRow + 1); - for(unsigned int j = 0; j < table_editor->columnCount(); j++) - { - ucstring rowValue = wk_file.getData(i, j + colIndex); // get the value - QTableWidgetItem *row = new QTableWidgetItem(); - row->setText(QString(rowValue.toString().c_str())); // set the value in table item - table_editor->setItem(lastRow, j, row); - } - } - } - } else { - QErrorMessage error; - error.showMessage(tr("This file is not a worksheet file.")); - error.exec(); - } + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { + bool hasHashValue = false; + int colIndex = 0; + if(wk_file.getData(0, 0) == ucstring("*HASH_VALUE")) + { + hasHashValue = true; + colIndex = 1; + } + // read rows + for(unsigned int i = 1; i < wk_file.size(); i++) + { + // search with the first column + ucstring rowId = wk_file.getData(i,colIndex); + QList search_results = table_editor->findItems(QString(rowId.toString().c_str()), Qt::MatchExactly); + if(search_results.size() == 0) + { + const int lastRow = table_editor->rowCount(); + table_editor->setRowCount(lastRow + 1); + for(int j = 0; j < table_editor->columnCount(); j++) + { + ucstring rowValue = wk_file.getData(i, j + colIndex); // get the value + QTableWidgetItem *row = new QTableWidgetItem(); + row->setText(QString(rowValue.toString().c_str())); // set the value in table item + table_editor->setItem(lastRow, j, row); + } + } + } + } + else + { + QErrorMessage error; + error.showMessage(tr("This file is not a worksheet file.")); + error.exec(); + } } void CEditorWorksheet::closeEvent(QCloseEvent *event) { - if(isWindowModified()) - { - QMessageBox msgBox; - msgBox.setText(tr("The document has been modified.")); - msgBox.setInformativeText(tr("Do you want to save your changes?")); - msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Save); - int ret = msgBox.exec(); - switch (ret) - { - case QMessageBox::Save: - save(); - event->accept(); - close(); - break; - case QMessageBox::Discard: - event->accept(); - close(); - break; - case QMessageBox::Cancel: - event->ignore(); - break; - default: - break; - } - } else { - event->accept(); - close(); - } + if(isWindowModified()) + { + QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Question); + msgBox.setText(tr("The document has been modified.")); + msgBox.setInformativeText(tr("Do you want to save your changes?")); + msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Save); + int ret = msgBox.exec(); + switch (ret) + { + case QMessageBox::Save: + save(); + break; + case QMessageBox::Discard: + break; + case QMessageBox::Cancel: + event->ignore(); + return; + } + } + event->accept(); + close(); } bool CEditorWorksheet::isBotNamesTable() { - bool status = true; - if(table_editor->horizontalHeaderItem(0)->text() != "bot name" - || table_editor->horizontalHeaderItem(1)->text() != "translated name" - || table_editor->horizontalHeaderItem(2)->text() != "sheet_name") - { - status = false; - } - - return status; + bool status = true; + if(table_editor->horizontalHeaderItem(0)->text() != "bot name" + || table_editor->horizontalHeaderItem(1)->text() != "translated name" + || table_editor->horizontalHeaderItem(2)->text() != "sheet_name") + { + status = false; + } + return status; } bool CEditorWorksheet::isSheetTable(QString type) { - QString column_name; - if(type.toAscii() == Constants::WK_ITEM) - { - column_name = "item ID"; - } else if(type.toAscii() == Constants::WK_CREATURE) { - column_name = "creature ID"; - } else if(type.toAscii() == Constants::WK_SBRICK) { - column_name = "sbrick ID"; - } else if(type.toAscii() == Constants::WK_SPHRASE) { - column_name = "sphrase ID"; - } else if(type.toAscii() == Constants::WK_PLACE) { - column_name = "placeId"; - } - bool status = true; - if(table_editor->horizontalHeaderItem(0)->text() != column_name - || table_editor->horizontalHeaderItem(1)->text() != "name") - { - status = false; - } - - return status; + QString column_name; + if(type.toAscii() == Constants::WK_ITEM) + { + column_name = "item ID"; + } + else if(type.toAscii() == Constants::WK_CREATURE) + { + column_name = "creature ID"; + } + else if(type.toAscii() == Constants::WK_SBRICK) + { + column_name = "sbrick ID"; + } + else if(type.toAscii() == Constants::WK_SPHRASE) + { + column_name = "sphrase ID"; + } + else if(type.toAscii() == Constants::WK_PLACE) + { + column_name = "placeId"; + } + bool status = true; + if(table_editor->horizontalHeaderItem(0)->text() != column_name + || table_editor->horizontalHeaderItem(1)->text() != "name") + { + status = false; + } + return status; } } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h index 487a9eea8..b60c7a534 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/editor_worksheet.h @@ -18,6 +18,10 @@ #ifndef EDITOR_WORKSHEET_H #define EDITOR_WORKSHEET_H +// Project includes +#include "translation_manager_editor.h" +#include "extract_new_sheet_names.h" + // Nel includes #include "nel/misc/types_nl.h" #include "nel/misc/sheet_id.h" @@ -34,11 +38,9 @@ #include #include -// Project includes -#include "translation_manager_editor.h" -#include "extract_new_sheet_names.h" -namespace TranslationManager { +namespace TranslationManager +{ struct CTableWidgetItemStore { @@ -47,6 +49,7 @@ public: m_item(item), m_row(row), m_column(column) { } + QTableWidgetItem *m_item; int m_row; int m_column; @@ -54,39 +57,41 @@ public: class CEditorWorksheet : public CEditor { - Q_OBJECT -private: - QString temp_content; + Q_OBJECT + public: - CEditorWorksheet(QMdiArea* parent) : CEditor(parent) {} - CEditorWorksheet() : CEditor() {} - QTableWidget* table_editor; - void open(QString filename); - void save(); - void saveAs(QString filename); - void activateWindow(); - void mergeWorksheetFile(QString filename); - bool compareWorksheetFile(QString filename); - void extractBotNames(list filters, string level_design_path, NLLIGO::CLigoConfig ligoConfig); - void extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder); + CEditorWorksheet(QMdiArea *parent) : CEditor(parent) {} + CEditorWorksheet() : CEditor() {} + QTableWidget *table_editor; + void open(QString filename); + void save(); + void saveAs(QString filename); + void activateWindow(); + void mergeWorksheetFile(QString filename); + bool compareWorksheetFile(QString filename); + void extractBotNames(std::list filters, std::string level_design_path, NLLIGO::CLigoConfig ligoConfig); + void extractWords(QString filename, QString columnId, IWordListBuilder &wordListBuilder); void insertTableRecords(QList records, QList new_items); - bool isBotNamesTable(); - bool isSheetTable(QString type); - void closeEvent(QCloseEvent *event); + bool isBotNamesTable(); + bool isSheetTable(QString type); + void closeEvent(QCloseEvent *event); + private Q_SLOTS: - void worksheetEditorCellEntered(QTableWidgetItem * item); - void worksheetEditorChanged(QTableWidgetItem * item); - void insertRow(); - void deleteRow(); + void worksheetEditorCellEntered(QTableWidgetItem *item); + void worksheetEditorChanged(QTableWidgetItem *item); + void insertRow(); + void deleteRow(); void contextMenuEvent(QContextMenuEvent *e); - + +private: + QString temp_content; }; class CUndoWorksheetCommand : public QUndoCommand { public: - CUndoWorksheetCommand(QTableWidget *table, QTableWidgetItem* item, const QString &ocontent, QUndoCommand *parent = 0) : QUndoCommand("Insert characters in cells", parent), m_table(table), m_item(item), m_ocontent(ocontent) - { + CUndoWorksheetCommand(QTableWidget *table, QTableWidgetItem *item, const QString &ocontent, QUndoCommand *parent = 0) : QUndoCommand("Insert characters in cells", parent), m_table(table), m_item(item), m_ocontent(ocontent) + { m_ccontent = m_ocontent; } @@ -98,16 +103,16 @@ public: } } void undo() - { + { if(m_item->text() != m_ocontent) { m_ccontent = m_item->text(); } m_item->setText(m_ocontent); - } + } private: - QTableWidget* m_table; - QTableWidgetItem* m_item; + QTableWidget *m_table; + QTableWidgetItem *m_item; QString m_ocontent; QString m_ccontent; }; @@ -123,7 +128,7 @@ public: m_table->setRowCount(m_rowID + 1); for(int j = 0; j < m_table->columnCount(); j++) { - QTableWidgetItem* item = new QTableWidgetItem(); + QTableWidgetItem *item = new QTableWidgetItem(); m_table->setItem(m_rowID, j, item); m_table->scrollToBottom(); } @@ -133,16 +138,16 @@ public: { m_table->removeRow(m_rowID); } -private: - QTableWidget* m_table; - int m_rowID; +private: + QTableWidget *m_table; + int m_rowID; }; class CUndoWorksheetExtraction : public QUndoCommand { public: - CUndoWorksheetExtraction(QList items, QTableWidget *table, QUndoCommand *parent = 0) : QUndoCommand("Word extraction", parent), + CUndoWorksheetExtraction(QList items, QTableWidget *table, QUndoCommand *parent = 0) : QUndoCommand("Word extraction", parent), m_items(items), m_table(table) { } @@ -153,7 +158,6 @@ public: { m_table->setItem(is.m_row, is.m_column, is.m_item); } - } void undo() @@ -161,14 +165,13 @@ public: Q_FOREACH(CTableWidgetItemStore is, m_items) { m_table->setItem(is.m_row, is.m_column, is.m_item); - m_table->takeItem(is.m_row, is.m_column); + m_table->takeItem(is.m_row, is.m_column); } - } private: QList m_items; - QTableWidget* m_table; + QTableWidget *m_table; }; class CUndoWorksheetDeleteCommand : public QUndoCommand @@ -181,8 +184,8 @@ public: { for(int i = 0; i < m_table->columnCount(); i++) { - QTableWidgetItem* item = new QTableWidgetItem(); - QTableWidgetItem* table_item = m_table->item(m_rowID, i); + QTableWidgetItem *item = new QTableWidgetItem(); + QTableWidgetItem *table_item = m_table->item(m_rowID, i); item->setText(table_item->text()); m_deletedItems.push_back(item); } @@ -203,8 +206,8 @@ public: } private: - QList m_deletedItems; - QTableWidget* m_table; + QList m_deletedItems; + QTableWidget *m_table; int m_rowID; }; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp index e8ed68bbf..ee65ca73c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.cpp @@ -17,18 +17,14 @@ #include "extract_bot_names.h" - static bool RemoveOlds = false; - - - namespace TranslationManager { TCreatureInfo *ExtractBotNames::getCreature(const std::string &sheetName) { - CSheetId id(sheetName+".creature"); + NLMISC::CSheetId id(sheetName+".creature"); if (Creatures.find(id) != Creatures.end()) return &(Creatures.find(id)->second); @@ -36,18 +32,17 @@ TCreatureInfo *ExtractBotNames::getCreature(const std::string &sheetName) return NULL; } -string ExtractBotNames::cleanupName(const std::string &name) +std::string ExtractBotNames::cleanupName(const std::string &name) { - string ret; + std::string ret; - for (uint i=0; i= 2) { - if ( *ret.begin() == ucchar('$')) + if (*ret.begin() == ucchar('$')) { ret=ret.substr(1); } - if ( *ret.rbegin() == ucchar('$')) + if (*ret.rbegin() == ucchar('$')) { - ret = ret.substr(0, ret.size()-1); + ret = ret.substr(0, ret.size() - 1); } } ret = cleanupUcName(ret); - return ret; + return ret; } - - - - -set ExtractBotNames::getGenericNames() +std::set ExtractBotNames::getGenericNames() { - return GenericNames; + return GenericNames; } -map ExtractBotNames::getSimpleNames() +std::map ExtractBotNames::getSimpleNames() { - return SimpleNames; + return SimpleNames; } void ExtractBotNames::cleanSimpleNames() -{ - SimpleNames.clear(); +{ + SimpleNames.clear(); } void ExtractBotNames::cleanGenericNames() { - GenericNames.clear(); + GenericNames.clear(); } -string ExtractBotNames::removeAndStoreFunction(const std::string &fullName) +std::string ExtractBotNames::removeAndStoreFunction(const std::string &fullName) { - string::size_type pos = fullName.find("$"); - if (pos == string::npos) + std::string::size_type pos = fullName.find("$"); + if (pos == std::string::npos) + { return fullName; + } else { // extract and store the function name - string ret; + std::string ret; ret = fullName.substr(0, pos); - string::size_type pos2 = fullName.find("$", pos+1); + std::string::size_type pos2 = fullName.find("$", pos+1); - string fct = fullName.substr(pos+1, pos2-(pos+1)); + std::string fct = fullName.substr(pos + 1, pos2 - (pos + 1)); - ret += fullName.substr(pos2+1); + ret += fullName.substr(pos2 + 1); if (Functions.find(fct) == Functions.end()) { nldebug("Adding function '%s'", fct.c_str()); Functions.insert(fct); } - return ret; } } - void ExtractBotNames::addGenericName(const std::string &name, const std::string &sheetName) { TCreatureInfo *c = getCreature(sheetName); if (!c || c->ForceSheetName || !c->DisplayName) return; - + if (SimpleNames.find(name) != SimpleNames.end()) { nldebug("Name '%s' is now a generic name", name.c_str()); @@ -177,7 +167,7 @@ void ExtractBotNames::addSimpleName(const std::string &name, const std::string & else { nldebug("Adding simple name '%s'", name.c_str()); - + TEntryInfo ei; ei.SheetName = sheetName; @@ -185,75 +175,72 @@ void ExtractBotNames::addSimpleName(const std::string &name, const std::string & } } -void ExtractBotNames::setRequiredSettings(list filters, string level_design_path) +void ExtractBotNames::setRequiredSettings(std::list filters, std::string level_design_path) { - for (std::list::iterator it = filters.begin(); it != filters.end(); ++it) + for (std::list::iterator it = filters.begin(); it != filters.end(); ++it) { Filters.push_back(*it); } //------------------------------------------------------------------- // init the sheets - CSheetId::init(false); - const string PACKED_SHEETS_NAME = "bin/translation_tools_creature.packed_sheets"; + NLMISC::CSheetId::init(false); + const std::string PACKED_SHEETS_NAME = "bin/translation_tools_creature.packed_sheets"; loadForm("creature", PACKED_SHEETS_NAME, Creatures, false, false); if (Creatures.empty()) { - loadForm("creature", PACKED_SHEETS_NAME, Creatures, true); + loadForm("creature", PACKED_SHEETS_NAME, Creatures, true); } - } -void ExtractBotNames::extractBotNamesFromPrimitives(CLigoConfig ligoConfig) +void ExtractBotNames::extractBotNamesFromPrimitives(NLLIGO::CLigoConfig ligoConfig) { - //------------------------------------------------------------------- // ok, ready for the real work, // first, read the primitives files and parse the primitives - vector files; - CPath::getFileList("primitive", files); - + std::vector files; + NLMISC::CPath::getFileList("primitive", files); for (uint i=0; i ps; + NLLIGO::CPrimitiveSet ps; ps.buildSet(primDoc.RootNode, pred, result); - for (uint i=0; igetPropertyByName("name", name); result[i]->getPropertyByName("count", countStr); result[i]->getPropertyByName("bot_sheet_look", sheetStr); @@ -276,16 +263,16 @@ void ExtractBotNames::extractBotNamesFromPrimitives(CLigoConfig ligoConfig) } // look for bot template { - TPrimitiveClassPredicate pred("bot_template_npc"); - TPrimitiveSet result; + NLLIGO::TPrimitiveClassPredicate pred("bot_template_npc"); + NLLIGO::TPrimitiveSet result; - CPrimitiveSet ps; + NLLIGO::CPrimitiveSet ps; ps.buildSet(primDoc.RootNode, pred, result); - for (uint i=0; igetPropertyByName("name", name); result[i]->getPropertyByName("sheet_look", sheetStr); @@ -305,19 +292,19 @@ void ExtractBotNames::extractBotNamesFromPrimitives(CLigoConfig ligoConfig) } } } - // look for npc_group + // look for npc_group { - TPrimitiveClassPredicate pred("npc_group"); - TPrimitiveSet result; + NLLIGO::TPrimitiveClassPredicate pred("npc_group"); + NLLIGO::TPrimitiveSet result; - CPrimitiveSet ps; + NLLIGO::CPrimitiveSet ps; ps.buildSet(primDoc.RootNode, pred, result); - for (uint i=0; igetPropertyByName("name", name); result[i]->getPropertyByName("count", countStr); result[i]->getPropertyByName("bot_sheet_client", sheetStr); @@ -342,18 +329,18 @@ void ExtractBotNames::extractBotNamesFromPrimitives(CLigoConfig ligoConfig) } } } - // look for bot + // look for bot { - TPrimitiveClassPredicate pred("npc_bot"); - TPrimitiveSet result; + NLLIGO::TPrimitiveClassPredicate pred("npc_bot"); + NLLIGO::TPrimitiveSet result; - CPrimitiveSet ps; + NLLIGO::CPrimitiveSet ps; ps.buildSet(primDoc.RootNode, pred, result); - for (uint i=0; igetPropertyByName("name", name); result[i]->getPropertyByName("sheet_client", sheetStr); @@ -373,7 +360,7 @@ void ExtractBotNames::extractBotNamesFromPrimitives(CLigoConfig ligoConfig) } } } - } + } } - + } \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h index df1cb39ca..208b4db5c 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_bot_names.h @@ -30,22 +30,16 @@ #include "nel/ligo/primitive.h" #include "nel/ligo/primitive_utils.h" -using namespace std; -using namespace NLMISC; -using namespace NLLIGO; -using namespace STRING_MANAGER; - namespace TranslationManager { struct TCreatureInfo { - CSheetId SheetId; - bool ForceSheetName; - bool DisplayName; + NLMISC::CSheetId SheetId; + bool ForceSheetName; + bool DisplayName; - - void readGeorges (const NLMISC::CSmartPtr &form, const NLMISC::CSheetId &sheetId) + void readGeorges(const NLMISC::CSmartPtr &form, const NLMISC::CSheetId &sheetId) { const NLGEORGES::UFormElm &item=form->getRootNode(); @@ -61,51 +55,45 @@ struct TCreatureInfo f.serial(DisplayName); } - - static uint getVersion () - { + static uint getVersion () + { return 1; } void removed() { } - }; struct TEntryInfo { - string SheetName; + std::string SheetName; }; struct ExtractBotNames { private: - vector Filters; - std::map Creatures; - set GenericNames; - map SimpleNames; - set Functions; + std::vector Filters; + std::map Creatures; + std::set GenericNames; + std::map SimpleNames; + std::set Functions; private: - TCreatureInfo *getCreature(const std::string &sheetName); - ucstring makeGroupName(const ucstring & translationName); - string removeAndStoreFunction(const std::string &fullName); - void addGenericName(const std::string &name, const std::string &sheetName); - void addSimpleName(const std::string &name, const std::string &sheetName); + TCreatureInfo *getCreature(const std::string &sheetName); + ucstring makeGroupName(const ucstring &translationName); + std::string removeAndStoreFunction(const std::string &fullName); + void addGenericName(const std::string &name, const std::string &sheetName); + void addSimpleName(const std::string &name, const std::string &sheetName); public: - void extractBotNamesFromPrimitives(CLigoConfig ligoConfig); - void setRequiredSettings(list filters, string level_design_path); - set getGenericNames(); - map getSimpleNames(); - string cleanupName(const std::string &name); - ucstring cleanupUcName(const ucstring &name); - void cleanSimpleNames(); - void cleanGenericNames(); - + void extractBotNamesFromPrimitives(NLLIGO::CLigoConfig ligoConfig); + void setRequiredSettings(std::list filters, std::string level_design_path); + std::set getGenericNames(); + std::map getSimpleNames(); + std::string cleanupName(const std::string &name); + ucstring cleanupUcName(const ucstring &name); + void cleanSimpleNames(); + void cleanGenericNames(); }; - } - -#endif /* EXTRACT_BOT_NAMES_H */ - +#endif /* EXTRACT_BOT_NAMES_H */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp index 5d0b9b455..984f86d17 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.cpp @@ -16,139 +16,135 @@ #include "extract_new_sheet_names.h" -using namespace std; -using namespace NLMISC; -using namespace NLLIGO; -using namespace STRING_MANAGER; - -namespace TranslationManager +namespace TranslationManager { - - // *************************************************************************** /* * Specialisation of IWordListBuilder to list sheets in a directory */ - -bool CSheetWordListBuilder::buildWordList(std::vector &allWords, string workSheetFileName) +bool CSheetWordListBuilder::buildWordList(std::vector &allWords, std::string workSheetFileName) +{ + SheetExt = NLMISC::toLower(SheetExt); + // verify the directory is correct + if(!NLMISC::CFile::isDirectory(SheetPath)) { - SheetExt= toLower(SheetExt); - // verify the directory is correct - if(!CFile::isDirectory(SheetPath)) - { - nlwarning("Error: Directory '%s' not found. '%s' Aborted", SheetPath.c_str(), workSheetFileName.c_str()); - return false; - } - - // list all files. - std::vector allFiles; - allFiles.reserve(100000); - CPath::getPathContent(SheetPath, true, false, true, allFiles, NULL); - - // Keep only the extension we want, and remove "_" (parent) - allWords.clear(); - allWords.reserve(allFiles.size()); - for(uint i=0;i allFiles; + allFiles.reserve(100000); + NLMISC::CPath::getPathContent(SheetPath, true, false, true, allFiles, NULL); + // Keep only the extension we want, and remove "_" (parent) + allWords.clear(); + allWords.reserve(allFiles.size()); + for(size_t i = 0; i < allFiles.size(); i++) + { + std::string fileNameWithoutExt = NLMISC::CFile::getFilenameWithoutExtension(allFiles[i]); + std::string extension = NLMISC::toLower(NLMISC::CFile::getExtension(allFiles[i])); + + // bad extension? + if(extension!=SheetExt) + continue; + + // parent? + if(fileNameWithoutExt.empty() || fileNameWithoutExt[0] == '_') + continue; + + // ok, add + allWords.push_back(NLMISC::toLower(fileNameWithoutExt)); + } + return true; +} // *************************************************************************** /* * Specialisation of IWordListBuilder to list new region/place name from .primitive */ -bool CRegionPrimWordListBuilder::buildWordList(std::vector &allWords, string workSheetFileName) +bool CRegionPrimWordListBuilder::buildWordList(std::vector &allWords, std::string workSheetFileName) +{ + // verify the directory is correct + if(!NLMISC::CFile::isDirectory(PrimPath)) { - // verify the directory is correct - if(!CFile::isDirectory(PrimPath)) + nlwarning("Error: Directory '%s' not found. '%s' Aborted", PrimPath.c_str(), workSheetFileName.c_str()); + return false; + } + + // list all files. + std::vector allFiles; + allFiles.reserve(100000); + NLMISC::CPath::getPathContent(PrimPath, true, false, true, allFiles, NULL); + + // parse all primitive that match the filter + allWords.clear(); + allWords.reserve(100000); + // to avoid duplicate + std::set allWordSet; + for(size_t i = 0; i < allFiles.size(); i++) + { + std::string fileName = NLMISC::CFile::getFilename(allFiles[i]); + // filter don't match? + bool oneMatch= false; + for(size_t filter = 0; filter < PrimFilter.size(); filter++) { - nlwarning("Error: Directory '%s' not found. '%s' Aborted", PrimPath.c_str(), workSheetFileName.c_str()); + if(NLMISC::testWildCard(fileName, PrimFilter[filter])) + oneMatch= true; + } + if(!oneMatch) + continue; + + // ok, read the file + NLLIGO::CPrimitives PrimDoc; + NLLIGO::CPrimitiveContext::instance().CurrentPrimitive = &PrimDoc; + if (!NLLIGO::loadXmlPrimitiveFile(PrimDoc, allFiles[i], LigoConfig)) + { + nlwarning("Error: cannot open file '%s'. '%s' Aborted", allFiles[i].c_str(), workSheetFileName.c_str()); + NLLIGO::CPrimitiveContext::instance().CurrentPrimitive = NULL; return false; } - - // list all files. - std::vector allFiles; - allFiles.reserve(100000); - CPath::getPathContent(PrimPath, true, false, true, allFiles, NULL); - - // parse all primitive that match the filter - allWords.clear(); - allWords.reserve(100000); - // to avoid duplicate - set allWordSet; - for(uint i=0;i setPlace; + NLLIGO::TPrimitiveSet placeRes; + setPlace.buildSet(PrimDoc.RootNode, predCont, placeRes); + + // for all found + for (size_t placeId = 0; placeId < placeRes.size(); ++placeId) { - nlwarning("Error: cannot open file '%s'. '%s' Aborted", allFiles[i].c_str(), workSheetFileName.c_str()); - CPrimitiveContext::instance().CurrentPrimitive = NULL; - return false; - } - CPrimitiveContext::instance().CurrentPrimitive = NULL; - - // For all primitives of interest - const char *listClass[]= {"continent", "region", "place", "stable", - "teleport_destination", "room_template"}; - const char *listProp[]= {"name", "name", "name", "name", - "place_name", "place_name"}; - const uint numListClass= sizeof(listClass)/sizeof(listClass[0]); - const uint numListProp= sizeof(listProp)/sizeof(listProp[0]); - nlctassert(numListProp==numListClass); - for(uint cid=0;cid setPlace; - TPrimitiveSet placeRes; - setPlace.buildSet(PrimDoc.RootNode, predCont, placeRes); - // for all found - for (uint placeId= 0; placeId < placeRes.size(); ++placeId) + std::string primName; + if(placeRes[placeId]->getPropertyByName(listProp[cid], primName) && !primName.empty()) { - string primName; - if(placeRes[placeId]->getPropertyByName(listProp[cid], primName) && !primName.empty()) + primName = NLMISC::toLower(primName); + // avoid duplicate + if(allWordSet.insert(primName).second) { - primName= toLower(primName); - // avoid duplicate - if(allWordSet.insert(primName).second) - { - allWords.push_back(primName); - } + allWords.push_back(primName); } } } } - - return true; } + return true; +} } \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h index ca7295f91..01d0f1a0a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/extract_new_sheet_names.h @@ -30,43 +30,34 @@ #include "nel/ligo/primitive.h" #include "nel/ligo/primitive_utils.h" -using namespace std; -using namespace NLMISC; -using namespace NLLIGO; -using namespace STRING_MANAGER; - -namespace TranslationManager +namespace TranslationManager { - // *************************************************************************** /* * Interface to build the whole list of words (key id) for a specific worksheet */ struct IWordListBuilder { - virtual bool buildWordList(std::vector &allWords, string workSheetFileName) =0; - + virtual bool buildWordList(std::vector &allWords, std::string workSheetFileName) =0; }; struct CSheetWordListBuilder : public IWordListBuilder { - string SheetExt; - string SheetPath; + std::string SheetExt; + std::string SheetPath; - virtual bool buildWordList(std::vector &allWords, string workSheetFileName); + virtual bool buildWordList(std::vector &allWords, std::string workSheetFileName); }; struct CRegionPrimWordListBuilder : public IWordListBuilder { - string PrimPath; - vector PrimFilter; - NLLIGO::CLigoConfig LigoConfig; - virtual bool buildWordList(std::vector &allWords, string workSheetFileName); + std::string PrimPath; + std::vector PrimFilter; + NLLIGO::CLigoConfig LigoConfig; + virtual bool buildWordList(std::vector &allWords, std::string workSheetFileName); }; } - -#endif /* EXTRACT_NEW_SHEET_NAMES_H */ - +#endif /* EXTRACT_NEW_SHEET_NAMES_H */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp index 41a8072e2..876599b61 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.cpp @@ -1,191 +1,216 @@ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . #include "ftp_selection.h" #include #include + namespace TranslationManager { - CFtpSelection::CFtpSelection(QWidget *parent): QDialog(parent) - { - _ui.setupUi(this); - connect(_ui.connectButton, SIGNAL(clicked()), this, SLOT(ConnectButtonClicked())); - connect(_ui.doneButton, SIGNAL(clicked()), this, SLOT(DoneButtonClicked())); - connect(_ui.cdToParrent, SIGNAL(clicked()), this, SLOT(cdToParent())); - connect(_ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); - - // file list - connect(_ui.fileList, SIGNAL(itemActivated(QTreeWidgetItem*,int)),this, SLOT(processItem(QTreeWidgetItem*,int))); - _ui.fileList->setEnabled(false); - _ui.fileList->setRootIsDecorated(false); - _ui.fileList->setHeaderLabels(QStringList() << tr("Name") << tr("Size") << tr("Owner") << tr("Group") << tr("Time")); - _ui.fileList->header()->setStretchLastSection(false); +CFtpSelection::CFtpSelection(QWidget *parent): QDialog(parent) +{ + _ui.setupUi(this); + connect(_ui.connectButton, SIGNAL(clicked()), this, SLOT(ConnectButtonClicked())); + connect(_ui.doneButton, SIGNAL(clicked()), this, SLOT(DoneButtonClicked())); + connect(_ui.cdToParrent, SIGNAL(clicked()), this, SLOT(cdToParent())); + connect(_ui.cancelButton, SIGNAL(clicked()), this, SLOT(reject())); - // buttons - _ui.cdToParrent->setEnabled(false); - _ui.doneButton->setEnabled(false); + // file list + connect(_ui.fileList, SIGNAL(itemActivated(QTreeWidgetItem *,int)),this, SLOT(processItem(QTreeWidgetItem *,int))); + _ui.fileList->setEnabled(false); + _ui.fileList->setRootIsDecorated(false); + _ui.fileList->setHeaderLabels(QStringList() << tr("Name") << tr("Size") << tr("Owner") << tr("Group") << tr("Time")); + _ui.fileList->header()->setStretchLastSection(false); - status = false; - } - - // Connection with the FTP Server. We retrieve the file list. - void CFtpSelection::ConnectButtonClicked() - { - conn = new QFtp(this); - connect(conn, SIGNAL(commandFinished(int,bool)), this, SLOT(FtpCommandFinished(int,bool))); - connect(conn, SIGNAL(listInfo(QUrlInfo)), this, SLOT(AddToList(QUrlInfo))); - #ifndef QT_NO_CURSOR - setCursor(Qt::WaitCursor); - #endif - QUrl url(_ui.url->text()); - if (!url.isValid() || url.scheme().toLower() != QLatin1String("ftp")) { - conn->connectToHost(_ui.url->text(), 21); - conn->login(); - } else { - conn->connectToHost(url.host(), url.port(21)); + // buttons + _ui.cdToParrent->setEnabled(false); + _ui.doneButton->setEnabled(false); - if (!url.userName().isEmpty()) - conn->login(QUrl::fromPercentEncoding(url.userName().toLatin1()), url.password()); - else - conn->login(); - if (!url.path().isEmpty()) - conn->cd(url.path()); - } - } - - // Get the user action. - void CFtpSelection::FtpCommandFinished(int, bool error) - { - #ifndef QT_NO_CURSOR - setCursor(Qt::ArrowCursor); - #endif - if (conn->currentCommand() == QFtp::ConnectToHost) - { - if (error) - { - QMessageBox::information(this, tr("FTP"), - tr("Unable to connect to the FTP server " - "at %1. Please check that the host " - "name is correct.") - .arg(_ui.url->text())); - return; - } + status = false; +} - return; - } - - if (conn->currentCommand() == QFtp::Login) - { - conn->list(); - } - - if (conn->currentCommand() == QFtp::Get) - { - if(error) - { - status = false; - file->close(); - file->remove(); - } else { - file->close(); - status = true; - } - _ui.cancelButton->setEnabled(true); - } +// Connection with the FTP Server. We retrieve the file list. +void CFtpSelection::ConnectButtonClicked() +{ + conn = new QFtp(this); + connect(conn, SIGNAL(commandFinished(int,bool)), this, SLOT(FtpCommandFinished(int,bool))); + connect(conn, SIGNAL(listInfo(QUrlInfo)), this, SLOT(AddToList(QUrlInfo))); - if (conn->currentCommand() == QFtp::List) - { - if (isDirectory.isEmpty()) { - _ui.fileList->addTopLevelItem(new QTreeWidgetItem(QStringList() << tr(""))); - _ui.fileList->setEnabled(false); - } - } - } - // Make the file list with directories and files - void CFtpSelection::AddToList(const QUrlInfo &urlInfo) - { - QTreeWidgetItem *item = new QTreeWidgetItem; - item->setText(0, urlInfo.name()); - item->setText(1, QString::number(urlInfo.size())); - item->setText(2, urlInfo.owner()); - item->setText(3, urlInfo.group()); - item->setText(4, urlInfo.lastModified().toString("MMM dd yyyy")); + setCursor(Qt::WaitCursor); - QPixmap pixmap(urlInfo.isDir() ? ":/translationManager/images/dir.png" : ":/translationManager/images/file.png"); - item->setIcon(0, pixmap); - - isDirectory[urlInfo.name()] = urlInfo.isDir(); - _ui.fileList->addTopLevelItem(item); - if (!_ui.fileList->currentItem()) { - _ui.fileList->setCurrentItem(_ui.fileList->topLevelItem(0)); - _ui.fileList->setEnabled(true); - } - } - - void CFtpSelection::processItem(QTreeWidgetItem* item, int) - { - QString name = item->text(0); - if (isDirectory.value(name)) - { - _ui.fileList->clear(); - isDirectory.clear(); - currentPath += '/'; - currentPath += name; - conn->cd(name); - conn->list(); - #ifndef QT_NO_CURSOR - setCursor(Qt::WaitCursor); - #endif - return; - } - _ui.doneButton->setEnabled(true); - } - - // Exit from a directory - void CFtpSelection::cdToParent() + QUrl url(_ui.url->text()); + if (!url.isValid() || url.scheme().toLower() != QLatin1String("ftp")) { - #ifndef QT_NO_CURSOR - setCursor(Qt::WaitCursor); - #endif - _ui.fileList->clear(); - isDirectory.clear(); - currentPath = currentPath.left(currentPath.lastIndexOf('/')); - if (currentPath.isEmpty()) { - _ui.cdToParrent->setEnabled(false); - conn->cd("/"); - } else { - conn->cd(currentPath); - } - conn->list(); + conn->connectToHost(_ui.url->text(), 21); + conn->login(); + } + else + { + conn->connectToHost(url.host(), url.port(21)); + + if (!url.userName().isEmpty()) + conn->login(QUrl::fromPercentEncoding(url.userName().toLatin1()), url.password()); + else + conn->login(); + if (!url.path().isEmpty()) + conn->cd(url.path()); + } +} + +// Get the user action. +void CFtpSelection::FtpCommandFinished(int, bool error) +{ + setCursor(Qt::ArrowCursor); + + if (conn->currentCommand() == QFtp::ConnectToHost) + { + if (error) + { + QMessageBox::information(this, tr("FTP"), + tr("Unable to connect to the FTP server " + "at %1. Please check that the host " + "name is correct.") + .arg(_ui.url->text())); + return; + } + + return; } - // Done action - void CFtpSelection::DoneButtonClicked() - { - QString fileName = _ui.fileList->currentItem()->text(0); - - if (QFile::exists(fileName)) { - QMessageBox::information(this, tr("FTP"), - tr("There already exists a file called %1 in " - "the current directory.") - .arg(fileName)); - return; - } - - file = new QFile(fileName); - #ifndef QT_NO_CURSOR - setCursor(Qt::WaitCursor); - #endif - if (!file->open(QIODevice::WriteOnly)) { - QMessageBox::information(this, tr("FTP"), - tr("Unable to save the file %1: %2.") - .arg(fileName).arg(file->errorString())); - delete file; - return; - } - _ui.cancelButton->setEnabled(false); - conn->get(_ui.fileList->currentItem()->text(0), file); - - reject(); - } - + if (conn->currentCommand() == QFtp::Login) + { + conn->list(); + } + + if (conn->currentCommand() == QFtp::Get) + { + if(error) + { + status = false; + file->close(); + file->remove(); + } + else + { + file->close(); + status = true; + } + _ui.cancelButton->setEnabled(true); + } + + if (conn->currentCommand() == QFtp::List) + { + if (isDirectory.isEmpty()) + { + _ui.fileList->addTopLevelItem(new QTreeWidgetItem(QStringList() << tr(""))); + _ui.fileList->setEnabled(false); + } + } +} +// Make the file list with directories and files +void CFtpSelection::AddToList(const QUrlInfo &urlInfo) +{ + QTreeWidgetItem *item = new QTreeWidgetItem; + item->setText(0, urlInfo.name()); + item->setText(1, QString::number(urlInfo.size())); + item->setText(2, urlInfo.owner()); + item->setText(3, urlInfo.group()); + item->setText(4, urlInfo.lastModified().toString("MMM dd yyyy")); + + QPixmap pixmap(urlInfo.isDir() ? ":/translationManager/images/dir.png" : ":/translationManager/images/file.png"); + item->setIcon(0, pixmap); + + isDirectory[urlInfo.name()] = urlInfo.isDir(); + _ui.fileList->addTopLevelItem(item); + if (!_ui.fileList->currentItem()) + { + _ui.fileList->setCurrentItem(_ui.fileList->topLevelItem(0)); + _ui.fileList->setEnabled(true); + } +} + +void CFtpSelection::processItem(QTreeWidgetItem *item, int) +{ + QString name = item->text(0); + if (isDirectory.value(name)) + { + _ui.fileList->clear(); + isDirectory.clear(); + currentPath += '/'; + currentPath += name; + conn->cd(name); + conn->list(); + + setCursor(Qt::WaitCursor); + return; + } + _ui.doneButton->setEnabled(true); +} + +// Exit from a directory +void CFtpSelection::cdToParent() +{ + setCursor(Qt::WaitCursor); + + _ui.fileList->clear(); + isDirectory.clear(); + currentPath = currentPath.left(currentPath.lastIndexOf('/')); + if (currentPath.isEmpty()) + { + _ui.cdToParrent->setEnabled(false); + conn->cd("/"); + } + else + { + conn->cd(currentPath); + } + conn->list(); +} + +// Done action +void CFtpSelection::DoneButtonClicked() +{ + QString fileName = _ui.fileList->currentItem()->text(0); + + if (QFile::exists(fileName)) + { + QMessageBox::information(this, tr("FTP"), + tr("There already exists a file called %1 in " + "the current directory.") + .arg(fileName)); + return; + } + + file = new QFile(fileName); + + setCursor(Qt::WaitCursor); + + if (!file->open(QIODevice::WriteOnly)) + { + QMessageBox::information(this, tr("FTP"), + tr("Unable to save the file %1: %2.") + .arg(fileName).arg(file->errorString())); + delete file; + return; + } + _ui.cancelButton->setEnabled(false); + conn->get(_ui.fileList->currentItem()->text(0), file); + + reject(); +} + } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h index 47dcfdb57..9f8af85dd 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/ftp_selection.h @@ -1,4 +1,4 @@ -/* +/* * File: ftp_selection.h * Author: cemycc * @@ -8,6 +8,8 @@ #ifndef FTP_SELECTION_H #define FTP_SELECTION_H +#include "ui_ftp_selection.h" + #include #include #include @@ -16,34 +18,33 @@ #include #include -#include "ui_ftp_selection.h" +namespace TranslationManager +{ -using namespace std; +class CFtpSelection : public QDialog +{ + Q_OBJECT -namespace TranslationManager { - - class CFtpSelection : public QDialog - { - Q_OBJECT - private: - Ui::FtpSelectionDialog _ui; - QFtp *conn; - QHash isDirectory; - QString currentPath; - private Q_SLOTS: - void cdToParent(); - void processItem(QTreeWidgetItem*,int); - void ConnectButtonClicked(); - void DoneButtonClicked(); - void FtpCommandFinished(int, bool error); - void AddToList(const QUrlInfo &urlInfo); - public: - bool status; - QFile *file; - CFtpSelection(QWidget* parent = 0); - ~CFtpSelection() {} - }; +public: + CFtpSelection(QWidget *parent = 0); + ~CFtpSelection() {} + bool status; + QFile *file; + +private Q_SLOTS: + void cdToParent(); + void processItem(QTreeWidgetItem *,int); + void ConnectButtonClicked(); + void DoneButtonClicked(); + void FtpCommandFinished(int, bool error); + void AddToList(const QUrlInfo &urlInfo); + +private: + Ui::FtpSelectionDialog _ui; + QFtp *conn; + QHash isDirectory; + QString currentPath; +}; } -#endif /* FTP_SELECTION_H */ - +#endif /* FTP_SELECTION_H */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp index 3015b447d..518906db0 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.cpp @@ -1,39 +1,54 @@ - -#include +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2010 Winch Gate Property Limited +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . #include "source_selection.h" +#include + namespace TranslationManager { - CSourceDialog::CSourceDialog(QWidget *parent): QDialog(parent) { _ui.setupUi(this); - // Set signal and slot for "OK Button" + connect(_ui.ok_button, SIGNAL(clicked()), this, SLOT(OkButtonClicked())); - // Set signal and slot for "Cancel Button" - connect(_ui.cancel_button, SIGNAL(clicked()), this, SLOT(reject())); - _ui.sourceSelectionListWidget->setSortingEnabled(false); + connect(_ui.cancel_button, SIGNAL(clicked()), this, SLOT(reject())); + + _ui.sourceSelectionListWidget->setSortingEnabled(false); connect(_ui.sourceSelectionListWidget, SIGNAL(itemDoubleClicked(QListWidgetItem *)), - this, SLOT(itemDoubleClicked(QListWidgetItem *))); + this, SLOT(itemDoubleClicked(QListWidgetItem *))); } // Insert options in the source dialog. Options like: from FTP Server, from Local directory etc. -void CSourceDialog::setSourceOptions(map options) +void CSourceDialog::setSourceOptions(std::map &options) { - map::iterator it; - - for(it = options.begin(); it != options.end(); ++it) - { - _ui.sourceSelectionListWidget->addItem((*it).first); - } + std::map::iterator it; + + for(it = options.begin(); it != options.end(); ++it) + { + _ui.sourceSelectionListWidget->addItem((*it).first); + } } void CSourceDialog::OkButtonClicked() { - selected_item = _ui.sourceSelectionListWidget->currentItem(); - accept(); + selected_item = _ui.sourceSelectionListWidget->currentItem(); + accept(); } void CSourceDialog::itemDoubleClicked(QListWidgetItem *item) @@ -42,4 +57,4 @@ void CSourceDialog::itemDoubleClicked(QListWidgetItem *item) accept(); } -} +} \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h index d32bfcb02..7b6fc2cb9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/source_selection.h @@ -1,16 +1,30 @@ - +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . #ifndef SOURCE_SELECTION_H #define SOURCE_SELECTION_H +#include "ui_source_selection.h" + #include #include #include #include -#include "ui_source_selection.h" -#include -using namespace std; +#include namespace TranslationManager { @@ -18,16 +32,19 @@ namespace TranslationManager class CSourceDialog : public QDialog { Q_OBJECT -private: - Ui::SourceSelectionDialog _ui; + +public: + CSourceDialog(QWidget *parent = 0); + ~CSourceDialog() {} + void setSourceOptions(std::map &options); + QListWidgetItem *selected_item; + private Q_SLOTS: void OkButtonClicked(); void itemDoubleClicked(QListWidgetItem *item); -public: - CSourceDialog(QWidget *parent = 0); - ~CSourceDialog(){} - void setSourceOptions(map options); - QListWidgetItem *selected_item; + +private: + Ui::SourceSelectionDialog _ui; }; } diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h index dcb91676b..d72a2b223 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_constants.h @@ -1,29 +1,38 @@ -/* - * File: translation_manager_constants.h - * Author: cemycc - * - * Created on July 5, 2011, 9:15 PM - */ +// Translation Manager Plugin - OVQT Plugin +// Copyright (C) 2011 Emanuel Costea +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . #ifndef TRANSLATION_MANAGER_CONSTANTS_H #define TRANSLATION_MANAGER_CONSTANTS_H namespace TranslationManager { - namespace Constants - { - const int ED_SHEET = 1; - const int ED_PHRASE = 2; +namespace Constants +{ +const int ED_SHEET = 1; +const int ED_PHRASE = 2; - const char * const WK_BOTNAMES = "bot_names_wk.txt"; - const char * const WK_ITEM = "item_words_wk.txt"; - const char * const WK_CREATURE = "creature_words_wk.txt"; - const char * const WK_SBRICK = "sbrick_words_wk.txt"; - const char * const WK_SPHRASE = "sphrase_words_wk.txt"; - const char * const WK_PLACE = "place_words_wk.txt"; - const char * const WK_CONTINENT = "place_words_wk.txt"; - const char * const WK_STABLE = "place_words_wk.txt"; - } +const char *const WK_BOTNAMES = "bot_names_wk.txt"; +const char *const WK_ITEM = "item_words_wk.txt"; +const char *const WK_CREATURE = "creature_words_wk.txt"; +const char *const WK_SBRICK = "sbrick_words_wk.txt"; +const char *const WK_SPHRASE = "sphrase_words_wk.txt"; +const char *const WK_PLACE = "place_words_wk.txt"; +const char *const WK_CONTINENT = "place_words_wk.txt"; +const char *const WK_STABLE = "place_words_wk.txt"; +} } #endif /* TRANSLATION_MANAGER_CONSTANTS_H */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h index bc46769c1..fd53ba863 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_editor.h @@ -1,5 +1,4 @@ // Translation Manager Plugin - OVQT Plugin -// Copyright (C) 2010 Winch Gate Property Limited // Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify @@ -25,31 +24,30 @@ #include #include -namespace TranslationManager { - -class CEditor : public QMdiSubWindow { -Q_OBJECT -protected: - QUndoStack* current_stack; - QString current_file; - int editor_type; -public: - CEditor(QMdiArea* parent) : QMdiSubWindow(parent) {} - CEditor() : QMdiSubWindow() {} - virtual void open(QString filename) =0; - virtual void save() =0; - virtual void saveAs(QString filename) =0; - virtual void activateWindow() =0; +namespace TranslationManager +{ + +class CEditor : public QMdiSubWindow +{ + Q_OBJECT + public: + CEditor(QMdiArea *parent) : QMdiSubWindow(parent) {} + CEditor() : QMdiSubWindow() {} + virtual void open(QString filename) =0; + virtual void save() =0; + virtual void saveAs(QString filename) =0; + virtual void activateWindow() =0; + int eType() { return editor_type; } - QString subWindowFilePath() - { - return current_file; - } - void setUndoStack(QUndoStack* stack) + QString subWindowFilePath() + { + return current_file; + } + void setUndoStack(QUndoStack *stack) { current_stack = stack; } @@ -58,14 +56,16 @@ public: QFileInfo *file = new QFileInfo(filename); current_file = file->canonicalFilePath(); setWindowModified(false); - setWindowTitle(file->fileName() + "[*]"); + setWindowTitle(file->fileName() + "[*]"); setWindowFilePath(current_file); } +protected: + QUndoStack *current_stack; + QString current_file; + int editor_type; }; } - -#endif /* TRANSLATION_MANAGER_EDITOR_H */ - +#endif /* TRANSLATION_MANAGER_EDITOR_H */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp index 23509bd20..46a0dd99f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.cpp @@ -1,4 +1,3 @@ - // Translation Manager Plugin - OVQT Plugin // Copyright (C) 2010 Winch Gate Property Limited // Copyright (C) 2011 Emanuel Costea @@ -16,32 +15,31 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -// Project system includes +// Plugin includes +#include "translation_manager_main_window.h" +#include "translation_manager_constants.h" +#include "ftp_selection.h" + +// Core includes #include "../core/icore.h" #include "../core/core_constants.h" #include "../core/menu_manager.h" #include "../../extension_system/iplugin_spec.h" // Qt includes -#include -#include +#include +#include #include -#include #include +#include +#include +#include #include #include #include #include -#include #include -#include -#include #include -// Plugin includes -#include "translation_manager_main_window.h" -#include "translation_manager_constants.h" -#include "ftp_selection.h" - namespace TranslationManager { @@ -49,122 +47,126 @@ namespace TranslationManager CMainWindow::CMainWindow(QWidget *parent) : QMainWindow(parent) { - _ui.setupUi(this); - - _ui.mdiArea->closeAllSubWindows(); - windowMapper = new QSignalMapper(this); - connect(windowMapper, SIGNAL(mapped(QWidget*)), this, SLOT(setActiveSubWindow(QWidget*))); - - initialize_settings["georges"] = false; - initialize_settings["ligo"] = false; + _ui.setupUi(this); - connect(Core::ICore::instance(), SIGNAL(changeSettings()), this, SLOT(readSettings())); - readSettings(); - createToolbar(); - m_undoStack = new QUndoStack(this); + _ui.mdiArea->closeAllSubWindows(); + windowMapper = new QSignalMapper(this); + connect(windowMapper, SIGNAL(mapped(QWidget *)), this, SLOT(setActiveSubWindow(QWidget *))); + + initialize_settings["georges"] = false; + initialize_settings["ligo"] = false; + + connect(Core::ICore::instance(), SIGNAL(changeSettings()), this, SLOT(readSettings())); + readSettings(); + createToolbar(); + m_undoStack = new QUndoStack(this); } // Functions that will insert the plugin buttons void CMainWindow::createToolbar() -{ - // File menu - openAct = new QAction(QIcon(Core::Constants::ICON_OPEN), "&Open file(s)...", this); - _ui.toolBar->addAction(openAct); - connect(openAct, SIGNAL(triggered()), this, SLOT(open())); - saveAct = new QAction(QIcon(Core::Constants::ICON_SAVE), "&Save...", this); - _ui.toolBar->addAction(saveAct); - connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); - saveAsAct = new QAction(QIcon(Core::Constants::ICON_SAVE_AS), "&Save as...", this); - _ui.toolBar->addAction(saveAsAct); - connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); +{ + // File menu + openAct = new QAction(QIcon(Core::Constants::ICON_OPEN), "&Open file(s)...", this); + _ui.toolBar->addAction(openAct); + connect(openAct, SIGNAL(triggered()), this, SLOT(open())); + saveAct = new QAction(QIcon(Core::Constants::ICON_SAVE), "&Save...", this); + _ui.toolBar->addAction(saveAct); + connect(saveAct, SIGNAL(triggered()), this, SLOT(save())); + saveAsAct = new QAction(QIcon(Core::Constants::ICON_SAVE_AS), "&Save as...", this); + _ui.toolBar->addAction(saveAsAct); + connect(saveAsAct, SIGNAL(triggered()), this, SLOT(saveAs())); - // Tools menu - QMenu *wordsExtractionMenu = new QMenu("&Words extraction..."); - wordsExtractionMenu->setIcon(QIcon(Core::Constants::ICON_SETTINGS)); - _ui.toolBar->addAction(wordsExtractionMenu->menuAction()); - // extract bot names - QAction *extractBotNamesAct = wordsExtractionMenu->addAction("&Extract bot names..."); - extractBotNamesAct->setStatusTip(tr("Extract bot names from primitives.")); - connect(extractBotNamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); - // Words extraction - // ----------------------------- - // signal mapper for extraction words - QSignalMapper *wordsExtractionMapper = new QSignalMapper(this); - connect(wordsExtractionMapper, SIGNAL(mapped(QString)), this, SLOT(extractWords(QString))); - // extract item words - QAction *extractItemWordsAct = wordsExtractionMenu->addAction("&Extract item words..."); - extractItemWordsAct->setStatusTip(tr("Extract item words")); - connect(extractItemWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractItemWordsAct, QString(Constants::WK_ITEM)); - // extract creature words - QAction *extractCreatureWordsAct = wordsExtractionMenu->addAction("&Extract creature words..."); - extractCreatureWordsAct->setStatusTip(tr("Extract creature words")); - connect(extractCreatureWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractCreatureWordsAct, QString(Constants::WK_CREATURE)); - // extract sbrick words - QAction *extractSbrickWordsAct = wordsExtractionMenu->addAction("&Extract sbrick words..."); - extractSbrickWordsAct->setStatusTip(tr("Extract sbrick words")); - connect(extractSbrickWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractSbrickWordsAct, QString(Constants::WK_SBRICK)); - // extract sphrase words - QAction *extractSphraseWordsAct = wordsExtractionMenu->addAction("&Extract sphrase words..."); - extractSphraseWordsAct->setStatusTip(tr("Extract sphrase words")); - connect(extractSphraseWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractSphraseWordsAct, QString(Constants::WK_SPHRASE)); - // extract place and region names - QAction *extractPlaceNamesAct = wordsExtractionMenu->addAction("&Extract place names..."); - extractPlaceNamesAct->setStatusTip(tr("Extract place names from primitives")); - connect(extractPlaceNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); - wordsExtractionMapper->setMapping(extractPlaceNamesAct, QString(Constants::WK_PLACE)); - // Merge options - // ----------------------------- - QAction *mergeSingleFileAct = wordsExtractionMenu->addAction("&Merge worksheet file..."); - mergeSingleFileAct->setStatusTip(tr("Merge worksheet file from local or remote directory")); - connect(mergeSingleFileAct, SIGNAL(triggered()), this, SLOT(mergeSingleFile())); - // Windows menu - Core::ICore *core = Core::ICore::instance(); - Core::MenuManager *menuManager = core->menuManager(); - windowMenu = menuManager->menuBar()->addMenu("Window"); - updateWindowsList(); - connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowsList())); + // Tools menu + QMenu *wordsExtractionMenu = new QMenu("&Words extraction..."); + wordsExtractionMenu->setIcon(QIcon(Core::Constants::ICON_SETTINGS)); + _ui.toolBar->addAction(wordsExtractionMenu->menuAction()); - // Undo, Redo actions - // ----------------------------- - QAction* undoAction = menuManager->action(Core::Constants::UNDO); - if (undoAction != 0) - _ui.toolBar->addAction(undoAction); - - QAction* redoAction = menuManager->action(Core::Constants::REDO); - if (redoAction != 0) - _ui.toolBar->addAction(redoAction); + // extract bot names + QAction *extractBotNamesAct = wordsExtractionMenu->addAction("&Extract bot names..."); + extractBotNamesAct->setStatusTip(tr("Extract bot names from primitives.")); + connect(extractBotNamesAct, SIGNAL(triggered()), this, SLOT(extractBotNames())); + + // Words extraction + QSignalMapper *wordsExtractionMapper = new QSignalMapper(this); + connect(wordsExtractionMapper, SIGNAL(mapped(QString)), this, SLOT(extractWords(QString))); + // extract item words + + QAction *extractItemWordsAct = wordsExtractionMenu->addAction("&Extract item words..."); + extractItemWordsAct->setStatusTip(tr("Extract item words")); + connect(extractItemWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractItemWordsAct, QString(Constants::WK_ITEM)); + + // extract creature words + QAction *extractCreatureWordsAct = wordsExtractionMenu->addAction(tr("&Extract creature words...")); + extractCreatureWordsAct->setStatusTip(tr("Extract creature words")); + connect(extractCreatureWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractCreatureWordsAct, QString(Constants::WK_CREATURE)); + + // extract sbrick words + QAction *extractSbrickWordsAct = wordsExtractionMenu->addAction("&Extract sbrick words..."); + extractSbrickWordsAct->setStatusTip(tr("Extract sbrick words")); + connect(extractSbrickWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractSbrickWordsAct, QString(Constants::WK_SBRICK)); + + // extract sphrase words + QAction *extractSphraseWordsAct = wordsExtractionMenu->addAction("&Extract sphrase words..."); + extractSphraseWordsAct->setStatusTip(tr("Extract sphrase words")); + connect(extractSphraseWordsAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractSphraseWordsAct, QString(Constants::WK_SPHRASE)); + + // extract place and region names + QAction *extractPlaceNamesAct = wordsExtractionMenu->addAction("&Extract place names..."); + extractPlaceNamesAct->setStatusTip(tr("Extract place names from primitives")); + connect(extractPlaceNamesAct, SIGNAL(triggered()), wordsExtractionMapper, SLOT(map())); + wordsExtractionMapper->setMapping(extractPlaceNamesAct, QString(Constants::WK_PLACE)); + + // Merge options + QAction *mergeSingleFileAct = wordsExtractionMenu->addAction("&Merge worksheet file..."); + mergeSingleFileAct->setStatusTip(tr("Merge worksheet file from local or remote directory")); + connect(mergeSingleFileAct, SIGNAL(triggered()), this, SLOT(mergeSingleFile())); + + // Windows menu + Core::ICore *core = Core::ICore::instance(); + Core::MenuManager *menuManager = core->menuManager(); + windowMenu = menuManager->menuBar()->addMenu("Window"); + updateWindowsList(); + connect(windowMenu, SIGNAL(aboutToShow()), this, SLOT(updateWindowsList())); + + // Undo, Redo actions + QAction *undoAction = menuManager->action(Core::Constants::UNDO); + if (undoAction != 0) + _ui.toolBar->addAction(undoAction); + + QAction *redoAction = menuManager->action(Core::Constants::REDO); + if (redoAction != 0) + _ui.toolBar->addAction(redoAction); } // Update the toolbar if the editor is worksheet void CMainWindow::updateToolbar(QMdiSubWindow *window) { - if(_ui.mdiArea->subWindowList().size() > 0) - if(QString(window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor - { - QAction *insertRowAct = new QAction(tr("Insert new row"), this); - connect(insertRowAct, SIGNAL(triggered()), window, SLOT(insertRow())); - windowMenu->addAction(insertRowAct); - QAction *deleteRowAct = new QAction(tr("Delete row"), this); - connect(deleteRowAct, SIGNAL(triggered()), window, SLOT(deleteRow())); - windowMenu->addAction(deleteRowAct); - - } + if(_ui.mdiArea->subWindowList().size() > 0) + if(QString(window->widget()->metaObject()->className()) == "QTableWidget") // Sheet Editor + { + QAction *insertRowAct = new QAction(tr("Insert new row"), this); + connect(insertRowAct, SIGNAL(triggered()), window, SLOT(insertRow())); + windowMenu->addAction(insertRowAct); + QAction *deleteRowAct = new QAction(tr("Delete row"), this); + connect(deleteRowAct, SIGNAL(triggered()), window, SLOT(deleteRow())); + windowMenu->addAction(deleteRowAct); + } } // Set the active subwindow -void CMainWindow::setActiveSubWindow(QWidget* window) +void CMainWindow::setActiveSubWindow(QWidget *window) { - if (!window) - { - return; - } - QMdiSubWindow *cwindow = qobject_cast(window); - _ui.mdiArea->setActiveSubWindow(cwindow); + if (!window) + return; + + QMdiSubWindow *mdiWindow = qobject_cast(window); + if (mdiWindow != 0) + _ui.mdiArea->setActiveSubWindow(mdiWindow); } // Functions for updating the windows list @@ -172,148 +174,139 @@ void CMainWindow::updateWindowsList() { if(_ui.mdiArea->activeSubWindow()) { - windowMenu->clear(); - QMdiSubWindow *current_window = _ui.mdiArea->activeSubWindow(); - QList subWindows = _ui.mdiArea->subWindowList(); - - updateToolbar(current_window); - - for(int i = 0; i < subWindows.size(); ++i) - { - QString window_file = QFileInfo(subWindows.at(i)->windowFilePath()).fileName(); - QString action_text; - if (i < 9) { - action_text = QString("&%1 %2").arg(i + 1).arg(window_file); - } else { - action_text = QString("%1 %2").arg(i + 1).arg(window_file); - } - QAction *action = new QAction(action_text, this); - action->setCheckable(true); - action->setChecked(subWindows.at(i) == current_window); - connect(action, SIGNAL(triggered()), windowMapper, SLOT(map())); - windowMenu->addAction(action); - windowMapper->setMapping(action, subWindows.at(i)); - } - } else { - windowMenu->clear(); - } + windowMenu->clear(); + QMdiSubWindow *current_window = _ui.mdiArea->activeSubWindow(); + QList subWindows = _ui.mdiArea->subWindowList(); + + updateToolbar(current_window); + + for(int i = 0; i < subWindows.size(); ++i) + { + QString window_file = QFileInfo(subWindows.at(i)->windowFilePath()).fileName(); + QString action_text; + if (i < 9) + { + action_text = QString("&%1 %2").arg(i + 1).arg(window_file); + } + else + { + action_text = QString("%1 %2").arg(i + 1).arg(window_file); + } + QAction *action = new QAction(action_text, this); + action->setCheckable(true); + action->setChecked(subWindows.at(i) == current_window); + connect(action, SIGNAL(triggered()), windowMapper, SLOT(map())); + windowMenu->addAction(action); + windowMapper->setMapping(action, subWindows.at(i)); + } + } + else + { + windowMenu->clear(); + } } // Open signal void CMainWindow::open() { - QSettings *settings = Core::ICore::instance()->settings(); - settings->beginGroup("translationmanager"); - QString lastOpenLocation = settings->value("lastOpenLocation").toString(); - QString file_name = QFileDialog::getOpenFileName(this, tr("Open translation file"), lastOpenLocation, tr("Translation files (*txt)")); - QFileInfo* file_info = new QFileInfo(file_name); - settings->setValue("lastOpenLocation", file_info->absolutePath()); - settings->endGroup(); + QSettings *settings = Core::ICore::instance()->settings(); + settings->beginGroup("translationmanager"); + QString lastOpenLocation = settings->value("lastOpenLocation").toString(); + QString file_name = QFileDialog::getOpenFileName(this, tr("Open translation file"), lastOpenLocation, tr("Translation files (*txt)")); + QFileInfo *file_info = new QFileInfo(file_name); + settings->setValue("lastOpenLocation", file_info->absolutePath()); + settings->endGroup(); - if(!file_name.isEmpty()) - { - CEditor *editor = getEditorByWindowFilePath(file_name); - if(editor != NULL) - { - editor->activateWindow(); - return; - } - #ifndef QT_NO_CURSOR - QApplication::setOverrideCursor(Qt::WaitCursor); - #endif - // sheet editor - if(isWorksheetEditor(file_name)) - { - CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); - new_window->setUndoStack(m_undoStack); - new_window->open(file_name); - new_window->activateWindow(); - } - // phrase editor - if(isPhraseEditor(file_name)) - { - CEditorPhrase *new_window = new CEditorPhrase(_ui.mdiArea); - new_window->setUndoStack(m_undoStack); - new_window->open(file_name); - new_window->activateWindow(); - } - #ifndef QT_NO_CURSOR - QApplication::restoreOverrideCursor(); - #endif - } - + if(!file_name.isEmpty()) + { + CEditor *editor = getEditorByWindowFilePath(file_name); + if(editor != NULL) + { + editor->activateWindow(); + return; + } + QApplication::setOverrideCursor(Qt::WaitCursor); + + // sheet editor + if(isWorksheetEditor(file_name)) + { + CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); + new_window->setUndoStack(m_undoStack); + new_window->open(file_name); + new_window->activateWindow(); + } + // phrase editor + if(isPhraseEditor(file_name)) + { + CEditorPhrase *new_window = new CEditorPhrase(_ui.mdiArea); + new_window->setUndoStack(m_undoStack); + new_window->open(file_name); + new_window->activateWindow(); + } + QApplication::restoreOverrideCursor(); + } } // Open a work file. You can set the directory for work file in the settings dialog void CMainWindow::openWorkFile(QString file) { - QFileInfo* file_path = new QFileInfo(QString("%1/%2").arg(work_path).arg(file)); - if(file_path->exists()) - { - if(isWorksheetEditor(file_path->filePath())) - { - CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); - new_window->open(file_path->filePath()); - new_window->activateWindow(); - } - } else { - QErrorMessage error; - error.showMessage(QString("The %1 file don't exists.").arg(file_path->fileName())); - error.exec(); - } - + QFileInfo *file_path = new QFileInfo(QString("%1/%2").arg(work_path).arg(file)); + if(file_path->exists()) + { + if(isWorksheetEditor(file_path->filePath())) + { + CEditorWorksheet *new_window = new CEditorWorksheet(_ui.mdiArea); + new_window->open(file_path->filePath()); + new_window->activateWindow(); + } + } + else + { + QErrorMessage error; + error.showMessage(tr("The %1 file don't exists.").arg(file_path->fileName())); + error.exec(); + } } -// Save signal void CMainWindow::save() { - if(_ui.mdiArea->subWindowList().size() > 0) - { - CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - #ifndef QT_NO_CURSOR - QApplication::setOverrideCursor(Qt::WaitCursor); - #endif + if(_ui.mdiArea->subWindowList().size() > 0) + { + CEditor *current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + QApplication::setOverrideCursor(Qt::WaitCursor); current_window->save(); - #ifndef QT_NO_CURSOR - QApplication::restoreOverrideCursor(); - #endif - } + QApplication::restoreOverrideCursor(); + } } -// Save as signal void CMainWindow::saveAs() { - QString file_name; - if (_ui.mdiArea->isActiveWindow()) - { - file_name = QFileDialog::getSaveFileName(this); - } - - if (!file_name.isEmpty()) - { - CEditor* current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - #ifndef QT_NO_CURSOR - QApplication::setOverrideCursor(Qt::WaitCursor); - #endif - current_window->saveAs(file_name); - #ifndef QT_NO_CURSOR - QApplication::restoreOverrideCursor(); - #endif - } + QString file_name; + if (_ui.mdiArea->isActiveWindow()) + { + file_name = QFileDialog::getSaveFileName(this); + } + if (!file_name.isEmpty()) + { + CEditor *current_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + QApplication::setOverrideCursor(Qt::WaitCursor); + current_window->saveAs(file_name); + QApplication::restoreOverrideCursor(); + } } // This function is needed by extraction. void CMainWindow::initializeSettings(bool georges = false) -{ - if(georges == true && initialize_settings["georges"] == false) - { - CPath::addSearchPath(level_design_path.toStdString() + "/DFN", true, false); - CPath::addSearchPath(level_design_path.toStdString() + "/Game_elem/Creature", true, false); - initialize_settings["georges"] = true; - } +{ + if(georges == true && initialize_settings["georges"] == false) + { + NLMISC::CPath::addSearchPath(level_design_path.toStdString() + "/DFN", true, false); + NLMISC::CPath::addSearchPath(level_design_path.toStdString() + "/Game_elem/Creature", true, false); + initialize_settings["georges"] = true; + } - if(initialize_settings["ligo"] == false) - { + if(initialize_settings["ligo"] == false) + { try { // Search path of file world_editor_classes.xml @@ -328,252 +321,263 @@ void CMainWindow::initializeSettings(bool georges = false) { nlerror("Can't found path to world_editor_classes.xml"); } - } - + } } // Extracting words void CMainWindow::extractWords(QString typeq) { - if(verifySettings() == true) - { - CEditorWorksheet* editor_window = getEditorByWorksheetType(typeq); + if(verifySettings() == true) + { + CEditorWorksheet *editor_window = getEditorByWorksheetType(typeq); + if(editor_window != NULL) + { + editor_window->activateWindow(); + QString file_path = editor_window->windowFilePath(); + } + else + { + openWorkFile(typeq); + editor_window = getEditorByWorksheetType(typeq); if(editor_window != NULL) { - editor_window->activateWindow(); - QString file_path = editor_window->windowFilePath(); - } else { - openWorkFile(typeq); - editor_window = getEditorByWorksheetType(typeq); - if(editor_window != NULL) - { - editor_window->activateWindow(); - QString file_path = editor_window->windowFilePath(); - } else return; + editor_window->activateWindow(); + QString file_path = editor_window->windowFilePath(); } + else return; + } - QString column_name; - // Sheet extraction - CSheetWordListBuilder builderS; - // Primitives extraction - CRegionPrimWordListBuilder builderP; - bool isSheet = false; - if(typeq.toAscii() == Constants::WK_ITEM) { - column_name = "item ID"; - builderS.SheetExt = "sitem"; - builderS.SheetPath = level_design_path.append("/game_element/sitem").toStdString(); - isSheet = true; - } else if(typeq.toAscii() == Constants::WK_CREATURE) { - column_name = "creature ID"; - builderS.SheetExt = "creature"; - builderS.SheetPath = level_design_path.append("/Game_elem/Creature/fauna").toStdString(); - isSheet = true; - } else if(typeq.toAscii() == Constants::WK_SBRICK) { - column_name = "sbrick ID"; - builderS.SheetExt = "sbrick"; - builderS.SheetPath = level_design_path.append("/game_element/sbrick").toStdString(); - isSheet = true; - } else if(typeq.toAscii() == Constants::WK_SPHRASE) { - column_name = "sphrase ID"; - builderS.SheetExt = "sphrase"; - builderS.SheetPath = level_design_path.append("/game_element/sphrase").toStdString(); - isSheet = true; - } else if(typeq.toAscii() == Constants::WK_PLACE) { - column_name = "placeId"; - builderP.PrimPath = primitives_path.toStdString(); - builderP.PrimFilter.push_back("region_*.primitive"); - builderP.PrimFilter.push_back("indoors_*.primitive"); - isSheet = false; - } - #ifndef QT_NO_CURSOR - QApplication::setOverrideCursor(Qt::WaitCursor); - #endif - if(isSheet) - { - editor_window->extractWords(editor_window->windowFilePath(), column_name, builderS); - } else { - initializeSettings(false); - editor_window->extractWords(editor_window->windowFilePath(), column_name, builderP); - } - #ifndef QT_NO_CURSOR - QApplication::restoreOverrideCursor(); - #endif - } - + QString column_name; + // Sheet extraction + CSheetWordListBuilder builderS; + // Primitives extraction + CRegionPrimWordListBuilder builderP; + bool isSheet = false; + if(typeq.toAscii() == Constants::WK_ITEM) + { + column_name = "item ID"; + builderS.SheetExt = "sitem"; + builderS.SheetPath = level_design_path.append("/game_element/sitem").toStdString(); + isSheet = true; + } + else if(typeq.toAscii() == Constants::WK_CREATURE) + { + column_name = "creature ID"; + builderS.SheetExt = "creature"; + builderS.SheetPath = level_design_path.append("/Game_elem/Creature/fauna").toStdString(); + isSheet = true; + } + else if(typeq.toAscii() == Constants::WK_SBRICK) + { + column_name = "sbrick ID"; + builderS.SheetExt = "sbrick"; + builderS.SheetPath = level_design_path.append("/game_element/sbrick").toStdString(); + isSheet = true; + } + else if(typeq.toAscii() == Constants::WK_SPHRASE) + { + column_name = "sphrase ID"; + builderS.SheetExt = "sphrase"; + builderS.SheetPath = level_design_path.append("/game_element/sphrase").toStdString(); + isSheet = true; + } + else if(typeq.toAscii() == Constants::WK_PLACE) + { + column_name = "placeId"; + builderP.PrimPath = primitives_path.toStdString(); + builderP.PrimFilter.push_back("region_*.primitive"); + builderP.PrimFilter.push_back("indoors_*.primitive"); + isSheet = false; + } + QApplication::setOverrideCursor(Qt::WaitCursor); + if(isSheet) + { + editor_window->extractWords(editor_window->windowFilePath(), column_name, builderS); + } + else + { + initializeSettings(false); + editor_window->extractWords(editor_window->windowFilePath(), column_name, builderP); + } + QApplication::restoreOverrideCursor(); + } } // Extract bot names from primitives void CMainWindow::extractBotNames() { - if(verifySettings() == true) - { - CEditorWorksheet* editor_window = getEditorByWorksheetType(NULL); + if(verifySettings() == true) + { + CEditorWorksheet *editor_window = getEditorByWorksheetType(NULL); + if(editor_window != NULL) + { + editor_window->activateWindow(); + QString file_path = editor_window->windowFilePath(); + } + else + { + openWorkFile(Constants::WK_BOTNAMES); + editor_window = getEditorByWorksheetType(NULL); if(editor_window != NULL) { - editor_window->activateWindow(); - QString file_path = editor_window->windowFilePath(); - } else { - openWorkFile(Constants::WK_BOTNAMES); - editor_window = getEditorByWorksheetType(NULL); - if(editor_window != NULL) - { - editor_window->activateWindow(); - QString file_path = editor_window->windowFilePath(); - } else return; + editor_window->activateWindow(); + QString file_path = editor_window->windowFilePath(); } - #ifndef QT_NO_CURSOR - QApplication::setOverrideCursor(Qt::WaitCursor); - #endif - initializeSettings(true); - editor_window->extractBotNames(convertQStringList(filters), level_design_path.toStdString(), ligoConfig); - #ifndef QT_NO_CURSOR - QApplication::restoreOverrideCursor(); - #endif - } + else return; + } + + QApplication::setOverrideCursor(Qt::WaitCursor); + initializeSettings(true); + editor_window->extractBotNames(convertQStringList(filters), level_design_path.toStdString(), ligoConfig); + QApplication::restoreOverrideCursor(); + } } // Merge the content for 2 worksheet files void CMainWindow::mergeSingleFile() { - CEditor* editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); - CSourceDialog *dialog = new CSourceDialog(this); - CFtpSelection* ftp_dialog; - map methods; + CEditor *editor_window = qobject_cast(_ui.mdiArea->currentSubWindow()); + CSourceDialog *dialog = new CSourceDialog(this); + CFtpSelection *ftp_dialog; + map methods; QString file_name; - if (_ui.mdiArea->subWindowList().size() == 0) - { + if (_ui.mdiArea->subWindowList().size() == 0) + { QErrorMessage error; - error.showMessage(QString("Open a work file in editor for merge operation.")); - error.exec(); + error.showMessage(tr("Open a work file in editor for merge operation.")); + error.exec(); return; } - if(editor_window->eType() != Constants::ED_SHEET) // Sheet Editor - { + if(editor_window->eType() != Constants::ED_SHEET) // Sheet Editor + { QErrorMessage error; - error.showMessage(QString("Please open or activate the window with a sheet file.")); - error.exec(); + error.showMessage(tr("Please open or activate the window with a sheet file.")); + error.exec(); return; } - // create items - QListWidgetItem* local_item = new QListWidgetItem(); - local_item->setText("Local directory"); - methods[local_item] = 0; - QListWidgetItem* ftp_item = new QListWidgetItem(); - ftp_item->setText("From a FTP server"); - methods[ftp_item] = 1; - - dialog->setSourceOptions(methods); - dialog->show(); - dialog->exec(); + // create items + QListWidgetItem *local_item = new QListWidgetItem(); + local_item->setText("Local directory"); + methods[local_item] = 0; + QListWidgetItem *ftp_item = new QListWidgetItem(); + ftp_item->setText("From a FTP server"); + methods[ftp_item] = 1; + + dialog->setSourceOptions(methods); + dialog->show(); + dialog->exec(); // get the file for merge - if(dialog->selected_item == local_item) // Local directory - { - file_name = QFileDialog::getOpenFileName(this); - } + if(dialog->selected_item == local_item) // Local directory + { + file_name = QFileDialog::getOpenFileName(this); + } else if(dialog->selected_item == ftp_item) // Ftp directory { - CFtpSelection* ftp_dialog = new CFtpSelection(this); - ftp_dialog->show(); + CFtpSelection *ftp_dialog = new CFtpSelection(this); + ftp_dialog->show(); if(ftp_dialog->exec() && ftp_dialog->status == true) - file_name = ftp_dialog->file->fileName(); + file_name = ftp_dialog->file->fileName(); delete ftp_dialog; - } + } else - return; - + return; + // Make sure we retrieved a file name if(file_name.isEmpty()) return; - editor_window->activateWindow(); - CEditorWorksheet* current_window = qobject_cast(editor_window); - if(current_window->windowFilePath() == file_name) + editor_window->activateWindow(); + CEditorWorksheet *current_window = qobject_cast(editor_window); + if(current_window->windowFilePath() == file_name) return; - if(current_window->compareWorksheetFile(file_name)) - { - current_window->mergeWorksheetFile(file_name); - } else { - QErrorMessage error; - error.showMessage(tr("The file: %1 has different columns from the current file in editor.").arg(file_name)); - error.exec(); - } - if(dialog->selected_item == ftp_item) + if(current_window->compareWorksheetFile(file_name)) { + current_window->mergeWorksheetFile(file_name); + } + else + { + QErrorMessage error; + error.showMessage(tr("The file: %1 has different columns from the current file in editor.").arg(file_name)); + error.exec(); + } + if(dialog->selected_item == ftp_item) + { + /* + // TODO: uninit ftp_dialog????? if(!ftp_dialog->file->remove()) { QErrorMessage error; error.showMessage(tr("Please remove the file from ftp server manually. The file is located on the same directory with OVQT application.")); - error.exec(); + error.exec(); } - + */ } } // Read the settings from QSettings void CMainWindow::readSettings() { - QSettings *settings = Core::ICore::instance()->settings(); - // translation manager settings - settings->beginGroup("translationmanager"); - filters = settings->value("filters").toStringList(); - languages = settings->value("trlanguages").toStringList(); - translation_path = settings->value("translation").toString(); - work_path = settings->value("work").toString(); - settings->endGroup(); - // core settings - settings->beginGroup(Core::Constants::DATA_PATH_SECTION); - level_design_path = settings->value(Core::Constants::LEVELDESIGN_PATH).toString(); - primitives_path = QString(Core::Constants::PRIMITIVES_PATH); //TODO - settings->endGroup(); + QSettings *settings = Core::ICore::instance()->settings(); + // translation manager settings + settings->beginGroup("translationmanager"); + filters = settings->value("filters").toStringList(); + languages = settings->value("trlanguages").toStringList(); + translation_path = settings->value("translation").toString(); + work_path = settings->value("work").toString(); + settings->endGroup(); + // core settings + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + level_design_path = settings->value(Core::Constants::LEVELDESIGN_PATH).toString(); + primitives_path = QString(Core::Constants::PRIMITIVES_PATH); //TODO + settings->endGroup(); } // Verify the settings bool CMainWindow::verifySettings() { - bool count_errors = false; - - if(level_design_path.isNull() || primitives_path.isNull() || work_path.isNull()) - { - QErrorMessage error_settings; - error_settings.showMessage(tr("Please write all the paths on the settings dialog.")); - error_settings.exec(); - count_errors = true; - } - - return !count_errors; - + bool count_errors = false; + + if(level_design_path.isNull() || primitives_path.isNull() || work_path.isNull()) + { + QErrorMessage error_settings; + error_settings.showMessage(tr("Please write all the paths on the settings dialog.")); + error_settings.exec(); + count_errors = true; + } + return !count_errors; } bool CCoreListener::closeMainWindow() const { bool okToClose = true; - Q_FOREACH(QMdiSubWindow *subWindow, m_MainWindow->_ui.mdiArea->subWindowList()) - { - CEditor *currentEditor = qobject_cast(subWindow); + Q_FOREACH(QMdiSubWindow *subWindow, m_MainWindow->_ui.mdiArea->subWindowList()) + { + CEditor *currentEditor = qobject_cast(subWindow); if(subWindow->isWindowModified()) { QMessageBox msgBox; + msgBox.setIcon(QMessageBox::Question); msgBox.setText(tr("The document has been modified ( %1 ).").arg(currentEditor->windowFilePath())); - msgBox.setInformativeText("Do you want to save your changes?"); + msgBox.setInformativeText(tr("Do you want to save your changes?")); msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); msgBox.setDefaultButton(QMessageBox::Save); int ret = msgBox.exec(); - if(ret == QMessageBox::Save) - { - currentEditor->save(); - } - else if(ret == QMessageBox::Cancel) - { - okToClose = false; - break; - } + if(ret == QMessageBox::Save) + { + currentEditor->save(); + } + else if(ret == QMessageBox::Cancel) + { + okToClose = false; + break; + } } - } + } return okToClose; } @@ -581,73 +585,68 @@ bool CCoreListener::closeMainWindow() const // Helper functions CEditor *CMainWindow::getEditorByWindowFilePath(const QString &fileName) { - Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) - { - CEditor *currentEditor = qobject_cast(subWindow); - if(currentEditor->subWindowFilePath() == fileName) - return currentEditor; - } - return NULL; + Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) + { + CEditor *currentEditor = qobject_cast(subWindow); + if(currentEditor->subWindowFilePath() == fileName) + return currentEditor; + } + return NULL; } CEditorWorksheet *CMainWindow::getEditorByWorksheetType(const QString &type) { - Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) - { - CEditor *currentEditor = qobject_cast(subWindow); + Q_FOREACH(QMdiSubWindow *subWindow, _ui.mdiArea->subWindowList()) + { + CEditor *currentEditor = qobject_cast(subWindow); if(currentEditor->eType() == Constants::ED_SHEET) { CEditorWorksheet *editor = qobject_cast(currentEditor); - if(type != NULL) { + if(type != NULL) + { if(editor->isSheetTable(type)) { return editor; } - } else { + } + else + { if(editor->isBotNamesTable()) { return editor; } } } - } - return NULL; + } + return NULL; } +std::list CMainWindow::convertQStringList(QStringList listq) +{ + std::list stdlist; -list CMainWindow::convertQStringList(QStringList listq) -{ - std::list stdlist; - - Q_FOREACH(QString text, listq) - { - stdlist.push_back(text.toStdString()); - } - - return stdlist; + Q_FOREACH(QString text, listq) + { + stdlist.push_back(text.toStdString()); + } + return stdlist; } bool CMainWindow::isWorksheetEditor(QString filename) { - STRING_MANAGER::TWorksheet wk_file; - if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) - { + STRING_MANAGER::TWorksheet wk_file; + if(loadExcelSheet(filename.toStdString(), wk_file, true) == true) + { if(wk_file.ColCount > 1) return true; - } - + } return false; } bool CMainWindow::isPhraseEditor(QString filename) { vector phrases; - if(readPhraseFile(filename.toStdString(), phrases, false)) - { - return true; - } else { - return false; - } + return readPhraseFile(filename.toStdString(), phrases, false); } } /* namespace TranslationManager */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h index 19c3c1976..c67f282e0 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_main_window.h @@ -1,5 +1,4 @@ // Translation Manager Plugin - OVQT Plugin -// Copyright (C) 2010 Winch Gate Property Limited // Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify @@ -15,20 +14,19 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . - #ifndef MAIN_WINDOW_H #define MAIN_WINDOW_H +// Project includes +#include "ui_translation_manager_main_window.h" +#include "translation_manager_editor.h" +#include "source_selection.h" +#include "editor_worksheet.h" +#include "editor_phrase.h" + // Project system includes #include "../core/icore_listener.h" -// Nel includes -#include "nel/misc/types_nl.h" -#include "nel/misc/sheet_id.h" -#include "nel/misc/path.h" -#include "nel/misc/diff_tool.h" -#include "nel/ligo/ligo_config.h" - // Qt includes #include #include @@ -39,80 +37,82 @@ #include #include -// Plugin includes -#include "translation_manager_editor.h" -#include "source_selection.h" -#include "ui_translation_manager_main_window.h" +// STL includes #include -#include "editor_worksheet.h" -#include "editor_phrase.h" - -class QWidget; +// Nel includes +#include "nel/misc/types_nl.h" +#include "nel/misc/sheet_id.h" +#include "nel/misc/path.h" +#include "nel/misc/diff_tool.h" +#include "nel/ligo/ligo_config.h" using namespace std; namespace TranslationManager { - + class CMainWindow : public QMainWindow { Q_OBJECT + public: - CMainWindow(QWidget *parent = 0); - virtual ~CMainWindow() {} - QUndoStack *m_undoStack; + CMainWindow(QWidget *parent = 0); + virtual ~CMainWindow() {} + QUndoStack *m_undoStack; + public: - Ui::CMainWindow _ui; -private: - // actions - QAction *openAct; - QAction *saveAct; - QAction *saveAsAct; - QMenu *windowMenu; - QSignalMapper *windowMapper; - // config - QMap initialize_settings; - QList filters; - QList languages; - QString level_design_path; - QString primitives_path; - QString translation_path; - QString work_path; - NLLIGO::CLigoConfig ligoConfig; -private Q_SLOTS: - void extractBotNames(); - void extractWords(QString typeq); - void open(); - void save(); - void saveAs(); - void setActiveSubWindow(QWidget *window); - void updateWindowsList(); - void mergeSingleFile(); + Ui::CMainWindow _ui; + private: - void openWorkFile(QString file); - void updateToolbar(QMdiSubWindow *window); - bool verifySettings(); - void readSettings(); - void createMenus(); - void createToolbar(); - void initializeSettings(bool georges); - list convertQStringList(QStringList listq); - CEditor* getEditorByWindowFilePath(const QString &fileName); + // actions + QAction *openAct; + QAction *saveAct; + QAction *saveAsAct; + QMenu *windowMenu; + QSignalMapper *windowMapper; + // config + QMap initialize_settings; + QList filters; + QList languages; + QString level_design_path; + QString primitives_path; + QString translation_path; + QString work_path; + NLLIGO::CLigoConfig ligoConfig; + +private Q_SLOTS: + void extractBotNames(); + void extractWords(QString typeq); + void open(); + void save(); + void saveAs(); + void setActiveSubWindow(QWidget *window); + void updateWindowsList(); + void mergeSingleFile(); + +private: + void openWorkFile(QString file); + void updateToolbar(QMdiSubWindow *window); + bool verifySettings(); + void readSettings(); + void createMenus(); + void createToolbar(); + void initializeSettings(bool georges); + std::list convertQStringList(QStringList listq); + CEditor *getEditorByWindowFilePath(const QString &fileName); // Worksheet specific functions - CEditorWorksheet* getEditorByWorksheetType(const QString &type); - bool isWorksheetEditor(QString filename); + CEditorWorksheet *getEditorByWorksheetType(const QString &type); + bool isWorksheetEditor(QString filename); bool isPhraseEditor(QString filename); - - - }; class CCoreListener : public Core::ICoreListener { Q_OBJECT + public: - CCoreListener(CMainWindow* mainWindow, QObject *parent = 0): ICoreListener(parent) + CCoreListener(CMainWindow *mainWindow, QObject *parent = 0): ICoreListener(parent) { m_MainWindow = mainWindow; } @@ -124,10 +124,6 @@ public: CMainWindow *m_MainWindow; }; - - } // namespace TranslationManager - - -#endif // SIMPLE_VIEWER_H +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp index a43311506..03aa06c87 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.cpp @@ -19,7 +19,8 @@ #include "translation_manager_plugin.h" #include "translation_manager_settings_page.h" #include "translation_manager_main_window.h" -// Project system includes + +// Core includes #include "../core/icore.h" #include "../core/core_constants.h" #include "../core/menu_manager.h" @@ -59,17 +60,14 @@ bool TranslationManagerPlugin::initialize(ExtensionSystem::IPluginManager *plugi addAutoReleasedObject(new CTranslationManagerSettingsPage(this)); addAutoReleasedObject(new CTranslationManagerContext(mainWindow, this)); addAutoReleasedObject(new CCoreListener(mainWindow, this)); - + return true; } void TranslationManagerPlugin::extensionsInitialized() { - } - - void TranslationManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) { #ifdef NL_OS_WINDOWS @@ -86,23 +84,6 @@ void TranslationManagerPlugin::addAutoReleasedObject(QObject *obj) _autoReleaseObjects.prepend(obj); } -QObject* TranslationManagerPlugin::objectByName(const QString &name) const -{ - Q_FOREACH (QObject *qobj, _plugMan->allObjects()) - if (qobj->objectName() == name) - return qobj; - return 0; } -ExtensionSystem::IPluginSpec *TranslationManagerPlugin::pluginByName(const QString &name) const -{ - Q_FOREACH (ExtensionSystem::IPluginSpec *spec, _plugMan->plugins()) - if (spec->name() == name) - return spec; - return 0; - -} - -} - -Q_EXPORT_PLUGIN(TranslationManager::TranslationManagerPlugin) +Q_EXPORT_PLUGIN(TranslationManager::TranslationManagerPlugin) \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h index f9cb6798c..fb03a49f1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_plugin.h @@ -45,8 +45,8 @@ class IPluginSpec; namespace TranslationManager { - class CTranslationManagerContext; - +class CTranslationManagerContext; + class TranslationManagerPlugin : public QObject, public ExtensionSystem::IPlugin { Q_OBJECT @@ -58,12 +58,8 @@ public: void extensionsInitialized(); void setNelContext(NLMISC::INelContext *nelContext); - void addAutoReleasedObject(QObject *obj); - QObject *objectByName(const QString &name) const; - ExtensionSystem::IPluginSpec *pluginByName(const QString &name) const; - protected: NLMISC::CLibraryContext *_LibContext; @@ -75,8 +71,9 @@ private: class CTranslationManagerContext: public Core::IContext { Q_OBJECT + public: - CTranslationManagerContext(CMainWindow* mainWindow, QObject *parent = 0): IContext(parent) + CTranslationManagerContext(CMainWindow *mainWindow, QObject *parent = 0): IContext(parent) { m_MainWindow = mainWindow; } @@ -109,9 +106,8 @@ public: } CMainWindow *m_MainWindow; - }; -} // namespace Plugin +} -#endif // TRANSLATION_MANAGER_PLUGIN_H +#endif // TRANSLATION_MANAGER_PLUGIN_H \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp index 09588da7d..e3dbd92df 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.cpp @@ -1,5 +1,4 @@ // Translation Manager Plugin - OVQT Plugin -// Copyright (C) 2010 Winch Gate Property Limited // Copyright (C) 2011 Emanuel Costea // // This program is free software: you can redistribute it and/or modify @@ -17,17 +16,15 @@ #include "translation_manager_settings_page.h" +// Core includes +#include "../core/icore.h" + // Qt includes #include #include #include #include -// NeL includes - -// Project includes -#include "../core/icore.h" - namespace TranslationManager { @@ -68,20 +65,20 @@ QWidget *CTranslationManagerSettingsPage::createPage(QWidget *parent) { _currentPage = new QWidget(parent); _ui.setupUi(_currentPage); - readSettings(); + readSettings(); connect(_ui.filter_add, SIGNAL(clicked()), this, SLOT(filterAdd())); connect(_ui.filter_del, SIGNAL(clicked()), this, SLOT(filterDel())); connect(_ui.lang_add, SIGNAL(clicked()), this, SLOT(languageAdd())); connect(_ui.lang_del, SIGNAL(clicked()), this, SLOT(languageDel())); - connect(_ui.translation_add, SIGNAL(clicked()), this, SLOT(translationAdd())); - connect(_ui.work_add, SIGNAL(clicked()), this, SLOT(workAdd())); - + connect(_ui.translation_add, SIGNAL(clicked()), this, SLOT(translationAdd())); + connect(_ui.work_add, SIGNAL(clicked()), this, SLOT(workAdd())); + return _currentPage; } void CTranslationManagerSettingsPage::filterAdd() -{ - QString newValue = _ui.filter_edit->text(); +{ + QString newValue = _ui.filter_edit->text(); if (!newValue.isEmpty()) { QListWidgetItem *newItem = new QListWidgetItem; @@ -95,113 +92,112 @@ void CTranslationManagerSettingsPage::filterDel() { QListWidgetItem *removeItem = _ui.filter_list->takeItem(_ui.filter_list->currentRow()); if (!removeItem) - delete removeItem; + delete removeItem; } void CTranslationManagerSettingsPage::languageAdd() { - QString newValue = _ui.lang_edit->text(); + QString newValue = _ui.lang_edit->text(); if (!newValue.isEmpty()) { QListWidgetItem *newItem = new QListWidgetItem; newItem->setText(newValue); newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.lang_list->addItem(newItem); - } + } } void CTranslationManagerSettingsPage::languageDel() { QListWidgetItem *removeItem = _ui.lang_list->takeItem(_ui.lang_list->currentRow()); if (!removeItem) - delete removeItem; + delete removeItem; } void CTranslationManagerSettingsPage::translationAdd() { - QString newPath = QFileDialog::getExistingDirectory(_currentPage, ""); - if (!newPath.isEmpty()) + QString newPath = QFileDialog::getExistingDirectory(_currentPage, ""); + if (!newPath.isEmpty()) { - _ui.translation_edit->setText(newPath); - } + _ui.translation_edit->setText(newPath); + } } void CTranslationManagerSettingsPage::workAdd() { - QString newPath = QFileDialog::getExistingDirectory(_currentPage, ""); - if (!newPath.isEmpty()) + QString newPath = QFileDialog::getExistingDirectory(_currentPage, ""); + if (!newPath.isEmpty()) { - _ui.work_edit->setText(newPath); - } + _ui.work_edit->setText(newPath); + } } void CTranslationManagerSettingsPage::apply() { - writeSettings(); + writeSettings(); } void CTranslationManagerSettingsPage::readSettings() { QStringList filters, languages; - QString ligo, translation, work; - + QString ligo, translation, work; + QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("translationmanager"); - - filters = settings->value("filters").toStringList(); /* filters */ - languages = settings->value("trlanguages").toStringList(); /* languages */ - ligo = settings->value("ligo").toString(); - translation = settings->value("translation").toString(); - work = settings->value("work").toString(); - + + filters = settings->value("filters").toStringList(); /* filters */ + languages = settings->value("trlanguages").toStringList(); /* languages */ + ligo = settings->value("ligo").toString(); + translation = settings->value("translation").toString(); + work = settings->value("work").toString(); + settings->endGroup(); - // filter + // filter Q_FOREACH(QString filter, filters) { QListWidgetItem *newItem = new QListWidgetItem; newItem->setText(filter); newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.filter_list->addItem(newItem); - } - // languages + } + // languages Q_FOREACH(QString lang, languages) { QListWidgetItem *newItem = new QListWidgetItem; newItem->setText(lang); newItem->setFlags(Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable); _ui.lang_list->addItem(newItem); - } - // translation - _ui.translation_edit->setText(translation); - // work - _ui.work_edit->setText(work); - + } + // translation + _ui.translation_edit->setText(translation); + // work + _ui.work_edit->setText(work); + } void CTranslationManagerSettingsPage::writeSettings() { QStringList filters, languages; - QString ligo, translation, work; - // filters + QString ligo, translation, work; + // filters for (int i = 0; i < _ui.filter_list->count(); ++i) filters << _ui.filter_list->item(i)->text(); - // languages + // languages for (int i = 0; i < _ui.lang_list->count(); ++i) - languages << _ui.lang_list->item(i)->text(); - // translations path - translation = _ui.translation_edit->text(); - // work path - work = _ui.work_edit->text(); - + languages << _ui.lang_list->item(i)->text(); + // translations path + translation = _ui.translation_edit->text(); + // work path + work = _ui.work_edit->text(); + QSettings *settings = Core::ICore::instance()->settings(); settings->beginGroup("translationmanager"); - settings->setValue("filters", filters); - settings->setValue("trlanguages", languages); - settings->setValue("translation", translation); - settings->setValue("work", work); + settings->setValue("filters", filters); + settings->setValue("trlanguages", languages); + settings->setValue("translation", translation); + settings->setValue("work", work); settings->endGroup(); - settings->sync(); + settings->sync(); } - -} /* namespace Plugin */ +} \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h index 34caea5e1..9b34d87ba 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.h @@ -24,13 +24,9 @@ #include "ui_translation_manager_settings_page.h" -class QWidget; - namespace TranslationManager { -/** -@class CTranslationManagerSettingsPage -*/ + class CTranslationManagerSettingsPage : public Core::IOptionsPage { Q_OBJECT @@ -44,23 +40,23 @@ public: virtual QString trCategory() const; virtual QIcon categoryIcon() const; virtual QWidget *createPage(QWidget *parent); - + virtual void apply(); virtual void finish() {} private Q_SLOTS: - void filterAdd(); - void filterDel(); - void languageAdd(); - void languageDel(); - void translationAdd(); - void workAdd(); + void filterAdd(); + void filterDel(); + void languageAdd(); + void languageDel(); + void translationAdd(); + void workAdd(); private: QWidget *_currentPage; Ui::CTranslationManagerSettingsPage _ui; - void writeSettings(); - void readSettings(); + void writeSettings(); + void readSettings(); }; -} // namespace Plugin +} -#endif // TRANSLATION_MANAGER_SETTINGS_H +#endif // TRANSLATION_MANAGER_SETTINGS_H \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui index aaa4337f4..bd377b6b9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/translation_manager/translation_manager_settings_page.ui @@ -6,178 +6,152 @@ 0 0 - 533 - 478 + 589 + 490 Form - - - - 0 - 10 - 531 - 421 - - - - Translation Manager Plugin - - - - - 0 - 30 - 521 - 232 - - - - - - - Filters - - - - - - - - - - - - - - :/core/icons/ic_nel_add_item.png:/core/icons/ic_nel_add_item.png - - - true - - - - - - - - - - - :/core/icons/ic_nel_delete_item.png:/core/icons/ic_nel_delete_item.png - - - true - - - - - - - Languages - - - - - - - - - - - - - - :/core/icons/ic_nel_add_item.png:/core/icons/ic_nel_add_item.png - - - true - - - - - - - - - - - :/core/icons/ic_nel_delete_item.png:/core/icons/ic_nel_delete_item.png - - - true - - - - - - - - - - - - - - - 0 - 340 - 521 - 60 - - - - - - - Translation directory - - - - - - - - - - ... - - - - - - - - - 0 - 270 - 521 - 60 - - - - - - - Work directory - - - - - - - - - - ... - - - - - - + + + + + Translation Manager Plugin + + + + + + + + Filters + + + + + + + + + + + + + + :/core/icons/ic_nel_add_item.png:/core/icons/ic_nel_add_item.png + + + true + + + + + + + + + + + :/core/icons/ic_nel_delete_item.png:/core/icons/ic_nel_delete_item.png + + + true + + + + + + + Languages + + + + + + + + + + + + + + :/core/icons/ic_nel_add_item.png:/core/icons/ic_nel_add_item.png + + + true + + + + + + + + + + + :/core/icons/ic_nel_delete_item.png:/core/icons/ic_nel_delete_item.png + + + true + + + + + + + + + + + + + + + + + Work directory + + + + + + + + + + ... + + + + + + + + + + + Translation directory + + + + + + + + + + ... + + + + + + + + + From 11d9de671d627b0a326496cfde405d2da9907a84 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Sat, 31 Dec 2011 12:24:23 +0300 Subject: [PATCH 158/215] Added #1193 Added scheme bank dialog in particles editor. --- .../src/plugins/object_viewer/attrib_form.ui | 2 +- .../plugins/object_viewer/attrib_widget.cpp | 11 +- .../plugins/object_viewer/particle_editor.cpp | 1 + .../object_viewer/scheme_bank_dialog.cpp | 149 ++++++++++++++++-- .../object_viewer/scheme_bank_dialog.h | 57 +++++-- .../plugins/object_viewer/scheme_bank_form.ui | 36 +++-- .../plugins/object_viewer/scheme_manager.cpp | 26 +-- 7 files changed, 217 insertions(+), 65 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_form.ui index 77c4b895f..404444fc0 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_form.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_form.ui @@ -91,7 +91,7 @@ - false + true diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_widget.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_widget.cpp index 648e0e1a5..e00d07cec 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_widget.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_widget.cpp @@ -186,12 +186,11 @@ void CAttribWidget::changeUseScheme(int index) void CAttribWidget::openSchemeBankDialog() { - CSchemeBankDialog *dialog = new CSchemeBankDialog(this); - dialog->setModal(true); - dialog->show(); - dialog->exec(); - delete dialog; - //updateUi(); + CSchemeBankDialog dialog(this); + dialog.setModal(true); + dialog.show(); + dialog.exec(); + updateUi(); } void CAttribWidget::inputValueUpdate(void) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_editor.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_editor.cpp index 768576755..12ea96c8a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_editor.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/particle_editor.cpp @@ -73,6 +73,7 @@ void CParticleEditor::release() { stop(); closeWorkspace(); + delete _SchemeManager; } void CParticleEditor::setActiveNode(CWorkspaceNode *node) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_dialog.cpp index 66deb2a17..bca0f4879 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_dialog.cpp @@ -1,24 +1,33 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License +// NeL - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // Project includes #include "stdpch.h" #include "scheme_bank_dialog.h" +#include "scheme_manager.h" #include "modules.h" +// NeL includes +#include + +// Qt includes +#include +#include +#include + namespace NLQT { @@ -27,6 +36,16 @@ CSchemeBankDialog::CSchemeBankDialog(CAttribWidget *attribWidget, QWidget *paren { _ui.setupUi(this); _attribWidget = attribWidget; + + connect(_ui.createButton, SIGNAL(clicked()), this, SLOT(createScheme())); + connect(_ui.currentButton, SIGNAL(clicked()), this, SLOT(setCurrentScheme())); + connect(_ui.removeButton, SIGNAL(clicked()), this, SLOT(removeScheme())); + connect(_ui.loadButton, SIGNAL(clicked()), this, SLOT(loadBank())); + connect(_ui.saveButton, SIGNAL(clicked()), this, SLOT(saveBank())); + connect(_ui.listWidget, SIGNAL(itemSelectionChanged()), this, SLOT(enableButtons())); + connect(_ui.listWidget, SIGNAL(itemChanged(QListWidgetItem *)), this, SLOT(changeNameScheme(QListWidgetItem *))); + + buildList(); } CSchemeBankDialog::~CSchemeBankDialog() @@ -35,27 +54,125 @@ CSchemeBankDialog::~CSchemeBankDialog() void CSchemeBankDialog::createScheme() { + bool ok; + QString text = QInputDialog::getText(this, tr("Insert new scheme"), + tr("Set name:"), QLineEdit::Normal, + "new scheme", &ok); + if (ok && !text.isEmpty()) + { + NL3D::CPSAttribMakerBase *attribMakerBase = _attribWidget->getCurrentSchemePtr()->clone(); + Modules::psEdit().getSchemeManager()->insertScheme(text.toStdString(), attribMakerBase); + + CSchemeItem *item = new CSchemeItem(text, _ui.listWidget); + item->setUserData(attribMakerBase); + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); + } } void CSchemeBankDialog::setCurrentScheme() { - //SchemeManager.insertScheme(cn.getName(), getCurrentSchemePtr()->clone()); + CSchemeItem *item = dynamic_cast(_ui.listWidget->currentItem()); + + NL3D::CPSAttribMakerBase *attrib = item->getUserData(); + nlassert(attrib); + + _attribWidget->setCurrentSchemePtr(attrib->clone()); + _attribWidget->updateUi(); } void CSchemeBankDialog::removeScheme() { + CSchemeItem *item = dynamic_cast(_ui.listWidget->currentItem()); + + NL3D::CPSAttribMakerBase *attrib = item->getUserData(); + nlassert(attrib); + Modules::psEdit().getSchemeManager()->remove(attrib); + _ui.listWidget->removeItemWidget(item); + delete item; + + if (_ui.listWidget->count() == 0) + { + _ui.currentButton->setEnabled(false); + _ui.removeButton->setEnabled(false); + } } void CSchemeBankDialog::saveBank() { + QString fileName = QFileDialog::getSaveFileName(this, + tr("Save scheme bank file"), ".", + tr("Scheme bank files (*.scb)")); + + if (!fileName.isEmpty()) + { + try + { + NLMISC::COFile iF; + iF.open(fileName.toStdString()); + NLQT::CSchemeManager *schemeManager = Modules::psEdit().getSchemeManager(); + iF.serial(*schemeManager); + } + catch (std::exception &e) + { + QMessageBox::critical(this, "Scheme manager", tr("Error saving scheme bank : %1").arg(e.what())); + return; + } + } } void CSchemeBankDialog::loadBank() { + QString fileName = QFileDialog::getOpenFileName(this, + tr("Open scheme bank file"), ".", + tr("Scheme bank files (*.scb)")); + + if (!fileName.isEmpty()) + { + NLQT::CSchemeManager sm; + try + { + NLMISC::CIFile iF; + iF.open(fileName.toStdString()); + iF.serial(sm); + Modules::psEdit().getSchemeManager()->swap(sm); + } + catch (std::exception &e) + { + QMessageBox::critical(this, "Scheme manager", tr("Error loading scheme bank : %1").arg(e.what())); + return; + } + buildList(); + } } -void CSchemeBankDialog::buildList() +void CSchemeBankDialog::changeNameScheme(QListWidgetItem *item) { + CSchemeItem *schemeItem = dynamic_cast(item); + + NL3D::CPSAttribMakerBase *attrib = schemeItem->getUserData(); + nlassert(attrib); + + Modules::psEdit().getSchemeManager()->rename(attrib, item->text().toStdString()); +} + +void CSchemeBankDialog::enableButtons() +{ + _ui.currentButton->setEnabled(true); + _ui.removeButton->setEnabled(true); +} + +void CSchemeBankDialog::buildList() +{ + _ui.listWidget->clear(); + typedef std::vector TSchemeVect; + static TSchemeVect schemes; + Modules::psEdit().getSchemeManager()->getSchemes(_attribWidget->getCurrentSchemePtr()->getType(), schemes); + for (TSchemeVect::const_iterator it = schemes.begin(); it != schemes.end(); ++it) + { + CSchemeItem *item = new CSchemeItem(it->first.c_str(), _ui.listWidget); + item->setUserData(it->second); + item->setFlags(Qt::ItemIsEditable | Qt::ItemIsSelectable | Qt::ItemIsEnabled); + } } } /* namespace NLQT */ \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_dialog.h index a02406468..8f9f300b8 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_dialog.h @@ -1,17 +1,17 @@ -// NeL - MMORPG Framework -// Copyright (C) 2010 Winch Gate Property Limited -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU Affero 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 Affero General Public License for more details. -// -// You should have received a copy of the GNU Affero General Public License +// NeL - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #ifndef SCHEME_BANK_DIALOG_H @@ -27,12 +27,37 @@ #include "nel/3d/particle_system.h" // Project includes +#include "attrib_widget.h" #include "ps_wrapper.h" namespace NLQT { class CAttribWidget; +/** +@class CSchemeItem +@brief Contain pointer to NL3D::CPSAttribMakerBase. +*/ +class CSchemeItem: public QListWidgetItem +{ +public: + CSchemeItem(const QString &text, QListWidget *parent = 0, int type = UserType ): + QListWidgetItem(text, parent, type), _attrib(NULL) {} + + void setUserData(NL3D::CPSAttribMakerBase *attrib) + { + _attrib = attrib; + } + NL3D::CPSAttribMakerBase *getUserData() const + { + return _attrib; + } + +private: + + NL3D::CPSAttribMakerBase *_attrib; +}; /* class CSchemeItem */ + class CSchemeBankDialog: public QDialog { Q_OBJECT @@ -47,9 +72,11 @@ private Q_SLOTS: void removeScheme(); void saveBank(); void loadBank(); + void enableButtons(); + void changeNameScheme(QListWidgetItem *item); private: - void buildList(); + void buildList(); CAttribWidget *_attribWidget; Ui::CSchemeBankDialog _ui; diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_form.ui index e7dd281f8..f8fa10865 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_form.ui +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_bank_form.ui @@ -14,31 +14,27 @@ Sceme bank - + - Create + Put current - - - Rename - - - - + + false + Remove - + Qt::Vertical @@ -51,24 +47,33 @@ - + + + true + Load bank - + + + true + Save bank - + + + false + 0 @@ -105,7 +110,10 @@ - + + + false + Set current diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_manager.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_manager.cpp index 62e847a73..c00422af5 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_manager.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/scheme_manager.cpp @@ -18,7 +18,7 @@ #include "scheme_manager.h" #include "nel/3d/ps_attrib_maker.h" -namespace NLQT +namespace NLQT { CSchemeManager::~CSchemeManager() @@ -34,7 +34,7 @@ void CSchemeManager::insertScheme(const std::string &name, NL3D::CPSAttribMakerB { nlassert(scheme); TSchemeInfo si(std::string(name), scheme); - _SchemeMap.insert(TSchemeMap::value_type(std::string(scheme->getType()), si)); + _SchemeMap.insert(TSchemeMap::value_type(std::string(scheme->getType()), si)); } void CSchemeManager::getSchemes(const std::string &type, std::vector &dest) @@ -49,30 +49,30 @@ void CSchemeManager::getSchemes(const std::string &type, std::vectorsecond.first); // name f.serialPolyPtr(smIt->second.second); // scheme - } + } } else { _SchemeMap.clear(); - + std::string name; NL3D::CPSAttribMakerBase *scheme = NULL; sint32 size; f.serial(size); for (sint32 k = 0; k < size; ++k) - { + { f.serial(name); f.serialPolyPtr(scheme); insertScheme(name, scheme); @@ -80,12 +80,12 @@ void CSchemeManager::serial(NLMISC::IStream &f) throw(NLMISC::EStream) } } -void CSchemeManager::swap(CSchemeManager &other) +void CSchemeManager::swap(CSchemeManager &other) { this->_SchemeMap.swap(other._SchemeMap); } -void CSchemeManager::remove(NL3D::CPSAttribMakerBase *am) +void CSchemeManager::remove(NL3D::CPSAttribMakerBase *am) { TSchemeMap::iterator smIt; for (smIt = _SchemeMap.begin(); smIt != _SchemeMap.end(); ++smIt) @@ -101,7 +101,7 @@ void CSchemeManager::remove(NL3D::CPSAttribMakerBase *am) } // rename a scheme, given a pointer on it -void CSchemeManager::rename(NL3D::CPSAttribMakerBase *am, const std::string &newName) +void CSchemeManager::rename(NL3D::CPSAttribMakerBase *am, const std::string &newName) { TSchemeMap::iterator smIt; for (smIt = _SchemeMap.begin(); smIt != _SchemeMap.end(); ++smIt) @@ -110,7 +110,7 @@ void CSchemeManager::rename(NL3D::CPSAttribMakerBase *am, const std::string &ne } if (smIt != _SchemeMap.end()) { - smIt->second.first = newName; + smIt->second.first = newName; } } From e7a2f7e12191bccaf09cf30213a7eff4ad9c40aa Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 2 Jan 2012 15:41:22 +0100 Subject: [PATCH 159/215] Changed: #825 Remove all warnings when compiling Ryzom --- code/nel/src/3d/patchdlm_context.cpp | 4 ++-- code/nel/src/ligo/primitive.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/nel/src/3d/patchdlm_context.cpp b/code/nel/src/3d/patchdlm_context.cpp index f5a5b578d..e50f1befe 100644 --- a/code/nel/src/3d/patchdlm_context.cpp +++ b/code/nel/src/3d/patchdlm_context.cpp @@ -1018,8 +1018,8 @@ void CPatchDLMContext::computeTextureFar() // compute src pixel const CRGBA *srcPixel= pTile->getPixels(CTileFarBank::diffuse, CTileFarBank::order1); // compute src info, for this tile rot and 256x256 context. - sint srcDeltaX; - sint srcDeltaY; + sint srcDeltaX = 0; + sint srcDeltaY = 0; srcPixel= computeTileFarSrcDeltas(nRot, is256x256, uvOff, srcPixel, srcDeltaX, srcDeltaY); // compute dst coordinate. start writing at pixel (1,1) diff --git a/code/nel/src/ligo/primitive.cpp b/code/nel/src/ligo/primitive.cpp index ff8c71c18..62c4ab9d6 100644 --- a/code/nel/src/ligo/primitive.cpp +++ b/code/nel/src/ligo/primitive.cpp @@ -2118,7 +2118,7 @@ bool CPrimAlias::read (xmlNodePtr xmlNode, const char *filename, uint version, C xmlNodePtr ptNode = CIXml::getFirstChildNode (xmlNode, "ALIAS"); if (ptNode) { - int val; + sint val = 0; if (ReadInt ("VALUE", val, filename, ptNode)) { _Alias = uint32(val); From 2014543ec1a80e2fe9e7aec3f3d155d2b2e69379 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 2 Jan 2012 15:53:21 +0100 Subject: [PATCH 160/215] Fixed: Missing "stdpch.h" include --- code/ryzom/client/src/app_bundle_utils.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ryzom/client/src/app_bundle_utils.cpp b/code/ryzom/client/src/app_bundle_utils.cpp index 214d278b2..b6321ad20 100644 --- a/code/ryzom/client/src/app_bundle_utils.cpp +++ b/code/ryzom/client/src/app_bundle_utils.cpp @@ -14,6 +14,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +#include "stdpch.h" #include "app_bundle_utils.h" #if defined(NL_OS_MAC) From cf84911a8a7d93c760499f46f2a4300d9f4d8c3e Mon Sep 17 00:00:00 2001 From: Krolock Date: Wed, 4 Jan 2012 09:45:25 +0100 Subject: [PATCH 161/215] Changed: added thumbs.db to hgignore --- .hgignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.hgignore b/.hgignore index 4a0311dcc..fcc548cd0 100644 --- a/.hgignore +++ b/.hgignore @@ -144,6 +144,8 @@ external external_stlport 3rdParty .svn +thumbs.db +Thumbs.db # build code/nel/build/* From e61e79eee3017c83a214670ff0f58cb89872f3f7 Mon Sep 17 00:00:00 2001 From: Krolock Date: Wed, 4 Jan 2012 09:51:10 +0100 Subject: [PATCH 162/215] Added: Added bnp manager plugin basic layout, list and unpack for ovqt --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/CMakeLists.txt | 1 + .../src/plugins/bnp_manager/CMakeLists.txt | 46 ++++ .../bnp_manager/bnp_dirtree_dialog.cpp | 87 +++++++ .../plugins/bnp_manager/bnp_dirtree_dialog.h | 80 +++++++ .../plugins/bnp_manager/bnp_dirtree_form.ui | 58 +++++ .../src/plugins/bnp_manager/bnp_file.cpp | 204 ++++++++++++++++ .../src/plugins/bnp_manager/bnp_file.h | 103 ++++++++ .../bnp_manager/bnp_filelist_dialog.cpp | 122 ++++++++++ .../plugins/bnp_manager/bnp_filelist_dialog.h | 81 +++++++ .../bnp_manager/bnp_filelist_dialog.ui | 70 ++++++ .../bnp_manager/bnp_filesystem_model.cpp | 52 ++++ .../bnp_manager/bnp_filesystem_model.h | 49 ++++ .../src/plugins/bnp_manager/bnp_manager.qrc | 9 + .../bnp_manager/bnp_manager_constants.h | 37 +++ .../bnp_manager/bnp_manager_plugin.cpp | 89 +++++++ .../plugins/bnp_manager/bnp_manager_plugin.h | 130 ++++++++++ .../bnp_manager/bnp_manager_window.cpp | 223 ++++++++++++++++++ .../plugins/bnp_manager/bnp_manager_window.h | 146 ++++++++++++ .../plugins/bnp_manager/bnp_manager_window.ui | 50 ++++ .../bnp_manager/images/ic_nel_add_item.png | Bin 0 -> 3270 bytes .../bnp_manager/images/ic_nel_bnp_make.png | Bin 0 -> 25705 bytes .../bnp_manager/images/ic_nel_delete_item.png | Bin 0 -> 1496 bytes .../bnp_manager/images/ic_nel_export.png | Bin 0 -> 27878 bytes .../bnp_manager/images/ic_nel_reset_all.png | Bin 0 -> 30281 bytes .../bnp_manager/ovqt_plugin_bnp_manager.xml | 10 + 25 files changed, 1647 insertions(+) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager.qrc create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.ui create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_add_item.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_bnp_make.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_delete_item.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_export.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_reset_all.png create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/ovqt_plugin_bnp_manager.xml diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt index 426d3aa61..467b00876 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/CMakeLists.txt @@ -7,6 +7,7 @@ ADD_SUBDIRECTORY(disp_sheet_id) ADD_SUBDIRECTORY(object_viewer) ADD_SUBDIRECTORY(georges_editor) ADD_SUBDIRECTORY(translation_manager) +ADD_SUBDIRECTORY(bnp_manager) # Note: Temporarily disabled until development continues. #ADD_SUBDIRECTORY(zone_painter) # Ryzom Specific Plugins diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt new file mode 100644 index 000000000..e06c82c94 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt @@ -0,0 +1,46 @@ +INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_BINARY_DIR} + ${CMAKE_CURRENT_SOURCE_DIR} + ${LIBXML2_INCLUDE_DIR} + ${QT_INCLUDES}) + +FILE(GLOB SRC *.cpp *.h) +SET(OVQT_EXT_SYS_SRC ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_manager.h + ${CMAKE_CURRENT_SOURCE_DIR}/../../extension_system/iplugin_spec.h) + +SET(OVQT_PLUG_BNP_MANAGER_HDR bnp_manager_plugin.h + bnp_manager_window.h + bnp_dirtree_dialog.h + bnp_filesystem_model.h + bnp_file.h + bnp_filelist_dialog.h + ) +SET(OVQT_PLUG_BNP_MANAGER_UIS bnp_dirtree_form.ui + bnp_filelist_dialog.ui + ) + +SET(OVQT_PLUGIN_BNP_MANAGER_RCS bnp_manager.qrc) + +SET(QT_USE_QTGUI TRUE) + +QT4_ADD_RESOURCES(OVQT_PLUGIN_BNP_MANAGER_RC_SRCS ${OVQT_PLUGIN_BNP_MANAGER_RCS}) +QT4_WRAP_CPP(OVQT_PLUG_BNP_MANAGER_MOC_SRC ${OVQT_PLUG_BNP_MANAGER_HDR}) +QT4_WRAP_UI(OVQT_PLUG_BNP_MANAGER_UI_HDRS ${OVQT_PLUG_BNP_MANAGER_UIS}) + +SOURCE_GROUP(QtResources FILES ${OVQT_PLUG_BNP_MANAGER_UIS} ${OVQT_PLUGIN_BNP_MANAGER_RCS}) +SOURCE_GROUP(QtGeneratedUiHdr FILES ${OVQT_PLUG_BNP_MANAGER_UI_HDRS}) +SOURCE_GROUP(QtGeneratedMocSrc FILES ${OVQT_PLUG_BNP_MANAGER_MOC_SRC}) +SOURCE_GROUP("BNP Manager Plugin" FILES ${SRC}) +SOURCE_GROUP("OVQT Extension System" FILES ${OVQT_EXT_SYS_SRC}) + +ADD_LIBRARY(ovqt_plugin_bnp_manager MODULE ${SRC} ${OVQT_PLUG_BNP_MANAGER_MOC_SRC} ${OVQT_EXT_SYS_SRC} ${OVQT_PLUGIN_BNP_MANAGER_RC_SRCS} ${OVQT_PLUG_BNP_MANAGER_UI_HDRS}) + +TARGET_LINK_LIBRARIES(ovqt_plugin_bnp_manager ovqt_plugin_core nelmisc nelgeorges ${QT_LIBRARIES}) + +NL_DEFAULT_PROPS(ovqt_plugin_bnp_manager "NeL, Tools, 3D: Object Viewer Qt Plugin: BNP Manager") +NL_ADD_RUNTIME_FLAGS(ovqt_plugin_bnp_manager) +NL_ADD_LIB_SUFFIX(ovqt_plugin_bnp_manager) + +ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} -DQT_PLUGIN -DQT_SHARED ${QT_DEFINITIONS}) + +INSTALL(TARGETS ovqt_plugin_bnp_manager LIBRARY DESTINATION lib RUNTIME DESTINATION bin ARCHIVE DESTINATION lib COMPONENT tools3d) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp new file mode 100644 index 000000000..9487f66ae --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp @@ -0,0 +1,87 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Project includes +#include "bnp_dirtree_dialog.h" +#include "bnp_filesystem_model.h" + +// Qt includes +#include + +// NeL includes +#include + +namespace BNPManager +{ + +CBnpDirTreeDialog::CBnpDirTreeDialog(QString bnpPath, QWidget *parent) + : QDockWidget(parent), + m_DataPath(bnpPath) +{ + // Setup the dialog + m_ui.setupUi(this); + + // Filter settings to only display files with bnp extension. + // Could be changed to display all files and react according to the extension: + // Bnp file: opened and displayed + // all other files: added to the currently opened bnp file + QStringList filter; + filter << tr("*.bnp"); + + // Setup the directory tree model + m_dirModel= new BNPFileSystemModel; + m_dirModel->setRootPath(m_DataPath); + m_dirModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::AllEntries); + m_dirModel->setNameFilters(filter); + m_dirModel->setNameFilterDisables(0); + + m_ui.dirTree->setModel(m_dirModel); + + m_ui.dirTree->setRootIndex(m_dirModel->index(m_DataPath)); + + // Trigger if one filename is activated + // In future drag&drop should be also possible + connect(m_ui.dirTree, SIGNAL(activated(QModelIndex)), + this, SLOT(fileSelected(QModelIndex))); +} +// *************************************************************************** +CBnpDirTreeDialog::~CBnpDirTreeDialog() +{ + +} +// *************************************************************************** +void CBnpDirTreeDialog::fileSelected(QModelIndex index) +{ + if (index.isValid() && !m_dirModel->isDir(index)) + { + // emit the according signal to BNPManagerWindow class + Q_EMIT selectedForm(m_dirModel->fileInfo(index).filePath()); + } +} +// *************************************************************************** +void CBnpDirTreeDialog::changeFile(QString file) +{ + +} +// *************************************************************************** +void CBnpDirTreeDialog::BnpPathChanged(QString path) +{ + +} +// *************************************************************************** +} + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h new file mode 100644 index 000000000..33b221a4d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h @@ -0,0 +1,80 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef BNP_DIRTREE_DIALOG_H +#define BNP_DIRTREE_DIALOG_H + +// Qt includes +#include + +// STL includes + +// NeL includes + +// Project includes +#include "ui_bnp_dirtree_form.h" + +namespace BNPManager +{ + +class BNPFileSystemModel; + +class CBnpDirTreeDialog : public QDockWidget +{ + Q_OBJECT +public: + + /** + * Constructor + * \param path to root directory, which should be displayed + */ + CBnpDirTreeDialog(QString bnpPath, QWidget *parent = 0); + + /** + * Destructor + */ + ~CBnpDirTreeDialog(); + + /** + * Change the root path for the dir tree view + * \param data path to the new directory + */ + void BnpPathChanged(QString); + +private: + + Ui::CBnpDirTreeDialog m_ui; + + // path ro data root directory + QString m_DataPath; + + BNPFileSystemModel *m_dirModel; + +Q_SIGNALS: + void selectedForm(const QString); + +private Q_SLOTS: + /** + * Triggered if the user activates (double klick on windows) + * a file name in the dir tree view + * \param selected ModelIndex (filename) + */ + void fileSelected(QModelIndex index); + + void changeFile(QString file); +}; +} +#endif \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui new file mode 100644 index 000000000..751c4f055 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_form.ui @@ -0,0 +1,58 @@ + + + CBnpDirTreeDialog + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + + 200 + 141 + + + + QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable + + + BNP Datapath + + + + + 50 + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp new file mode 100644 index 000000000..d6782a9a7 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp @@ -0,0 +1,204 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Project includes +#include "bnp_file.h" + +// Nel includes +#include +#include +#include +#include +#include + +// Qt includes + +using namespace NLMISC; +using namespace std; + + +namespace BNPManager +{ + +NLMISC_SAFE_SINGLETON_IMPL(BNPFileHandle); + +BNPFileHandle::BNPFileHandle() +{ + m_offsetFromBeginning = 0; +} +// *************************************************************************** +BNPFileHandle::~BNPFileHandle() +{ + // Erase the list + m_packedFiles.clear(); +} +// *************************************************************************** +void BNPFileHandle::releaseInstance() +{ + if (_Instance) + { + NLMISC::INelContext::getInstance().releaseSingletonPointer("BNPFileHandle", _Instance); + delete _Instance; + _Instance = NULL; + } +} +// *************************************************************************** +bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) +{ + FILE *bnp = fopen (m_activeBNPFile.c_str(), "rb"); + FILE *out; + if (bnp == NULL) + return false; + + TPackedFilesList::iterator it_files = m_packedFiles.begin(); + + for (it_files; it_files != m_packedFiles.end(); it_files++) + { + // Check if the file should be unpacked or not + if (find(fileList.begin(), fileList.end(), it_files->m_name) != fileList.end()) + { + string filename = dirName + "/" + it_files->m_name; + + out = fopen (filename.c_str(), "wb"); + if (out != NULL) + { + nlfseek64 (bnp, it_files->m_pos, SEEK_SET); + uint8 *ptr = new uint8[it_files->m_size]; + if (fread (ptr, it_files->m_size, 1, bnp) != 1) + { + nlwarning("%s read error", filename.c_str()); + return false; + } + if (fwrite (ptr, it_files->m_size, 1, out) != 1) + { + nlwarning("%s write error", filename.c_str()); + return false; + } + fclose (out); + delete [] ptr; + } + } + } + fclose (bnp); + return true; +} +// *************************************************************************** +// Read the header from a big file +bool BNPFileHandle::readHeader(const std::string &filename) +{ + m_packedFiles.clear(); + + m_activeBNPFile = filename; + + FILE *f = fopen (filename.c_str(), "rb"); + if (f == NULL) + { + nlwarning("Could not open file!"); + return false; + } + + nlfseek64 (f, 0, SEEK_END); + uint32 nFileSize=CFile::getFileSize (filename ); + nlfseek64 (f, nFileSize-sizeof(uint32), SEEK_SET); + + uint32 nOffsetFromBegining; + + if (fread (&nOffsetFromBegining, sizeof(uint32), 1, f) != 1) + { + fclose (f); + return false; + } +#ifdef NL_BIG_ENDIAN + NLMISC_BSWAP32(nOffsetFromBegining); +#endif + + if (nlfseek64 (f, nOffsetFromBegining, SEEK_SET) != 0) + { + nlwarning("Could not read offset from begining"); + fclose (f); + return false; + } + + uint32 nNbFile; + if (fread (&nNbFile, sizeof(uint32), 1, f) != 1) + { + nlwarning("Could not read number of files!"); + fclose (f); + return false; + } + +#ifdef NL_BIG_ENDIAN + NLMISC_BSWAP32(nNbFile); +#endif + + for (uint32 i = 0; i < nNbFile; ++i) + { + uint8 nStringSize; + char sName[256]; + if (fread (&nStringSize, 1, 1, f) != 1) + { + nlwarning("Error reading packed filename!"); + fclose (f); + return false; + } + if (fread (sName, 1, nStringSize, f) != nStringSize) + { + fclose (f); + return false; + } + sName[nStringSize] = 0; + PackedFile tmpPackedFile; + tmpPackedFile.m_name = sName; + if (fread (&tmpPackedFile.m_size, sizeof(uint32), 1, f) != 1) + { + nlwarning("Error reading packed file size!"); + fclose (f); + return false; + } +#ifdef NL_BIG_ENDIAN + NLMISC_BSWAP32(tmpBNPFile.Size); +#endif + if (fread (&tmpPackedFile.m_pos, sizeof(uint32), 1, f) != 1) + { + nlwarning("Error reading packed file position!"); + fclose (f); + return false; + } +#ifdef NL_BIG_ENDIAN + NLMISC_BSWAP32(tmpBNPFile.Pos); +#endif + m_packedFiles.push_back (tmpPackedFile); + } + + fclose (f); + return true; +} +// *************************************************************************** +void BNPFileHandle::list(TPackedFilesList& FileList) +{ + PackedFile tmpFile; + TPackedFilesList::iterator it = m_packedFiles.begin(); + while (it != m_packedFiles.end() ) + { + tmpFile.m_name = it->m_name; + tmpFile.m_pos = it->m_pos; + tmpFile.m_size = it->m_size; + FileList.push_back(tmpFile); + it++; + } +} +// *************************************************************************** +} // namespace BNPManager \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h new file mode 100644 index 000000000..d1c642e3d --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h @@ -0,0 +1,103 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef BNP_FILE_H +#define BNP_FILE_H + +// Project includes + +// Nel includes +#include "nel/misc/types_nl.h" +#include + +// Qt includes +#include + + +namespace BNPManager +{ + +struct PackedFile +{ + std::string m_name; + uint32 m_size; + uint32 m_pos; +}; + +typedef std::vector TPackedFilesList; + +class BNPFileHandle +{ + NLMISC_SAFE_SINGLETON_DECL(BNPFileHandle) + + /** + * Private constructor + */ + BNPFileHandle(); + + /** + * Private destructor + */ + ~BNPFileHandle(); + +public: + // release memory + static void releaseInstance(); + + /*void append (const QString destFilename, const QString origFilename, uint32 sizeToRead); + void packRecurse();*/ + + /** + * Read the header from the bnp file and create a filelist + * \param filename (consisting the whole path) + */ + bool readHeader (const std::string &filename); + + /** + * Append the header to a created bnp file + * \param filename (consisting the whole path) + */ + void appendHeader (const std::string &filename) {}; + + /** + * Create a list of all packed files inside the bnp file + * \param reference to the list, which has to be filled + */ + void list (TPackedFilesList& FileList); + + /** + * Unpack the selected packed files into user defined dir + * \param directory path, where the files should be unpacked + * \param list of files, which has to be unpacked + */ + bool unpack (const std::string &dirName, const std::vector& fileList); + +private: + + TPackedFilesList m_packedFiles; + + // currently opened and displayed bnp file + std::string m_activeBNPFile; + + // offset where the header of the bnp file begins + uint32 m_offsetFromBeginning; + +}; + + +} + +#endif \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp new file mode 100644 index 000000000..b78e6c0bf --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp @@ -0,0 +1,122 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Project includes +#include "bnp_filelist_dialog.h" +#include "bnp_file.h" + +// Qt includes +#include + +// NeL includes +#include + +using namespace std; + +namespace BNPManager +{ + +BnpFileListDialog::BnpFileListDialog(QString bnpPath, QWidget *parent) + : QDockWidget(parent), + m_DataPath(bnpPath) +{ + m_ui.setupUi(this); +} +// *************************************************************************** +BnpFileListDialog::~BnpFileListDialog() +{ + +} +// *************************************************************************** +void BnpFileListDialog::setupTable(int nbrows) +{ + // delete all old entries + m_ui.tableWidget->clear(); + + // set 2 colums: filename and size + m_ui.tableWidget->setColumnCount(2); + + // set number of rows according to the number of files in the bnp file + m_ui.tableWidget->setRowCount(nbrows); + + // only entire rows can be selected + m_ui.tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows); + + // set the horizontal headers + QStringList labels; + labels << tr("Filename") << tr("Size"); + m_ui.tableWidget->setHorizontalHeaderLabels(labels); + + m_ui.tableWidget->horizontalHeader()->setResizeMode(0, QHeaderView::Interactive); + m_ui.tableWidget->horizontalHeader()->setResizeMode(1, QHeaderView::Stretch ); + m_ui.tableWidget->verticalHeader()->hide(); + + // set vertical size a little bit smaller + m_ui.tableWidget->verticalHeader()->setDefaultSectionSize(15); + m_ui.tableWidget->setShowGrid(false); + m_ui.tableWidget->setObjectName("tablewidget"); +} +// *************************************************************************** +bool BnpFileListDialog::loadTable(const QString fileName) +{ + // reference to the BNPFileHandle singletone instance + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + // string vector of all packed files inside a bnp + TPackedFilesList filelist; + int row = 0; + + // read the header from the bnp file + if (!myBNPFileHandle.readHeader( fileName.toStdString()) ) + { + return false; + } + myBNPFileHandle.list( filelist ); + + // create table with number of rows + setupTable(filelist.size()); + + // fill the table items + TPackedFilesList::iterator it = filelist.begin(); + while (it != filelist.end() ) + { + QTableWidgetItem *nameItem = new QTableWidgetItem (it->m_name.c_str() ); + QTableWidgetItem *sizeItem = new QTableWidgetItem (tr("%1 KB").arg(it->m_size)); + m_ui.tableWidget->setItem(row, 0, nameItem); + m_ui.tableWidget->setItem(row, 1, sizeItem); + it++; + row++; + } + + return true; +} +// *************************************************************************** +void BnpFileListDialog::getSelections(TSelectionList& SelectionList) +{ + QModelIndex index; + QAbstractItemModel *model = m_ui.tableWidget->model(); + QItemSelectionModel *selection = m_ui.tableWidget->selectionModel(); + QModelIndexList indexes = selection->selectedRows(); + + Q_FOREACH(index, indexes) + { + QVariant data = model->data(index); + QString filename = data.toString(); + SelectionList.push_back( filename.toStdString() ); + } +} +// *************************************************************************** + +} // namespace BNPManager \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h new file mode 100644 index 000000000..f2cc021a9 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h @@ -0,0 +1,81 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef BNP_FILELIST_DIALOG_H +#define BNP_FILELIST_DIALOG_H + +// Qt includes +#include + +// STL includes +#include +#include + +// NeL includes + +// Project includes +#include "ui_bnp_filelist_dialog.h" + +namespace BNPManager +{ + +typedef std::vector TSelectionList; + +class BnpFileListDialog : public QDockWidget +{ + Q_OBJECT + +public: + + // Constructor + BnpFileListDialog(QString bnpPath, QWidget *parent = 0); + + // Destructor + ~BnpFileListDialog(); + + /** + * Load the bnp file and setup the table view + * \param Filename + * \return true if everything went well + */ + bool loadTable(const QString filename); + + /** + * Set the dimension of the table + * \param number of rows + */ + void setupTable(int nbrows); + + /** + * Fill the files selected in the table view to + * unpack them. + * \param reference to a vector of filenames. + * \return true if everything went well + */ + void getSelections(TSelectionList& SelectionList); + +private: + + Ui::BnpFileListDialog m_ui; + + // common data path as root folder for the dirtree view + QString m_DataPath; + +}; + +} + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.ui new file mode 100644 index 000000000..9f62ff7c0 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.ui @@ -0,0 +1,70 @@ + + + BnpFileListDialog + + + + 0 + 0 + 400 + 300 + + + + + 0 + 0 + + + + + 200 + 141 + + + + true + + + QDockWidget::DockWidgetFloatable|QDockWidget::DockWidgetMovable + + + BNP File List + + + + + 50 + 0 + + + + + + + true + + + QAbstractItemView::NoEditTriggers + + + QAbstractItemView::SelectRows + + + false + + + 15 + + + + + + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp new file mode 100644 index 000000000..eaf6389f5 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp @@ -0,0 +1,52 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see + +#include "bnp_filesystem_model.h" + +#include +#include + +namespace BNPManager +{ + +BNPFileSystemModel::BNPFileSystemModel(QObject *parent) + : QFileSystemModel(parent) +{ + +} +// *************************************************************************** +BNPFileSystemModel::~BNPFileSystemModel() +{ + +} +// *************************************************************************** +int BNPFileSystemModel::columnCount(const QModelIndex &) const +{ + return 1; +} +// *************************************************************************** +QVariant BNPFileSystemModel::data(const QModelIndex& index, int role) const +{ + + if (role == Qt::DecorationRole) + { + if (isDir(index)) + return QApplication::style()->standardIcon(QStyle::SP_DirIcon); + } + return QFileSystemModel::data(index, role); +} +// *************************************************************************** +} // namespace BNPManager diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.h new file mode 100644 index 000000000..2ca086b39 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.h @@ -0,0 +1,49 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see + +#ifndef BNP_FILESYSTEM_MODEL_H +#define BNP_FILESYSTEM_MODEL_H + +#include + +namespace BNPManager +{ + +class BNPFileSystemModel : public QFileSystemModel +{ + Q_OBJECT + +public: + + /** + * Constructor + */ + BNPFileSystemModel(QObject *parent = 0); + + /** + * Destructor + */ + ~BNPFileSystemModel(); + + int columnCount(const QModelIndex &) const; + + QVariant data(const QModelIndex& index, int role) const ; + +}; + +} + +#endif \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager.qrc b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager.qrc new file mode 100644 index 000000000..bf32595d6 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager.qrc @@ -0,0 +1,9 @@ + + + images/ic_nel_bnp_make.png + images/ic_nel_delete_item.png + images/ic_nel_add_item.png + images/ic_nel_export.png + images/ic_nel_reset_all.png + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h new file mode 100644 index 000000000..7ec5eecd0 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_constants.h @@ -0,0 +1,37 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef BNP_MANAGER_CONSTANTS_H +#define BNP_MANAGER_CONSTANTS_H + +namespace BNPManager +{ +namespace Constants +{ +//settings +const char * const BNP_MANAGER_SECTION = "BNPManager"; + +//resources +const char *const ICON_ADD = ":/images/ic_nel_add_item.png"; +const char *const ICON_DELETE = ":/images/ic_nel_delete_item.png"; +const char *const ICON_UNPACK = ":/images/ic_nel_export.png"; +const char *const ICON_CLOSE = ":/images/ic_nel_reset_all.png"; + + +} // namespace Constants +} // namespace Plugin + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.cpp new file mode 100644 index 000000000..70015773b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.cpp @@ -0,0 +1,89 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Project includes +#include "bnp_manager_plugin.h" +#include "bnp_manager_window.h" + +#include "../core/icore.h" +#include "../core/core_constants.h" +#include "../core/menu_manager.h" + +// NeL includes +#include "nel/misc/debug.h" + +// Qt includes +#include +#include + +namespace BNPManager +{ + + BNPManagerPlugin::BNPManagerPlugin() + { + } + + BNPManagerPlugin::~BNPManagerPlugin() + { + Q_FOREACH(QObject *obj, m_autoReleaseObjects) + { + m_plugMan->removeObject(obj); + } + qDeleteAll(m_autoReleaseObjects); + m_autoReleaseObjects.clear(); + } + +bool BNPManagerPlugin::initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString) +{ + Q_UNUSED(errorString); + m_plugMan = pluginManager; + + addAutoReleasedObject(new BNPManagerContext(this)); + return true; +} + +void BNPManagerPlugin::extensionsInitialized() +{ +} + +void BNPManagerPlugin::shutdown() +{ + +} + +void BNPManagerPlugin::setNelContext(NLMISC::INelContext *nelContext) +{ +#ifdef NL_OS_WINDOWS + // Ensure that a context doesn't exist yet. + // This only applies to platforms without PIC, e.g. Windows. + nlassert(!NLMISC::INelContext::isContextInitialised()); +#endif // NL_OS_WINDOWS + m_libContext = new NLMISC::CLibraryContext(*nelContext); +} + +void BNPManagerPlugin::addAutoReleasedObject(QObject *obj) +{ + m_plugMan->addObject(obj); + m_autoReleaseObjects.prepend(obj); +} + +/*void BNPManagerContext::open() +{ + m_BnpManagerWindow->open(); +}*/ +} + +Q_EXPORT_PLUGIN(BNPManager::BNPManagerPlugin) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.h new file mode 100644 index 000000000..55e2e8444 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_plugin.h @@ -0,0 +1,130 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef BNP_MANAGER_PLUGIN_H +#define BNP_MANAGER_PLUGIN_H + +// Project includes +#include "../../extension_system/iplugin.h" +#include "../core/icontext.h" +#include "bnp_manager_window.h" + +// NeL includes +#include "nel/misc/app_context.h" +#include + +// Qt includes +#include +#include + +namespace NLMISC +{ +class CLibraryContext; +} + +namespace ExtensionSystem +{ +class IPluginSpec; +} + +namespace BNPManager +{ +class m_BnpManagerWindow; + +class BNPManagerPlugin : public QObject, public ExtensionSystem::IPlugin +{ + Q_OBJECT + Q_INTERFACES(ExtensionSystem::IPlugin) + +public: + BNPManagerPlugin(); + virtual ~BNPManagerPlugin(); + + virtual bool initialize(ExtensionSystem::IPluginManager *pluginManager, QString *errorString); + virtual void extensionsInitialized(); + virtual void shutdown(); + virtual void setNelContext(NLMISC::INelContext *nelContext); + + void addAutoReleasedObject(QObject *obj); + +protected: + + NLMISC::CLibraryContext *m_libContext; + +private: + + ExtensionSystem::IPluginManager *m_plugMan; + QList m_autoReleaseObjects; +}; + +/** + * Implementation of the IContext interface + * + * \date 2011 + */ + +class BNPManagerContext : public Core::IContext +{ + Q_OBJECT + +public: + // Constructor + BNPManagerContext(QObject *parent = 0) : IContext(parent) + { + // run new manager window app + m_BnpManagerWindow = new BNPManagerWindow(); + } + + // Destructor + virtual ~BNPManagerContext() {} + + virtual QString id() const + { + return QLatin1String("BNPManagerContext"); + } + virtual QString trName() const + { + return tr("BNP Manager"); + } + virtual QIcon icon() const + { + return QIcon(":/images/ic_nel_bnp_make.png"); + } + + virtual void open() + { + m_BnpManagerWindow->open(); + } + + virtual QUndoStack *undoStack() + { + return m_BnpManagerWindow->m_undoStack; + } + + virtual QWidget *widget() + { + return m_BnpManagerWindow; + } + + BNPManagerWindow *m_BnpManagerWindow; + +}; + +} // namespace Plugin + + + +#endif // BNP_MANAGER_PLUGIN_H diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp new file mode 100644 index 000000000..3ed36d181 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp @@ -0,0 +1,223 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +// Project includes +#include "bnp_manager_window.h" +#include "bnp_manager_constants.h" +#include "bnp_dirtree_dialog.h" +#include "bnp_filelist_dialog.h" +#include "bnp_file.h" + +#include "../core/icore.h" +#include "../core/menu_manager.h" +#include "../core/core_constants.h" +#include "../../extension_system/iplugin_spec.h" + +// NeL includes +#include + +// Qt includes +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace NLMISC; + + +namespace BNPManager +{ + +BNPManagerWindow::BNPManagerWindow(QWidget *parent) + : QMainWindow(parent) +{ + // add new mainwindow for sheet dockwidgets + QTableWidget* hideWidget = new QTableWidget(0,0,this); + setCentralWidget(hideWidget); + hideWidget->hide(); + + // Read the settings + readSettings(); + + // create main dialogs and display them + createDialogs(); + + // create actions like open, close, add etc. + createActions(); + + // create a toolbar with icons + createToolBars(); + + // this SLOT is triggered if the user activates a bnp files in the + // dirtree view + connect(m_BnpDirTreeDialog, SIGNAL(selectedForm(const QString)), + this, SLOT(loadFile(const QString))); + + // not used + m_undoStack = new QUndoStack(this); +} +// *************************************************************************** +BNPManagerWindow::~BNPManagerWindow() +{ + writeSettings(); +} +// *************************************************************************** +void BNPManagerWindow::createDialogs() +{ + // create dialog to list the contents of the specified + // bnp data file directory + m_BnpDirTreeDialog = new CBnpDirTreeDialog(tr(m_DataPath.toStdString().c_str()),this); + addDockWidget(Qt::LeftDockWidgetArea, m_BnpDirTreeDialog); + m_BnpDirTreeDialog->setVisible(true); + restoreDockWidget(m_BnpDirTreeDialog); + + // create dialog to list the packed file contents of bnp files on + // the right hand side + m_BnpFileListDialog = new BnpFileListDialog(m_DataPath,this); + addDockWidget(Qt::RightDockWidgetArea, m_BnpFileListDialog); + m_BnpFileListDialog->setVisible(true); + restoreDockWidget(m_BnpFileListDialog); +} +// *************************************************************************** +void BNPManagerWindow::createActions() +{ + // open action + m_openAction = new QAction(tr("&Open..."), this); + m_openAction->setIcon(QIcon(Core::Constants::ICON_OPEN)); + m_openAction->setStatusTip(tr("Open file")); + connect(m_openAction, SIGNAL(triggered()), this, SLOT( open() )); + + // close action + m_closeAction = new QAction(tr("&Close..."), this); + m_closeAction->setIcon(QIcon(Constants::ICON_CLOSE)); + m_closeAction->setStatusTip(tr("Close the BNP File")); + connect(m_closeAction, SIGNAL(triggered()), this, SLOT( close() )); + + // add files into the bnp file + m_addFilesAction = new QAction(tr("&Add..."), this); + m_addFilesAction->setIcon(QIcon(Constants::ICON_ADD)); + m_addFilesAction->setStatusTip(tr("Add Files to BNP")); + connect(m_addFilesAction, SIGNAL(triggered()), this, SLOT( addFiles() )); + + // delete files from the bnp file + m_deleteFilesAction = new QAction(tr("&Delete..."), this); + m_deleteFilesAction->setIcon(QIcon(Constants::ICON_DELETE)); + m_deleteFilesAction->setStatusTip(tr("Delete Files")); + connect(m_deleteFilesAction, SIGNAL(triggered()), this, SLOT( deleteFiles() )); + + // unpack selected files into user defined dir + m_unpackFilesAction = new QAction(tr("&Unpack..."), this); + m_unpackFilesAction->setIcon(QIcon(Constants::ICON_UNPACK)); + m_unpackFilesAction->setStatusTip(tr("Unpack Files")); + connect(m_unpackFilesAction, SIGNAL(triggered()), this, SLOT( unpackFiles() )); +} +// *************************************************************************** +void BNPManagerWindow::createToolBars() +{ + m_fileToolBar = addToolBar(tr("&File")); + m_fileToolBar->addAction(m_openAction); + m_fileToolBar->addAction(m_closeAction); + + m_toolsBar = addToolBar(tr("&Tools")); + m_toolsBar->addAction(m_addFilesAction); + m_toolsBar->addAction(m_deleteFilesAction); + m_toolsBar->addAction(m_unpackFilesAction); +} +// *************************************************************************** +bool BNPManagerWindow::loadFile(const QString fileName) +{ + m_BnpFileListDialog->loadTable(fileName); + return true; +} +// *************************************************************************** +void BNPManagerWindow::open() +{ + QString fileName; + // file dialog to select with file should be opened + fileName = QFileDialog::getOpenFileName(this, + tr("Open BNP file"), tr(m_DataPath.toStdString().c_str()), tr("BNP Files (*.bnp)")); + + // check if there is a filename + if (fileName.isNull()) + return; + loadFile(fileName); +} +// *************************************************************************** +void BNPManagerWindow::close() +{ + //TODO +} +// *************************************************************************** +void BNPManagerWindow::addFiles() +{ + //TODO +} +// *************************************************************************** +void BNPManagerWindow::deleteFiles() +{ + //TODO +} +// *************************************************************************** +void BNPManagerWindow::unpackFiles() +{ + QFileDialog filedialog(this); + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + vector selectedrows; + + m_BnpFileListDialog->getSelections(selectedrows); + + // Check if files were selected. If not, inform the user. + // TODO: Ask the user if nothing was selected, if he wants to unpack all + // files. This is more like Winzip. + if (selectedrows.empty()) + { + QMessageBox::information(this, tr("BNP Manager"), + tr("No files were selected to unpack!"), + QMessageBox::Ok, + QMessageBox::Ok); + return; + } + + QString dir = QFileDialog::getExistingDirectory(this, tr("Open Directory"), + tr(m_DataPath.toStdString().c_str()), + QFileDialog::ShowDirsOnly + | QFileDialog::DontResolveSymlinks); + + if (myBNPFileHandle.unpack(dir.toStdString(),selectedrows)) + { + QMessageBox::information(this, tr("BNP Manager"), + tr("All files has been exported successfully."), + QMessageBox::Ok, + QMessageBox::Ok); + } +} +// *************************************************************************** +void BNPManagerWindow::readSettings() +{ + QSettings *settings = Core::ICore::instance()->settings(); + + settings->beginGroup(Core::Constants::DATA_PATH_SECTION); + m_DataPath = settings->value(Core::Constants::ASSETS_PATH, "w:/database").toString(); + settings->endGroup(); +} +// *************************************************************************** +void BNPManagerWindow::writeSettings() +{ +} +} // namespace BNPManager diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h new file mode 100644 index 000000000..b38e2b7be --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h @@ -0,0 +1,146 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +#ifndef BNP_MANAGER_WINDOW_H +#define BNP_MANAGER_WINDOW_H + +// Project includes +//#include "ui_bnp_manager_window.h" + +// Qt includes +#include +#include +#include +#include + + +namespace BNPManager +{ + +class CBnpDirTreeDialog; +class BnpFileListDialog; +class BNPFileHandle; + +/** + * Main window class. Derived from QMainWindow and implements + * the basic layout like menue, toolbars and dialogs. + * + * \date 2011 + */ + +class BNPManagerWindow : public QMainWindow +{ + Q_OBJECT + +public: + + // Constructor + BNPManagerWindow(QWidget *parent = 0); + + //Destructor + ~BNPManagerWindow(); + + + QUndoStack *m_undoStack; + +public Q_SLOTS: + + /** + * Open a file dialog to choose which file should be opened. + * \return Filename string + */ + void open(); + + /** + * Load a certain bnp file into the manager + * \param Filename + * \return true if everything went well + */ + bool loadFile(const QString fileName); + + /** + * close an opened bnp file and reset all views + */ + void close(); + + /** + * Add files into an opened bnp file. + * \param Filelist + */ + void addFiles(); + + /** + * Unpack the files marked in the filelist dialog into user defined + * directory. + * \param TBD + * \return true if everything went well + */ + void unpackFiles(); + + /** + * Delete marked files from the bnp file + * \param TBD + */ + void deleteFiles(); + +private: + + /** + * Read plugin settings and set the window accordingly + */ + void readSettings(); + + /** + * Write plugin settings + */ + void writeSettings(); + + /** + * Create all plugin dialogs + */ + void createDialogs(); + + /** + * Create all plugin actions + */ + void createActions(); + + /** + * Create the plugin toolbar + */ + void createToolBars(); + + QToolBar *m_fileToolBar; + QToolBar *m_toolsBar; + + QAction *m_openAction; + QAction *m_closeAction; + QAction *m_addFilesAction; + QAction *m_unpackFilesAction; + QAction *m_deleteFilesAction; + + CBnpDirTreeDialog *m_BnpDirTreeDialog; + BnpFileListDialog *m_BnpFileListDialog; + + QString m_DataPath; + + BNPFileHandle *m_BNPFileHandle; + +}; /* class BNPManagerWindow */ + +} /* namespace Plugin */ + +#endif diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.ui b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.ui new file mode 100644 index 000000000..4e695f72c --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.ui @@ -0,0 +1,50 @@ + + + BNPManagerWindow + + + + 0 + 0 + 800 + 600 + + + + BNP Manager + + + + + + + QWidget#centralwidget { + image: url(:/images/ic_nel_georges_editor.png); + } + + + + + + + + + + + + + toolBar + + + TopToolBarArea + + + false + + + + + + + + diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_add_item.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_add_item.png new file mode 100644 index 0000000000000000000000000000000000000000..bde338f7851d2675b29272c7dcf9471196a8f6cc GIT binary patch literal 3270 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 Eg5p97)c^nh literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_bnp_make.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_bnp_make.png new file mode 100644 index 0000000000000000000000000000000000000000..19b749b1da76ec5ae8a7f063c7127f5672915ef6 GIT binary patch literal 25705 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 literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_delete_item.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_delete_item.png new file mode 100644 index 0000000000000000000000000000000000000000..a5a1787d593591f855be9a7eb8da271cc8d8f214 GIT binary patch literal 1496 zcmaKs{XY{30LC|C-c2!W(h#B2QhA@kOt{brn;~tK$;-mn#DM?-zdb>=i zu*@RTd8@_pvUxpuxyoG1>Bsxw?uX}jKHujL_-0@|T~w8{l>h*Ms+%k3&``sHb<81=-~(e+|N^9C&_KHVw5X^3IM3J{S1(sAHJ}8!egC%W4+FY#L`JIX8~B! zIZ7C+< z6bcMhk7Jz$z zt<~^Npzeyeat^;b(tkMW#bRkwb5S`|-P(cZ{!1geIKC7Tzpi^Q9XhN7r*| zh)Tmw?h}4)r(e%EPx*~jbiDn>W9k_F^gyx#1J1b!&uZuD!J$Duh_I|$-B|3)pjcABT`DZr@@$gnJ?g*7niqC zPg~-q=_uM61|+bWZC}wlE4;C-L;LVrU6J(R@D0Z4=p`kt`4==LnW(d8wXiBZND)3NugXyk}j`B>QSp`rx>7I47#1U z_C?Zqn_!w_iq*Q=7#33$KOO+R;CW!bcdn#k;~tqqg>*o8s|#;{%M1@^2B=`1X*QOW z`~~EL<8YMd;Wk~hP%ln9+T{3weTCGPC>#3IdbbgtfSL(^;<-$(&+()%CWY(ZY}rUs zYZ^=Sws7E+dDpj6e_>>!qz;jFg;SMzWx6GvaM=_CJv#h+^qF(IggsR{8BYMQlBT|c zLR(J;=NJUkn>YRLcdDtY?zXt@zIEUuTe=1hEndEU6|7%)i#=`5cs5S}P9Mh=PcJfo z{ZbSwueq|yZyG~GMGk&1;XI&33fgfyu~6eIQgk!Fkv4<^W8F=|9U5oXINb+#fCen_ z{sB?u3Xfi@SxqMAC%ZI%1BJIf0uMiCx!yXz62rndMv z$=6toP8$LinV$s7C|ZjMS5+@W1rn4NxN=o7bmp|tGx-1vXy7V6(LK7Owl1b<1|o_O z>bY9rcsZ;+(mT(U8Vqy|@K*SC7Xeg!@WG?ntw{ZC`G3Vh1-90Bf1vJ=9f)jI$P4Q6YBQ@Z8;^^H#>9pe+1$q6DXU$-5VbBeJ(3kUiuh943T}3C$!G=4Zw&rX zIZx;etQ%$b10W;H`0fD|-g&n~Eq? zJZ zC}pc4u@0qD`7mQX=zMD+8o3Hi#&31p7{mxJ*6-KkigeYIgt}@Xn1a05YyE6U8VHU~ z8PL=CTn*0W$(#-|89Sz`9P!(hY?pOfm^ZQVXfjUeBhD+B#yUg0vkok?_%`$Xq0`=> g{}tB1bjDO$2a)_&SEAML{IoE@&Dj&vfF`E?18{b*hyVZp literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_export.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_export.png new file mode 100644 index 0000000000000000000000000000000000000000..9fc71c09dab0615021bb5c813d47e3f5b2a72623 GIT binary patch literal 27878 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 literal 0 HcmV?d00001 diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_reset_all.png b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/images/ic_nel_reset_all.png new file mode 100644 index 0000000000000000000000000000000000000000..c6728740865db94a38a7d740accd930a9c8326d5 GIT binary patch literal 30281 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 + ovqt_plugin_bnp_manager + BNPManager + 0.1 + Krolock + Edit BNP Files + + + + \ No newline at end of file From 15fa0b67f74f9e6b14bd2efcc6cfd6523e4c5da5 Mon Sep 17 00:00:00 2001 From: kervala Date: Sat, 7 Jan 2012 19:14:24 +0100 Subject: [PATCH 163/215] Fixed: ryzom_client_patcher missing file --- code/ryzom/tools/client/client_patcher/CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/code/ryzom/tools/client/client_patcher/CMakeLists.txt b/code/ryzom/tools/client/client_patcher/CMakeLists.txt index 5ea9b320d..afe2cbc31 100644 --- a/code/ryzom/tools/client/client_patcher/CMakeLists.txt +++ b/code/ryzom/tools/client/client_patcher/CMakeLists.txt @@ -1,4 +1,5 @@ FILE(GLOB SRC main.cpp + ${CMAKE_SOURCE_DIR}/ryzom/client/src/app_bundle_utils.cpp ${CMAKE_SOURCE_DIR}/ryzom/client/src/client_cfg.cpp ${CMAKE_SOURCE_DIR}/ryzom/client/src/login_patch.cpp ${CMAKE_SOURCE_DIR}/ryzom/client/src/login_xdelta.cpp From 6a6115061c8604a3d358dda8dd3c32f2e8ffe202 Mon Sep 17 00:00:00 2001 From: kervala Date: Tue, 10 Jan 2012 10:19:52 +0100 Subject: [PATCH 164/215] Fixed: #1420 Client patcher compilation under Mac OS X --- code/ryzom/tools/client/client_patcher/CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/code/ryzom/tools/client/client_patcher/CMakeLists.txt b/code/ryzom/tools/client/client_patcher/CMakeLists.txt index afe2cbc31..4a8b47bdf 100644 --- a/code/ryzom/tools/client/client_patcher/CMakeLists.txt +++ b/code/ryzom/tools/client/client_patcher/CMakeLists.txt @@ -22,6 +22,11 @@ TARGET_LINK_LIBRARIES(ryzom_client_patcher ryzom_sevenzip ${CURL_LIBRARIES}) +IF(APPLE) + FIND_LIBRARY(FOUNDATION_LIBRARY Foundation) + TARGET_LINK_LIBRARIES(ryzom_client_patcher ${FOUNDATION_LIBRARY}) +ENDIF(APPLE) + ADD_DEFINITIONS(${LIBXML2_DEFINITIONS} ${CURL_DEFINITIONS} -DRZ_NO_CLIENT -DNL_USE_SEVENZIP) NL_DEFAULT_PROPS(ryzom_client_patcher "Ryzom, Tools: Ryzom Client Patcher") From ba66f5e07047e2d8eb85409da3d85ff146109cef Mon Sep 17 00:00:00 2001 From: Krolock Date: Wed, 11 Jan 2012 20:54:54 +0100 Subject: [PATCH 165/215] Added: Implemented bnp_manager_plugin add and delete files --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/bnp_file.cpp | 194 +++++++++++++++++- .../src/plugins/bnp_manager/bnp_file.h | 46 ++++- .../bnp_manager/bnp_manager_window.cpp | 76 ++++++- .../plugins/bnp_manager/bnp_manager_window.h | 9 +- 4 files changed, 306 insertions(+), 19 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp index d6782a9a7..370b6e3a9 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp @@ -33,6 +33,12 @@ using namespace std; namespace BNPManager { +PackedFile::PackedFile() +{ + m_size = 0; + m_pos = 0; +} + NLMISC_SAFE_SINGLETON_IMPL(BNPFileHandle); BNPFileHandle::BNPFileHandle() @@ -58,7 +64,7 @@ void BNPFileHandle::releaseInstance() // *************************************************************************** bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) { - FILE *bnp = fopen (m_activeBNPFile.c_str(), "rb"); + FILE *bnp = fopen (m_openedBNPFile.c_str(), "rb"); FILE *out; if (bnp == NULL) return false; @@ -97,13 +103,13 @@ bool BNPFileHandle::unpack(const string &dirName, const vector& fileList } // *************************************************************************** // Read the header from a big file -bool BNPFileHandle::readHeader(const std::string &filename) +bool BNPFileHandle::readHeader(const std::string &filePath) { m_packedFiles.clear(); - m_activeBNPFile = filename; + m_openedBNPFile = filePath; - FILE *f = fopen (filename.c_str(), "rb"); + FILE *f = fopen (filePath.c_str(), "rb"); if (f == NULL) { nlwarning("Could not open file!"); @@ -111,7 +117,7 @@ bool BNPFileHandle::readHeader(const std::string &filename) } nlfseek64 (f, 0, SEEK_END); - uint32 nFileSize=CFile::getFileSize (filename ); + uint32 nFileSize=CFile::getFileSize (filePath ); nlfseek64 (f, nFileSize-sizeof(uint32), SEEK_SET); uint32 nOffsetFromBegining; @@ -162,6 +168,7 @@ bool BNPFileHandle::readHeader(const std::string &filename) sName[nStringSize] = 0; PackedFile tmpPackedFile; tmpPackedFile.m_name = sName; + tmpPackedFile.m_path = m_openedBNPFile; if (fread (&tmpPackedFile.m_size, sizeof(uint32), 1, f) != 1) { nlwarning("Error reading packed file size!"); @@ -196,9 +203,186 @@ void BNPFileHandle::list(TPackedFilesList& FileList) tmpFile.m_name = it->m_name; tmpFile.m_pos = it->m_pos; tmpFile.m_size = it->m_size; + tmpFile.m_path = it->m_path; FileList.push_back(tmpFile); it++; } } // *************************************************************************** +bool BNPFileHandle::writeHeader( const std::string &filePath, uint32 offset ) +{ + FILE *f = fopen (filePath.c_str(), "ab"); + if (f == NULL) return false; + + uint32 nNbFile = (uint32)m_packedFiles.size(); + if (fwrite (&nNbFile, sizeof(uint32), 1, f) != 1) + { + fclose(f); + return false; + } + + for (uint32 i = 0; i < nNbFile; ++i) + { + uint8 nStringSize = (uint8)m_packedFiles[i].m_name.size(); + if (fwrite (&nStringSize, 1, 1, f) != 1) + { + fclose(f); + return false; + } + + if (fwrite (m_packedFiles[i].m_name.c_str(), 1, nStringSize, f) != nStringSize) + { + fclose(f); + return false; + } + + if (fwrite (&m_packedFiles[i].m_size, sizeof(uint32), 1, f) != 1) + { + fclose(f); + return false; + } + + if (fwrite (&m_packedFiles[i].m_pos, sizeof(uint32), 1, f) != 1) + { + fclose(f); + return false; + } + } + + if (fwrite (&offset, sizeof(uint32), 1, f) != 1) + { + fclose(f); + return false; + } + + fclose (f); + return true; +} +// *************************************************************************** +void BNPFileHandle::fileNames(std::vector &fileNames) +{ + TPackedFilesList::iterator it = m_packedFiles.begin(); + while (it != m_packedFiles.end() ) + { + fileNames.push_back(it->m_name); + it++; + } +} +// *************************************************************************** +void BNPFileHandle::addFiles( const vector &filePathes) +{ + uint32 OffsetFromBegining = 0; + + // create packed files and add them to the private vector + vector::const_iterator it_vec = filePathes.begin(); + while (it_vec != filePathes.end() ) + { + PackedFile tmpFile; + tmpFile.m_name = CFile::getFilename (*it_vec); + // Leave position to 0 and set the value during the new bnp file is creating + // We need the position only for the header at the end + tmpFile.m_pos = 0; + tmpFile.m_size = CFile::getFileSize(*it_vec); + tmpFile.m_path = *it_vec; + m_packedFiles.push_back( tmpFile ); + + it_vec++; + } + + // sort packed files alphabetic + std::sort ( m_packedFiles.begin(), m_packedFiles.end(), compare ); + + // create a new temporary bnp file with extension *.tmp + TPackedFilesList::iterator it_packed = m_packedFiles.begin(); + while (it_packed != m_packedFiles.end() ) + { + append(m_openedBNPFile + ".tmp", *it_packed); + // Set now the new offset for the new header + it_packed->m_pos = OffsetFromBegining; + OffsetFromBegining += it_packed->m_size; + + it_packed++; + } + + writeHeader(m_openedBNPFile + ".tmp", OffsetFromBegining); + + CFile::deleteFile( m_openedBNPFile ); + string src = m_openedBNPFile + ".tmp"; + CFile::moveFile( m_openedBNPFile.c_str(), src.c_str() ); +} +// *************************************************************************** +void BNPFileHandle::deleteFiles( const vector& fileNames) +{ + vector::const_iterator it_vec; + TPackedFilesList::iterator it_packed; + uint32 OffsetFromBegining = 0; + string tmpFile = m_openedBNPFile + ".tmp"; + + // create a new temporary bnp file with extension *.tmp + it_packed = m_packedFiles.begin(); + while (it_packed != m_packedFiles.end() ) + { + // check each packed file if it should be deleted + it_vec = find (fileNames.begin(), fileNames.end(), it_packed->m_name ); + if ( it_vec != fileNames.end() ) + { + nlinfo("Deleting file %s.", it_packed->m_name.c_str() ); + it_packed = m_packedFiles.erase(it_packed); + } + else + { + append(tmpFile, *it_packed); + // Set now the new offset for the new header + it_packed->m_pos = OffsetFromBegining; + OffsetFromBegining += it_packed->m_size; + + it_packed++; + } + } + nldebug("Writing header..."); + + writeHeader(tmpFile, OffsetFromBegining); + + CFile::deleteFile( m_openedBNPFile ); + string src = m_openedBNPFile + ".tmp"; + CFile::moveFile( m_openedBNPFile.c_str(), src.c_str() ); +} +// *************************************************************************** +void BNPFileHandle::append(const string &destination, const PackedFile &source) +{ + // check if the file exists and create one if not + if ( !CFile::fileExists(destination) ) + CFile::createEmptyFile( destination ); + + FILE *bnpfile = fopen(destination.c_str(), "ab"); + FILE *packedfile = fopen(source.m_path.c_str(), "rb"); + if (bnpfile == NULL) return; + if (packedfile == NULL) { fclose(bnpfile); return; } + + uint8 *ptr = new uint8[source.m_size]; + + // check if the source is a bnp file. + if ( nlstricmp( CFile::getExtension(source.m_path), "bnp" ) == 0 ) + { + // Jump to the file position inside the bnp + nlfseek64(packedfile, source.m_pos, SEEK_SET); + } + // Read the source + if (fread (ptr, source.m_size, 1, packedfile) != 1) + nlwarning("%s read error", source.m_path.c_str()); + + // Append the data to the destination + if (fwrite (ptr, source.m_size, 1, bnpfile) != 1) + nlwarning("%s write error", destination.c_str()); + + delete [] ptr; + + fclose(packedfile); + fclose(bnpfile); +} +// *************************************************************************** +bool BNPFileHandle::compare(const PackedFile &left, const PackedFile &right) +{ + return nlstricmp (left.m_name.c_str(), right.m_name.c_str()) < 0; +} } // namespace BNPManager \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h index d1c642e3d..e03d0e664 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.h @@ -32,9 +32,11 @@ namespace BNPManager struct PackedFile { + PackedFile(); std::string m_name; uint32 m_size; uint32 m_pos; + std::string m_path; }; typedef std::vector TPackedFilesList; @@ -64,7 +66,9 @@ public: * Read the header from the bnp file and create a filelist * \param filename (consisting the whole path) */ - bool readHeader (const std::string &filename); + bool readHeader (const std::string &filePath); + + bool writeHeader (const std::string &filePath, uint32 offset); /** * Append the header to a created bnp file @@ -73,10 +77,28 @@ public: void appendHeader (const std::string &filename) {}; /** - * Create a list of all packed files inside the bnp file - * \param reference to the list, which has to be filled + * Create a vector of all packed files inside the bnp file + * \param reference to the vector, which has to be filled */ void list (TPackedFilesList& FileList); + + /** + * Create a vector of all file names inside the bnp file + * \param reference to the vector, which has to be filled + */ + void fileNames( std::vector& fileNames ); + + /** + * Add files to the current aktive bnp file + * \param vector of file pathes to add + */ + void addFiles( const std::vector& filePathes ); + + /** + * Delete files from the current aktive bnp file + * \param vector of files names + */ + void deleteFiles (const std::vector& fileNames); /** * Unpack the selected packed files into user defined dir @@ -85,19 +107,33 @@ public: */ bool unpack (const std::string &dirName, const std::vector& fileList); + /** + * Compares two filenames + * \param left: left packed file + * \param right: right packed file + * \return: TODO + */ + static bool compare(const PackedFile &left, const PackedFile &right); + private: + /** + * Append one file to an existing bnp file + * \param destination: the active bnp file to append the file + * \param source: the source file to pack + */ + void append( const std::string& destination, const PackedFile& source ); + TPackedFilesList m_packedFiles; // currently opened and displayed bnp file - std::string m_activeBNPFile; + std::string m_openedBNPFile; // offset where the header of the bnp file begins uint32 m_offsetFromBeginning; }; - } #endif \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp index 3ed36d181..33157733e 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp @@ -28,6 +28,7 @@ // NeL includes #include +#include // Qt includes #include @@ -153,9 +154,11 @@ void BNPManagerWindow::open() fileName = QFileDialog::getOpenFileName(this, tr("Open BNP file"), tr(m_DataPath.toStdString().c_str()), tr("BNP Files (*.bnp)")); - // check if there is a filename + // Check if filename is empty if (fileName.isNull()) return; + + m_openedBNPFile = fileName; loadFile(fileName); } // *************************************************************************** @@ -166,12 +169,73 @@ void BNPManagerWindow::close() // *************************************************************************** void BNPManagerWindow::addFiles() { - //TODO + // reference to the BNPFileHandle singletone instance + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + + // vector of all current packed filenames + vector currentFiles; + + // vector of files to add + vector addFiles; + + // open a file dialog and to add files + QStringList FileList; + + FileList = QFileDialog::getOpenFileNames(this,tr("Add Files..."), + QDir::currentPath(), tr("All Files (*.*)") ); + + // get all current filenames from the opened bnp file + myBNPFileHandle.fileNames(currentFiles); + + QStringList::iterator it_list = FileList.begin(); + while (it_list != FileList.end() ) + { + string fileName = CFile::getFilename (it_list->toStdString() ); + if ( std::find(currentFiles.begin(), currentFiles.end(), fileName ) != currentFiles.end() ) + { + // Ask the user if he wants to override the existing file + // atm only warn the user and do not override + QMessageBox::warning(this, tr("BNP Manager"), + tr("File is already in the list!"), + QMessageBox::Ok, + QMessageBox::Ok); + } + else + { + addFiles.push_back( it_list->toStdString() ); + // log it + nlinfo("Add file %s", fileName.c_str() ); + } + it_list++; + } + + if ( !addFiles.empty() ) + { + myBNPFileHandle.addFiles( addFiles ); + } + loadFile(m_openedBNPFile); } // *************************************************************************** void BNPManagerWindow::deleteFiles() { - //TODO + QFileDialog filedialog(this); + BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); + vector selectedRows; + + m_BnpFileListDialog->getSelections(selectedRows); + + // Check if files were selected. If not, inform the user. + if (selectedRows.empty()) + { + QMessageBox::information(this, tr("BNP Manager"), + tr("No files selected!"), + QMessageBox::Ok, + QMessageBox::Ok); + return; + } + + myBNPFileHandle.deleteFiles(selectedRows); + loadFile(m_openedBNPFile); } // *************************************************************************** void BNPManagerWindow::unpackFiles() @@ -188,7 +252,7 @@ void BNPManagerWindow::unpackFiles() if (selectedrows.empty()) { QMessageBox::information(this, tr("BNP Manager"), - tr("No files were selected to unpack!"), + tr("No files selected!"), QMessageBox::Ok, QMessageBox::Ok); return; @@ -198,6 +262,10 @@ void BNPManagerWindow::unpackFiles() tr(m_DataPath.toStdString().c_str()), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + + // If anything went wrong or the user pressed "cancel" + if ( dir.isEmpty() ) + return; if (myBNPFileHandle.unpack(dir.toStdString(),selectedrows)) { diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h index b38e2b7be..b31d17a09 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.h @@ -101,11 +101,11 @@ private: /** * Read plugin settings and set the window accordingly */ - void readSettings(); - + void readSettings(); + /** * Write plugin settings - */ + */ void writeSettings(); /** @@ -136,8 +136,7 @@ private: BnpFileListDialog *m_BnpFileListDialog; QString m_DataPath; - - BNPFileHandle *m_BNPFileHandle; + QString m_openedBNPFile; }; /* class BNPManagerWindow */ From 22ff1f79dac876393a0f3becb488529d3957ce5e Mon Sep 17 00:00:00 2001 From: Krolock Date: Wed, 11 Jan 2012 23:52:18 +0100 Subject: [PATCH 166/215] Changed: bnp_manager_plugin file handles to CIFile/COFile to use serial system --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/bnp_file.cpp | 139 +++++------------- 1 file changed, 37 insertions(+), 102 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp index 370b6e3a9..7c404b347 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp @@ -64,9 +64,8 @@ void BNPFileHandle::releaseInstance() // *************************************************************************** bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) { - FILE *bnp = fopen (m_openedBNPFile.c_str(), "rb"); - FILE *out; - if (bnp == NULL) + CIFile bnp; + if ( !bnp.open(m_openedBNPFile) ) return false; TPackedFilesList::iterator it_files = m_packedFiles.begin(); @@ -78,27 +77,17 @@ bool BNPFileHandle::unpack(const string &dirName, const vector& fileList { string filename = dirName + "/" + it_files->m_name; - out = fopen (filename.c_str(), "wb"); - if (out != NULL) + COFile out; + if ( out.open(filename) ) { - nlfseek64 (bnp, it_files->m_pos, SEEK_SET); + bnp.seek(it_files->m_pos, IStream::begin); uint8 *ptr = new uint8[it_files->m_size]; - if (fread (ptr, it_files->m_size, 1, bnp) != 1) - { - nlwarning("%s read error", filename.c_str()); - return false; - } - if (fwrite (ptr, it_files->m_size, 1, out) != 1) - { - nlwarning("%s write error", filename.c_str()); - return false; - } - fclose (out); + bnp.serialBuffer(ptr,it_files->m_size); + out.serialBuffer(ptr,it_files->m_size); delete [] ptr; } } } - fclose (bnp); return true; } // *************************************************************************** @@ -109,42 +98,32 @@ bool BNPFileHandle::readHeader(const std::string &filePath) m_openedBNPFile = filePath; - FILE *f = fopen (filePath.c_str(), "rb"); - if (f == NULL) + CIFile bnp; + if ( !bnp.open (filePath) ) { nlwarning("Could not open file!"); return false; } - nlfseek64 (f, 0, SEEK_END); + bnp.seek(0, IStream::end); uint32 nFileSize=CFile::getFileSize (filePath ); - nlfseek64 (f, nFileSize-sizeof(uint32), SEEK_SET); + bnp.seek(nFileSize-sizeof(uint32), IStream::begin); uint32 nOffsetFromBegining; - if (fread (&nOffsetFromBegining, sizeof(uint32), 1, f) != 1) - { - fclose (f); - return false; - } + bnp.serial(nOffsetFromBegining); #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nOffsetFromBegining); #endif - if (nlfseek64 (f, nOffsetFromBegining, SEEK_SET) != 0) + if ( !bnp.seek (nOffsetFromBegining, IStream::begin) ) { nlwarning("Could not read offset from begining"); - fclose (f); return false; } uint32 nNbFile; - if (fread (&nNbFile, sizeof(uint32), 1, f) != 1) - { - nlwarning("Could not read number of files!"); - fclose (f); - return false; - } + bnp.serial(nNbFile); #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(nNbFile); @@ -153,44 +132,28 @@ bool BNPFileHandle::readHeader(const std::string &filePath) for (uint32 i = 0; i < nNbFile; ++i) { uint8 nStringSize; + uint32 fileSize; + uint32 filePos; char sName[256]; - if (fread (&nStringSize, 1, 1, f) != 1) - { - nlwarning("Error reading packed filename!"); - fclose (f); - return false; - } - if (fread (sName, 1, nStringSize, f) != nStringSize) - { - fclose (f); - return false; - } + bnp.serial(nStringSize); + bnp.serialBuffer( (uint8*)sName, nStringSize); sName[nStringSize] = 0; PackedFile tmpPackedFile; tmpPackedFile.m_name = sName; tmpPackedFile.m_path = m_openedBNPFile; - if (fread (&tmpPackedFile.m_size, sizeof(uint32), 1, f) != 1) - { - nlwarning("Error reading packed file size!"); - fclose (f); - return false; - } + + bnp.serial(fileSize); + tmpPackedFile.m_size = fileSize; #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(tmpBNPFile.Size); #endif - if (fread (&tmpPackedFile.m_pos, sizeof(uint32), 1, f) != 1) - { - nlwarning("Error reading packed file position!"); - fclose (f); - return false; - } + bnp.serial(filePos); + tmpPackedFile.m_pos = filePos; #ifdef NL_BIG_ENDIAN NLMISC_BSWAP32(tmpBNPFile.Pos); #endif m_packedFiles.push_back (tmpPackedFile); } - - fclose (f); return true; } // *************************************************************************** @@ -211,52 +174,24 @@ void BNPFileHandle::list(TPackedFilesList& FileList) // *************************************************************************** bool BNPFileHandle::writeHeader( const std::string &filePath, uint32 offset ) { - FILE *f = fopen (filePath.c_str(), "ab"); - if (f == NULL) return false; + COFile bnp; + if ( !bnp.open(filePath, true) ) return false; - uint32 nNbFile = (uint32)m_packedFiles.size(); - if (fwrite (&nNbFile, sizeof(uint32), 1, f) != 1) - { - fclose(f); - return false; - } + uint32 nNbFile = (uint32)m_packedFiles.size(); + bnp.serial(nNbFile); - for (uint32 i = 0; i < nNbFile; ++i) - { - uint8 nStringSize = (uint8)m_packedFiles[i].m_name.size(); - if (fwrite (&nStringSize, 1, 1, f) != 1) - { - fclose(f); - return false; - } + for (uint32 i = 0; i < nNbFile; ++i) + { + uint8 nStringSize = (uint8)m_packedFiles[i].m_name.size(); + bnp.serial( nStringSize ); + bnp.serialBuffer( (uint8*)m_packedFiles[i].m_name.c_str(), nStringSize ); + bnp.serial(m_packedFiles[i].m_size); + bnp.serial(m_packedFiles[i].m_pos); + } - if (fwrite (m_packedFiles[i].m_name.c_str(), 1, nStringSize, f) != nStringSize) - { - fclose(f); - return false; - } + bnp.serial(offset); - if (fwrite (&m_packedFiles[i].m_size, sizeof(uint32), 1, f) != 1) - { - fclose(f); - return false; - } - - if (fwrite (&m_packedFiles[i].m_pos, sizeof(uint32), 1, f) != 1) - { - fclose(f); - return false; - } - } - - if (fwrite (&offset, sizeof(uint32), 1, f) != 1) - { - fclose(f); - return false; - } - - fclose (f); - return true; + return true; } // *************************************************************************** void BNPFileHandle::fileNames(std::vector &fileNames) From 215eb46cbcfd57b896ffcc4f5d3d0ec012eba115 Mon Sep 17 00:00:00 2001 From: Krolock Date: Thu, 12 Jan 2012 00:00:09 +0100 Subject: [PATCH 167/215] Changed: BIG_ENDIAN check is no longer needed --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/bnp_file.cpp | 33 ++++--------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp index 7c404b347..cce2a022a 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp @@ -65,8 +65,7 @@ void BNPFileHandle::releaseInstance() bool BNPFileHandle::unpack(const string &dirName, const vector& fileList) { CIFile bnp; - if ( !bnp.open(m_openedBNPFile) ) - return false; + bnp.open(m_openedBNPFile); TPackedFilesList::iterator it_files = m_packedFiles.begin(); @@ -99,11 +98,7 @@ bool BNPFileHandle::readHeader(const std::string &filePath) m_openedBNPFile = filePath; CIFile bnp; - if ( !bnp.open (filePath) ) - { - nlwarning("Could not open file!"); - return false; - } + bnp.open (filePath); bnp.seek(0, IStream::end); uint32 nFileSize=CFile::getFileSize (filePath ); @@ -112,9 +107,6 @@ bool BNPFileHandle::readHeader(const std::string &filePath) uint32 nOffsetFromBegining; bnp.serial(nOffsetFromBegining); -#ifdef NL_BIG_ENDIAN - NLMISC_BSWAP32(nOffsetFromBegining); -#endif if ( !bnp.seek (nOffsetFromBegining, IStream::begin) ) { @@ -125,33 +117,22 @@ bool BNPFileHandle::readHeader(const std::string &filePath) uint32 nNbFile; bnp.serial(nNbFile); -#ifdef NL_BIG_ENDIAN - NLMISC_BSWAP32(nNbFile); -#endif - for (uint32 i = 0; i < nNbFile; ++i) { uint8 nStringSize; - uint32 fileSize; - uint32 filePos; char sName[256]; + bnp.serial(nStringSize); bnp.serialBuffer( (uint8*)sName, nStringSize); sName[nStringSize] = 0; + PackedFile tmpPackedFile; tmpPackedFile.m_name = sName; tmpPackedFile.m_path = m_openedBNPFile; - bnp.serial(fileSize); - tmpPackedFile.m_size = fileSize; -#ifdef NL_BIG_ENDIAN - NLMISC_BSWAP32(tmpBNPFile.Size); -#endif - bnp.serial(filePos); - tmpPackedFile.m_pos = filePos; -#ifdef NL_BIG_ENDIAN - NLMISC_BSWAP32(tmpBNPFile.Pos); -#endif + bnp.serial(tmpPackedFile.m_size); + bnp.serial(tmpPackedFile.m_pos); + m_packedFiles.push_back (tmpPackedFile); } return true; From 2719c61efc6326f57a994f3b9f22d609bed4aae1 Mon Sep 17 00:00:00 2001 From: Krolock Date: Thu, 12 Jan 2012 00:18:09 +0100 Subject: [PATCH 168/215] Changed: Finished serial system --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/bnp_file.cpp | 37 ++++++++++++------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp index cce2a022a..d9b2f45c1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_file.cpp @@ -85,8 +85,11 @@ bool BNPFileHandle::unpack(const string &dirName, const vector& fileList out.serialBuffer(ptr,it_files->m_size); delete [] ptr; } + out.close(); } } + + bnp.close(); return true; } // *************************************************************************** @@ -101,7 +104,7 @@ bool BNPFileHandle::readHeader(const std::string &filePath) bnp.open (filePath); bnp.seek(0, IStream::end); - uint32 nFileSize=CFile::getFileSize (filePath ); + uint32 nFileSize = bnp.getFileSize(); bnp.seek(nFileSize-sizeof(uint32), IStream::begin); uint32 nOffsetFromBegining; @@ -111,6 +114,7 @@ bool BNPFileHandle::readHeader(const std::string &filePath) if ( !bnp.seek (nOffsetFromBegining, IStream::begin) ) { nlwarning("Could not read offset from begining"); + bnp.close(); return false; } @@ -135,6 +139,8 @@ bool BNPFileHandle::readHeader(const std::string &filePath) m_packedFiles.push_back (tmpPackedFile); } + + bnp.close(); return true; } // *************************************************************************** @@ -156,7 +162,9 @@ void BNPFileHandle::list(TPackedFilesList& FileList) bool BNPFileHandle::writeHeader( const std::string &filePath, uint32 offset ) { COFile bnp; - if ( !bnp.open(filePath, true) ) return false; + bnp.open(filePath, true); + if ( !bnp.isOpen() ) + return false; uint32 nNbFile = (uint32)m_packedFiles.size(); bnp.serial(nNbFile); @@ -172,6 +180,8 @@ bool BNPFileHandle::writeHeader( const std::string &filePath, uint32 offset ) bnp.serial(offset); + bnp.close(); + return true; } // *************************************************************************** @@ -255,7 +265,6 @@ void BNPFileHandle::deleteFiles( const vector& fileNames) it_packed++; } } - nldebug("Writing header..."); writeHeader(tmpFile, OffsetFromBegining); @@ -270,10 +279,12 @@ void BNPFileHandle::append(const string &destination, const PackedFile &source) if ( !CFile::fileExists(destination) ) CFile::createEmptyFile( destination ); - FILE *bnpfile = fopen(destination.c_str(), "ab"); - FILE *packedfile = fopen(source.m_path.c_str(), "rb"); - if (bnpfile == NULL) return; - if (packedfile == NULL) { fclose(bnpfile); return; } + COFile bnpfile; + CIFile packedfile; + bnpfile.open(destination, true); + packedfile.open(source.m_path); + if ( !bnpfile.isOpen() ) return; + uint8 *ptr = new uint8[source.m_size]; @@ -281,20 +292,18 @@ void BNPFileHandle::append(const string &destination, const PackedFile &source) if ( nlstricmp( CFile::getExtension(source.m_path), "bnp" ) == 0 ) { // Jump to the file position inside the bnp - nlfseek64(packedfile, source.m_pos, SEEK_SET); + packedfile.seek(source.m_pos, IStream::begin); } // Read the source - if (fread (ptr, source.m_size, 1, packedfile) != 1) - nlwarning("%s read error", source.m_path.c_str()); + packedfile.serialBuffer(ptr, source.m_size); // Append the data to the destination - if (fwrite (ptr, source.m_size, 1, bnpfile) != 1) - nlwarning("%s write error", destination.c_str()); + bnpfile.serialBuffer(ptr, source.m_size); delete [] ptr; - fclose(packedfile); - fclose(bnpfile); + packedfile.close(); + bnpfile.close(); } // *************************************************************************** bool BNPFileHandle::compare(const PackedFile &left, const PackedFile &right) From e0b225fa93ae87451b46a294b30da9180152f937 Mon Sep 17 00:00:00 2001 From: dnk-88 Date: Sun, 15 Jan 2012 20:52:33 +0300 Subject: [PATCH 169/215] Fixed: #1375 Fixed compilation error. --- .../object_viewer_qt/src/plugins/object_viewer/attrib_widget.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_widget.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_widget.h index 7bd929d72..da308aba7 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_widget.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/object_viewer/attrib_widget.h @@ -58,7 +58,7 @@ public: /// Force to update dialog content void updateUi(); - /// Сonnects all the slots with signals + /// Connects all the slots with signals void init(); /// Sets the pointer CWorkspaceNode* in the wrappers. @@ -192,6 +192,7 @@ protected: QDialog *_SchemeWidget; Ui::CAttribWidget _ui; + friend class CSchemeBankDialog; }; /* class CAttribWidget */ /** From dd05742f88e9e226940c5776f25d511d549b296f Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 13 Feb 2012 11:31:28 +0100 Subject: [PATCH 170/215] Fixed: Bad check in emote tokens --- code/ryzom/client/src/interface_v3/interface_manager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/ryzom/client/src/interface_v3/interface_manager.cpp b/code/ryzom/client/src/interface_v3/interface_manager.cpp index 180a25763..27b8f9197 100644 --- a/code/ryzom/client/src/interface_v3/interface_manager.cpp +++ b/code/ryzom/client/src/interface_v3/interface_manager.cpp @@ -6374,7 +6374,7 @@ bool CInterfaceManager::parseTokens(ucstring& ucstr) // Get everything between the two "$" size_t token_start_pos = start_pos + start_token.length(); size_t token_end_pos = end_pos - end_token.length(); - if (token_start_pos < token_end_pos) + if (token_start_pos > token_end_pos) { // Wrong formatting; give up on this one. start_pos = end_pos; From a178f13d618b75dfdc44892b08349b885654399e Mon Sep 17 00:00:00 2001 From: Krolock Date: Tue, 21 Feb 2012 17:45:47 +0100 Subject: [PATCH 171/215] Added: Sortproxymodel in order to sort DirTreeView (folders on top) --HG-- branch : branch-bnp-manager-plugin --- .../src/plugins/bnp_manager/CMakeLists.txt | 1 + .../plugins/bnp_manager/bnp_dirtree_dialog.cpp | 18 ++++++++++++------ .../plugins/bnp_manager/bnp_dirtree_dialog.h | 5 ++++- .../bnp_manager/bnp_filesystem_model.cpp | 1 - .../plugins/bnp_manager/bnp_manager_window.cpp | 2 +- 5 files changed, 18 insertions(+), 9 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt index e06c82c94..7ecfd7396 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/CMakeLists.txt @@ -14,6 +14,7 @@ SET(OVQT_PLUG_BNP_MANAGER_HDR bnp_manager_plugin.h bnp_filesystem_model.h bnp_file.h bnp_filelist_dialog.h + bnp_proxy_model.h ) SET(OVQT_PLUG_BNP_MANAGER_UIS bnp_dirtree_form.ui bnp_filelist_dialog.ui diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp index 9487f66ae..78cc2f3cd 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.cpp @@ -17,6 +17,7 @@ // Project includes #include "bnp_dirtree_dialog.h" #include "bnp_filesystem_model.h" +#include "bnp_proxy_model.h" // Qt includes #include @@ -39,18 +40,22 @@ CBnpDirTreeDialog::CBnpDirTreeDialog(QString bnpPath, QWidget *parent) // Bnp file: opened and displayed // all other files: added to the currently opened bnp file QStringList filter; - filter << tr("*.bnp"); + //filter << tr("*.bnp"); // Setup the directory tree model - m_dirModel= new BNPFileSystemModel; + m_dirModel= new BNPFileSystemModel(); + m_proxyModel = new BNPSortProxyModel(); m_dirModel->setRootPath(m_DataPath); m_dirModel->setFilter(QDir::AllDirs | QDir::NoDotAndDotDot | QDir::AllEntries); m_dirModel->setNameFilters(filter); m_dirModel->setNameFilterDisables(0); - m_ui.dirTree->setModel(m_dirModel); + m_proxyModel->setSourceModel(m_dirModel); - m_ui.dirTree->setRootIndex(m_dirModel->index(m_DataPath)); + m_ui.dirTree->setModel(m_proxyModel); + + m_ui.dirTree->setRootIndex( m_proxyModel->mapFromSource (m_dirModel->index(m_DataPath) ) ); + m_ui.dirTree->setSortingEnabled(true); // Trigger if one filename is activated // In future drag&drop should be also possible @@ -65,10 +70,11 @@ CBnpDirTreeDialog::~CBnpDirTreeDialog() // *************************************************************************** void CBnpDirTreeDialog::fileSelected(QModelIndex index) { - if (index.isValid() && !m_dirModel->isDir(index)) + QModelIndex source = m_proxyModel->mapToSource(index); + if (source.isValid() && !m_dirModel->isDir(source)) { // emit the according signal to BNPManagerWindow class - Q_EMIT selectedForm(m_dirModel->fileInfo(index).filePath()); + Q_EMIT selectedFile(m_dirModel->fileInfo(source).filePath()); } } // *************************************************************************** diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h index 33b221a4d..737085185 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_dirtree_dialog.h @@ -31,6 +31,7 @@ namespace BNPManager { class BNPFileSystemModel; +class BNPSortProxyModel; class CBnpDirTreeDialog : public QDockWidget { @@ -63,8 +64,10 @@ private: BNPFileSystemModel *m_dirModel; + BNPSortProxyModel *m_proxyModel; + Q_SIGNALS: - void selectedForm(const QString); + void selectedFile(const QString); private Q_SLOTS: /** diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp index eaf6389f5..75ef031ff 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filesystem_model.cpp @@ -25,7 +25,6 @@ namespace BNPManager BNPFileSystemModel::BNPFileSystemModel(QObject *parent) : QFileSystemModel(parent) { - } // *************************************************************************** BNPFileSystemModel::~BNPFileSystemModel() diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp index 33157733e..4022931d1 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_manager_window.cpp @@ -67,7 +67,7 @@ BNPManagerWindow::BNPManagerWindow(QWidget *parent) // this SLOT is triggered if the user activates a bnp files in the // dirtree view - connect(m_BnpDirTreeDialog, SIGNAL(selectedForm(const QString)), + connect(m_BnpDirTreeDialog, SIGNAL(selectedFile(const QString)), this, SLOT(loadFile(const QString))); // not used From 94aab791daa1f17734c85ef8d4388350874affb8 Mon Sep 17 00:00:00 2001 From: kervala Date: Tue, 21 Feb 2012 17:47:16 +0100 Subject: [PATCH 172/215] Changed: Search in default paths if DXSDK_DIR is not defined --- code/CMakeModules/FindDirectXSDK.cmake | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/code/CMakeModules/FindDirectXSDK.cmake b/code/CMakeModules/FindDirectXSDK.cmake index 1f832cf95..9947778db 100644 --- a/code/CMakeModules/FindDirectXSDK.cmake +++ b/code/CMakeModules/FindDirectXSDK.cmake @@ -14,17 +14,22 @@ FIND_PATH(DXSDK_DIR "Include/dxsdkver.h" PATHS "$ENV{DXSDK_DIR}" + "C:/Program Files (x86)/Microsoft DirectX SDK (June 2010)" + "C:/Program Files/Microsoft DirectX SDK (June 2010)" + "C:/Program Files (x86)/Microsoft DirectX SDK (February 2010)" + "C:/Program Files/Microsoft DirectX SDK (February 2010)" + "C:/Program Files (x86)/Microsoft DirectX SDK (November 2007)" + "C:/Program Files/Microsoft DirectX SDK (November 2007)" + "C:/Program Files (x86)/Microsoft DirectX SDK" + "C:/Program Files/Microsoft DirectX SDK" ) MACRO(FIND_DXSDK_LIBRARY MYLIBRARY MYLIBRARYNAME) FIND_LIBRARY(${MYLIBRARY} NAMES ${MYLIBRARYNAME} PATHS - "${DXSDK_LIBRARY_DIR}" - "$ENV{DXSDK_DIR}" - "$ENV{DXSDK_DIR}/Lib" - "$ENV{DXSDK_DIR}/Lib/x86" - ) + "${DXSDK_LIBRARY_DIR}" + ) ENDMACRO(FIND_DXSDK_LIBRARY MYLIBRARY MYLIBRARYNAME) IF(DXSDK_DIR) From 23c6ff964ec2c929fd47f90b06aeb65a996361b7 Mon Sep 17 00:00:00 2001 From: Krolock Date: Tue, 21 Feb 2012 17:47:26 +0100 Subject: [PATCH 173/215] Added: Sortproxymodel class --HG-- branch : branch-bnp-manager-plugin --- .../plugins/bnp_manager/bnp_proxy_model.cpp | 56 +++++++++++++++++++ .../src/plugins/bnp_manager/bnp_proxy_model.h | 44 +++++++++++++++ 2 files changed, 100 insertions(+) create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.cpp create mode 100644 code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.h diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.cpp new file mode 100644 index 000000000..d3657d13b --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.cpp @@ -0,0 +1,56 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see + +// NeL includes +#include + +// project includes +#include "bnp_proxy_model.h" + +namespace BNPManager +{ + +bool BNPSortProxyModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + if ( sourceModel()->hasChildren(left) ) + { + if ( !sourceModel()->hasChildren(right) ) + { + return true; + } + else + { + QString leftString = sourceModel()->data( left ).toString(); + QString rightString = sourceModel()->data( right ).toString(); + return QString::localeAwareCompare(leftString, rightString) < 0; + } + } + else + { + if ( sourceModel()->hasChildren(right) ) + return false; + else + { + QString leftString = sourceModel()->data( left ).toString(); + QString rightString = sourceModel()->data( right ).toString(); + return QString::localeAwareCompare(leftString, rightString) < 0; + } + } +} + +} /* namespace Plugin */ + +/* end of file */ diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.h new file mode 100644 index 000000000..ed2da5966 --- /dev/null +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_proxy_model.h @@ -0,0 +1,44 @@ +// Object Viewer Qt - BNP Manager Plugin - MMORPG Framework +// Copyright (C) 2011 Roland Winklmeier +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see + +#ifndef BNP_PROXY_MODEL_H +#define BNP_PROXY_MODEL_H + +// Qt includes +#include + +namespace BNPManager +{ + + class BNPSortProxyModel : public QSortFilterProxyModel + { + + public: + BNPSortProxyModel(QObject *parent = 0): QSortFilterProxyModel(parent) + { + } + ~BNPSortProxyModel() + { + } + + protected: + virtual bool lessThan ( const QModelIndex & left, const QModelIndex & right ) const; + + };/* class BNPSortProxyModel */ + +} // BNPManager + +#endif // BNP_PROXY_MODEL_H From 0d607ba7931b780d28e7384187e6175b8bd9544d Mon Sep 17 00:00:00 2001 From: Krolock Date: Tue, 21 Feb 2012 18:00:05 +0100 Subject: [PATCH 174/215] Added: Drop handling from outside into FileListDialog --HG-- branch : branch-bnp-manager-plugin --- .../bnp_manager/bnp_filelist_dialog.cpp | 27 +++++++++++++++++-- .../plugins/bnp_manager/bnp_filelist_dialog.h | 6 +++-- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp index b78e6c0bf..418d7fa04 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.cpp @@ -20,6 +20,10 @@ // Qt includes #include +#include +#include +#include +#include // NeL includes #include @@ -34,6 +38,7 @@ BnpFileListDialog::BnpFileListDialog(QString bnpPath, QWidget *parent) m_DataPath(bnpPath) { m_ui.setupUi(this); + setAcceptDrops(true); } // *************************************************************************** BnpFileListDialog::~BnpFileListDialog() @@ -70,7 +75,7 @@ void BnpFileListDialog::setupTable(int nbrows) m_ui.tableWidget->setObjectName("tablewidget"); } // *************************************************************************** -bool BnpFileListDialog::loadTable(const QString fileName) +bool BnpFileListDialog::loadTable(const QString filePath) { // reference to the BNPFileHandle singletone instance BNPFileHandle& myBNPFileHandle = BNPFileHandle::getInstance(); @@ -79,7 +84,7 @@ bool BnpFileListDialog::loadTable(const QString fileName) int row = 0; // read the header from the bnp file - if (!myBNPFileHandle.readHeader( fileName.toStdString()) ) + if (!myBNPFileHandle.readHeader( filePath.toStdString()) ) { return false; } @@ -100,6 +105,9 @@ bool BnpFileListDialog::loadTable(const QString fileName) row++; } + // Set the file path as the widgets title + setWindowTitle(filePath); + return true; } // *************************************************************************** @@ -118,5 +126,20 @@ void BnpFileListDialog::getSelections(TSelectionList& SelectionList) } } // *************************************************************************** +void BnpFileListDialog::dragEnterEvent(QDragEnterEvent *event) +{ + // Accept only one file + // In the future a tabbed FileListDialog would accept more + if ( event->mimeData()->hasUrls() && event->mimeData()->urls().count() == 1) + event->acceptProposedAction(); +} +// *************************************************************************** +void BnpFileListDialog::dropEvent(QDropEvent *event) + { + // Excraft the local file url from the drop object and fill the table + const QMimeData *mimeData = event->mimeData(); + QList urlList = mimeData->urls(); + loadTable( urlList.first().toLocalFile() ); + } } // namespace BNPManager \ No newline at end of file diff --git a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h index f2cc021a9..5b5491d8f 100644 --- a/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h +++ b/code/nel/tools/3d/object_viewer_qt/src/plugins/bnp_manager/bnp_filelist_dialog.h @@ -51,7 +51,7 @@ public: * \param Filename * \return true if everything went well */ - bool loadTable(const QString filename); + bool loadTable(const QString filePath); /** * Set the dimension of the table @@ -67,8 +67,10 @@ public: */ void getSelections(TSelectionList& SelectionList); +protected: + void dragEnterEvent (QDragEnterEvent *event); + void dropEvent(QDropEvent *event); private: - Ui::BnpFileListDialog m_ui; // common data path as root folder for the dirtree view From f0008d9952b482a98bf55fcfb4c7804725d77307 Mon Sep 17 00:00:00 2001 From: kervala Date: Tue, 21 Feb 2012 21:11:04 +0100 Subject: [PATCH 175/215] Changed: #1219 Bad color when rgba.cpp is compiled with GCC 4.2.4 --- code/nel/src/misc/rgba.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/code/nel/src/misc/rgba.cpp b/code/nel/src/misc/rgba.cpp index 844a100cf..598d8eb53 100644 --- a/code/nel/src/misc/rgba.cpp +++ b/code/nel/src/misc/rgba.cpp @@ -643,6 +643,8 @@ bool CRGBA::convertToHLS(float &h, float &l, float &s) const { h = 2.f + (b - r) / diff; } +#if defined(GCC_VERSION) && (GCC_VERSION == 40204) + // use the fix only if using the specific GCC version else if (maxV == b) { h = 4.f + (r - g) / diff; @@ -652,6 +654,12 @@ bool CRGBA::convertToHLS(float &h, float &l, float &s) const // this case is to fix a compiler bug h = (g - b) / diff; } +#else + else + { + h = 4.f + (r - g) / diff; + } +#endif h *= 60.f; // scale to [0..360] From 7523a91fe15de1c0f04b6c9fb601c45515633d58 Mon Sep 17 00:00:00 2001 From: kervala Date: Wed, 22 Feb 2012 22:17:09 +0100 Subject: [PATCH 176/215] Changed: Fix again bad MSVC10 CMake generator... --- code/CMakeModules/nel.cmake | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 5c5efc4a9..eb2bc24ac 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -37,7 +37,9 @@ ENDMACRO(NL_TARGET_DRIVER) # Argument: ### MACRO(NL_DEFAULT_PROPS name label) - SET_TARGET_PROPERTIES(${name} PROPERTIES PROJECT_LABEL ${label}) + IF(NOT MSVC10) + SET_TARGET_PROPERTIES(${name} PROPERTIES PROJECT_LABEL ${label}) + ENDIF(NOT MSVC10) GET_TARGET_PROPERTY(type ${name} TYPE) IF(${type} STREQUAL SHARED_LIBRARY) # Set versions only if target is a shared library From dbb327deb05228bf5f8a6c0dae0219372d6ecdb9 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 27 Feb 2012 09:59:27 +0100 Subject: [PATCH 177/215] Changed: #1433 Merge changes from patch 1.13 --- code/nel/include/nel/misc/common.h | 2 ++ code/nel/include/nel/misc/eid_translator.h | 2 ++ code/nel/include/nel/net/transport_class.h | 3 ++- code/nel/src/misc/common.cpp | 26 ++++++++++++++++++++++ code/nel/src/misc/eid_translator.cpp | 11 +++++++++ code/nel/src/misc/win_event_emitter.cpp | 7 ++++++ code/nel/src/net/transport_class.cpp | 3 ++- 7 files changed, 52 insertions(+), 2 deletions(-) diff --git a/code/nel/include/nel/misc/common.h b/code/nel/include/nel/misc/common.h index 42bf56d3b..fa9272386 100644 --- a/code/nel/include/nel/misc/common.h +++ b/code/nel/include/nel/misc/common.h @@ -342,6 +342,8 @@ std::string secondsToHumanReadable (uint32 time); /// Get a bytes or time in string format and convert it in seconds or bytes uint32 fromHumanReadable (const std::string &str); +/// Add digit grouping seperator to if value >= 10 000. Assumes input is numerical string. +std::string formatThousands(const std::string& s); /// This function executes a program in the background and returns instantly (used for example to launch services in AES). /// The program will be launched in the current directory diff --git a/code/nel/include/nel/misc/eid_translator.h b/code/nel/include/nel/misc/eid_translator.h index 695f7724e..9937b72fd 100644 --- a/code/nel/include/nel/misc/eid_translator.h +++ b/code/nel/include/nel/misc/eid_translator.h @@ -146,6 +146,8 @@ public: TAdditionalInfoCb EntityInfoCallback; + static void removeShardFromName(ucstring& name); + private: // get all eid for a user using the user name or the user id void getByUser (uint32 uid, std::vector &res); diff --git a/code/nel/include/nel/net/transport_class.h b/code/nel/include/nel/net/transport_class.h index 11161d7cd..2517da64b 100644 --- a/code/nel/include/nel/net/transport_class.h +++ b/code/nel/include/nel/net/transport_class.h @@ -75,7 +75,7 @@ public: enum TProp { PropUInt8, PropUInt16, PropUInt32, PropUInt64, PropSInt8, PropSInt16, PropSInt32, PropSInt64, - PropBool, PropFloat, PropDouble, PropString, PropDataSetRow, PropSheetId, PropUKN }; + PropBool, PropFloat, PropDouble, PropString, PropDataSetRow, PropSheetId, PropUCString, PropUKN }; // PropBool, PropFloat, PropDouble, PropString, PropDataSetRow, PropEntityId, PropSheetId, PropUKN }; @@ -160,6 +160,7 @@ public: case PropString: nlassert(sizeof(T) == sizeof (std::string)); break; // case PropEntityId: nlassert(sizeof(T) == sizeof (NLMISC::CEntityId)); break; case PropSheetId: nlassert(sizeof(T) == sizeof (NLMISC::CSheetId)); break; + case PropUCString: nlassert(sizeof(T) == sizeof (ucstring)); break; default: nlerror ("property %s have unknown type %d", name.c_str(), type); } diff --git a/code/nel/src/misc/common.cpp b/code/nel/src/misc/common.cpp index fb9a9c6f5..19be95367 100644 --- a/code/nel/src/misc/common.cpp +++ b/code/nel/src/misc/common.cpp @@ -30,6 +30,7 @@ #include "nel/misc/command.h" #include "nel/misc/path.h" +#include "nel/misc/i18n.h" using namespace std; @@ -526,6 +527,31 @@ void toUpper(char *str) } } +std::string formatThousands(const std::string& s) +{ + int i, k; + int remaining = s.length() - 1; + static std::string separator = NLMISC::CI18N::get("uiThousandsSeparator").toUtf8(); + + // Don't add separator if the number is < 10k + if (remaining < 4) return s; + + std::string ns; + + do + { + for (i = remaining, k = 0; i >= 0 && k < 3; --i, ++k ) + { + ns = s[i] + ns; // New char is added to front of ns + if ( i > 0 && k == 2) ns = separator + ns; // j > 0 means still more digits + } + + remaining -= 3; + } + while (remaining >= 0); + + return ns; +} // // Exceptions diff --git a/code/nel/src/misc/eid_translator.cpp b/code/nel/src/misc/eid_translator.cpp index 36ea84247..e254831c4 100644 --- a/code/nel/src/misc/eid_translator.cpp +++ b/code/nel/src/misc/eid_translator.cpp @@ -417,6 +417,17 @@ void CEntityIdTranslator::checkEntity (const CEntityId &eid, const ucstring &ent } } +void CEntityIdTranslator::removeShardFromName(ucstring& name) +{ + // The string must contain a '(' and a ')' + ucstring::size_type p0= name.find('('); + ucstring::size_type p1= name.find(')'); + if (p0 == ucstring::npos || p1 == ucstring::npos || p1 <= p0) + return; + + name = name.substr(0, p0) + name.substr(p1 + 1); +} + // this callback is call when the file is changed void cbInvalidEntityNamesFilename(const std::string &invalidEntityNamesFilename) { diff --git a/code/nel/src/misc/win_event_emitter.cpp b/code/nel/src/misc/win_event_emitter.cpp index 2c46a7530..c216e3e1a 100644 --- a/code/nel/src/misc/win_event_emitter.cpp +++ b/code/nel/src/misc/win_event_emitter.cpp @@ -153,6 +153,13 @@ bool CWinEventEmitter::processMessage (HWND hWnd, uint32 msg, WPARAM wParam, LPA if ((int)wParam==VK_SHIFT) _ShiftButton=false; + // As Print Screen button does not trigger a WM_KEYDOWN msg, simulate it here + if ((int)wParam==VK_SNAPSHOT) + { + if (wParam < KeyCount) + server->postEvent (new CEventKeyDown ((NLMISC::TKey)wParam, getKeyButton(_AltButton, _ShiftButton, _CtrlButton), true, this)); + } + // Post the message if (wParam < KeyCount) server->postEvent (new CEventKeyUp ((NLMISC::TKey)wParam, getKeyButton(_AltButton, _ShiftButton, _CtrlButton), this)); diff --git a/code/nel/src/net/transport_class.cpp b/code/nel/src/net/transport_class.cpp index a5f826728..dca866916 100644 --- a/code/nel/src/net/transport_class.cpp +++ b/code/nel/src/net/transport_class.cpp @@ -77,7 +77,7 @@ string typeToString (CTransportClass::TProp type) string conv[] = { "PropUInt8", "PropUInt16", "PropUInt32", "PropUInt64", "PropSInt8", "PropSInt16", "PropSInt32", "PropSInt64", - "PropBool", "PropFloat", "PropDouble", "PropString", "PropDataSetRow", "PropSheetId", "PropUKN" }; + "PropBool", "PropFloat", "PropDouble", "PropString", "PropDataSetRow", "PropSheetId", "PropUCString", "PropUKN" }; // "PropBool", "PropFloat", "PropDouble", "PropString", "PropDataSetRow", "PropEntityId", "PropSheetId", "PropUKN" }; if (type > CTransportClass::PropUKN) @@ -352,6 +352,7 @@ void CTransportClass::init () // nlassert (PropDataSetRow < PropUKN); DummyProp[PropDataSetRow] = new CTransportClass::CRegisteredProp; // nlassert (PropEntityId < PropUKN); DummyProp[PropEntityId] = new CTransportClass::CRegisteredProp; nlassert (PropSheetId < PropUKN); DummyProp[PropSheetId] = new CTransportClass::CRegisteredProp; + nlassert (PropUCString < PropUKN); DummyProp[PropUCString] = new CTransportClass::CRegisteredProp; // we have to know when a service comes, so add callback (put the callback before all other one because we have to send this message first) CUnifiedNetwork::getInstance()->setServiceUpCallback("*", cbTCUpService, NULL, false); From d50e7369cf056cf74b64f8061c6772ee891241b6 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 27 Feb 2012 10:01:45 +0100 Subject: [PATCH 178/215] Changed: #878 Fix typos in comments/code --- code/nel/include/nel/misc/event_emitter.h | 2 +- code/nel/src/3d/driver/direct3d/driver_direct3d.cpp | 2 +- code/nel/src/3d/driver/opengl/driver_opengl_light.cpp | 2 +- code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp | 2 +- code/nel/src/3d/nelu.cpp | 3 ++- code/nel/src/misc/events.cpp | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/code/nel/include/nel/misc/event_emitter.h b/code/nel/include/nel/misc/event_emitter.h index e678c2674..5c547d07f 100644 --- a/code/nel/include/nel/misc/event_emitter.h +++ b/code/nel/include/nel/misc/event_emitter.h @@ -49,7 +49,7 @@ public: * \param server */ virtual void submitEvents(CEventServer & server, bool allWindows) = 0; - + /** * Instruct the event emitter to send CGDMouseMove instead of CEventMouseMove. * diff --git a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp index 5ac1d2906..bfbd3fdb7 100644 --- a/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp +++ b/code/nel/src/3d/driver/direct3d/driver_direct3d.cpp @@ -1228,7 +1228,7 @@ bool CDriverD3D::init (uint windowIcon, emptyProc exitFunc) ExitFunc = exitFunc; createCursors(); - + // Register a window class WNDCLASSW wc; diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_light.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_light.cpp index e5c3cff6e..2fc25f94a 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_light.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_light.cpp @@ -138,7 +138,7 @@ void CDriverGL::setLightInternal(uint8 num, const CLight& light) } else { - // Deactivate spot properties + // Disable spot properties #ifdef USE_OPENGLES glLightf (lightNum, GL_SPOT_CUTOFF, 180.f); glLightf (lightNum, GL_SPOT_EXPONENT, 0.f); diff --git a/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp b/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp index 502c971f1..7241d499b 100644 --- a/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp +++ b/code/nel/src/3d/driver/opengl/driver_opengl_vertex.cpp @@ -978,7 +978,7 @@ void CDriverGL::setupGlArraysStd(CVertexBufferInfo &vb) // Check type nlassert (vb.Type[CVertexBuffer::Normal]==CVertexBuffer::Float3); _DriverGLStates.enableNormalArray(true); - nglArrayObjectATI(GL_NORMAL_ARRAY, 3, GL_FLOAT, vb.VertexSize, vb.VertexObjectId, (ptrdiff_t) vb.ValuePtr[CVertexBuffer::Normal]); + nglArrayObjectATI(GL_NORMAL_ARRAY, 3, GL_FLOAT, vb.VertexSize, vb.VertexObjectId, (ptrdiff_t) vb.ValuePtr[CVertexBuffer::Normal]); } else { diff --git a/code/nel/src/3d/nelu.cpp b/code/nel/src/3d/nelu.cpp index 80ef89ec4..3d143bce1 100644 --- a/code/nel/src/3d/nelu.cpp +++ b/code/nel/src/3d/nelu.cpp @@ -58,7 +58,7 @@ bool CNELU::initDriver (uint w, uint h, uint bpp, bool windowed, nlWindow syst CNELU::Driver = NULL; // Init driver. -#if defined(NL_OS_WINDOWS) +#ifdef NL_OS_WINDOWS if (direct3d) { CNELU::Driver= CDRU::createD3DDriver(); @@ -75,6 +75,7 @@ bool CNELU::initDriver (uint w, uint h, uint bpp, bool windowed, nlWindow syst nlwarning ("CNELU::initDriver: no driver found"); return false; } + if (!CNELU::Driver->init()) { nlwarning ("CNELU::initDriver: init() failed"); diff --git a/code/nel/src/misc/events.cpp b/code/nel/src/misc/events.cpp index 4d3979416..4d0f996d5 100644 --- a/code/nel/src/misc/events.cpp +++ b/code/nel/src/misc/events.cpp @@ -179,7 +179,7 @@ static const CStringConversion::CPair stringTable [] = { "KeyNONAME", KeyNONAME }, { "KeyPA1", KeyPA1 }, { "KeyOEM_CLEAR", KeyOEM_CLEAR }, -}; +} static CStringConversion KeyConversion(stringTable, sizeof(stringTable) / sizeof(stringTable[0]), KeyCount); From 457704b2b6bdf5090ea3ccc95ffc6faf363d3935 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 27 Feb 2012 15:04:33 +0100 Subject: [PATCH 179/215] Changed: #1433 Merge changes from patch 1.13 --- code/ryzom/common/src/game_share/bot_chat_types.h | 2 ++ code/ryzom/common/src/game_share/entity_types.h | 2 +- code/ryzom/common/src/game_share/item_infos.cpp | 2 ++ code/ryzom/common/src/game_share/item_infos.h | 1 + .../common/src/game_share/persistent_data_template.h | 12 ++++++++---- code/ryzom/common/src/game_share/pvp_mode.cpp | 2 ++ code/ryzom/common/src/game_share/pvp_mode.h | 4 +++- .../common/src/game_share/ryzom_mirror_properties.h | 2 +- code/ryzom/common/src/game_share/send_chat.h | 2 +- 9 files changed, 21 insertions(+), 8 deletions(-) diff --git a/code/ryzom/common/src/game_share/bot_chat_types.h b/code/ryzom/common/src/game_share/bot_chat_types.h index 779bc8ba3..19982f106 100644 --- a/code/ryzom/common/src/game_share/bot_chat_types.h +++ b/code/ryzom/common/src/game_share/bot_chat_types.h @@ -82,6 +82,8 @@ namespace BOTCHATTYPE ResaleKOBroken, // this item can't be sold because its Resold time has expired ResaleKONoTimeLeft, + // this item can't be sold because the owner has locked it (temporary hack to get around modifying database.xml) + ResaleKOLockedByOwner, NumBotChatResaleFlag }; diff --git a/code/ryzom/common/src/game_share/entity_types.h b/code/ryzom/common/src/game_share/entity_types.h index 3841f9a17..68b42e954 100644 --- a/code/ryzom/common/src/game_share/entity_types.h +++ b/code/ryzom/common/src/game_share/entity_types.h @@ -130,7 +130,7 @@ const TCoord THRESHOLD_BEHAVIOUR = 60000; // Name const TPropIndex PROPERTY_NAME_STRING_ID = 6; -const TCoord THRESHOLD_NAME_STRING_ID = 25000; +const TCoord THRESHOLD_NAME_STRING_ID = 100000; // Main target const TPropIndex PROPERTY_TARGET_ID = 7; diff --git a/code/ryzom/common/src/game_share/item_infos.cpp b/code/ryzom/common/src/game_share/item_infos.cpp index a968ff8a4..af84c5d6c 100644 --- a/code/ryzom/common/src/game_share/item_infos.cpp +++ b/code/ryzom/common/src/game_share/item_infos.cpp @@ -73,6 +73,7 @@ CItemInfos::CItemInfos() LacustreMagicResistance = 0; JungleMagicResistance = 0; PrimaryRootMagicResistance = 0; + PetNumber = 0; // 1 based! } void CItemInfos::serial(NLMISC::IStream & s) @@ -130,5 +131,6 @@ void CItemInfos::serial(NLMISC::IStream & s) s.serial( CustomText ); s.serial( R2ItemDescription ); s.serial( R2ItemComment ); + s.serial( PetNumber ); } diff --git a/code/ryzom/common/src/game_share/item_infos.h b/code/ryzom/common/src/game_share/item_infos.h index ff34bfddc..3fd94444e 100644 --- a/code/ryzom/common/src/game_share/item_infos.h +++ b/code/ryzom/common/src/game_share/item_infos.h @@ -139,6 +139,7 @@ public: ucstring CustomText; ucstring R2ItemDescription; ucstring R2ItemComment; + uint8 PetNumber; // 1 based pet index //@} }; diff --git a/code/ryzom/common/src/game_share/persistent_data_template.h b/code/ryzom/common/src/game_share/persistent_data_template.h index ef76101cd..08211687a 100644 --- a/code/ryzom/common/src/game_share/persistent_data_template.h +++ b/code/ryzom/common/src/game_share/persistent_data_template.h @@ -176,16 +176,20 @@ inline uint32 saveGameCycleToSecond(NLMISC::TGameCycle tick) { // Evaluate the UTC of this event (with the current date of save). Suppose that 1 second==10 tick - return sint32(NLMISC::CTime::getSecondsSince1970()) + (sint32(tick) - sint32(CTickEventHandler::getGameCycle()))/10; + if (tick < CTickEventHandler::getGameCycle()) + return NLMISC::CTime::getSecondsSince1970(); + else + return NLMISC::CTime::getSecondsSince1970() + (tick - CTickEventHandler::getGameCycle())/10; // NB: result should be positive since no event should have been launched before 1970! } inline NLMISC::TGameCycle loadSecondToGameCycle(uint32 second) { + if (second < NLMISC::CTime::getSecondsSince1970()) + return 0; + // Convert UTC of the event to game cycle. Suppose that 1 second==10 tick - sint32 newTick= CTickEventHandler::getGameCycle() + (sint32(second) - sint32(NLMISC::CTime::getSecondsSince1970()))*10; - // If game cycle is loaded on a server where current game cycle is too young, we can have here negative values => avoid them - return std::max(newTick, sint32(0)); + return CTickEventHandler::getGameCycle() + (second - NLMISC::CTime::getSecondsSince1970())*10; } #endif diff --git a/code/ryzom/common/src/game_share/pvp_mode.cpp b/code/ryzom/common/src/game_share/pvp_mode.cpp index aa3ac1496..3e13fd428 100644 --- a/code/ryzom/common/src/game_share/pvp_mode.cpp +++ b/code/ryzom/common/src/game_share/pvp_mode.cpp @@ -35,6 +35,8 @@ namespace PVP_MODE NL_STRING_CONVERSION_TABLE_ENTRY(PvpZoneOutpost) NL_STRING_CONVERSION_TABLE_ENTRY(PvpFaction) NL_STRING_CONVERSION_TABLE_ENTRY(PvpFactionFlagged) + NL_STRING_CONVERSION_TABLE_ENTRY(PvpZoneSafe) + NL_STRING_CONVERSION_TABLE_ENTRY(PvpSafe) NL_END_STRING_CONVERSION_TABLE(TPVPMode, PVPModeConversion, Unknown) TPVPMode fromString(const std::string & str) diff --git a/code/ryzom/common/src/game_share/pvp_mode.h b/code/ryzom/common/src/game_share/pvp_mode.h index dd52f1fd6..0e654752f 100644 --- a/code/ryzom/common/src/game_share/pvp_mode.h +++ b/code/ryzom/common/src/game_share/pvp_mode.h @@ -32,10 +32,12 @@ namespace PVP_MODE PvpZoneOutpost = 32, PvpFaction = 64, PvpFactionFlagged = 128, + PvpZoneSafe = 256, + PvpSafe = 512, Unknown, NbModes = Unknown, - NbBits = 8 // number of bits needed to store all valid values + NbBits = 10 // number of bits needed to store all valid values }; diff --git a/code/ryzom/common/src/game_share/ryzom_mirror_properties.h b/code/ryzom/common/src/game_share/ryzom_mirror_properties.h index 0aab0b870..afd0ac892 100644 --- a/code/ryzom/common/src/game_share/ryzom_mirror_properties.h +++ b/code/ryzom/common/src/game_share/ryzom_mirror_properties.h @@ -138,7 +138,7 @@ void initRyzomVisualPropertyIndices( CMirroredDataSet& dataset ); #define TYPE_BOT_TRADE_SELECTOR2 uint64 #define TYPE_EVENT_FACTION_ID uint32 -#define TYPE_PVP_MODE uint8 +#define TYPE_PVP_MODE uint32 #define TYPE_PVP_CLAN uint32 #define TYPE_FUEL bool diff --git a/code/ryzom/common/src/game_share/send_chat.h b/code/ryzom/common/src/game_share/send_chat.h index cefb8bba0..1efa7acee 100644 --- a/code/ryzom/common/src/game_share/send_chat.h +++ b/code/ryzom/common/src/game_share/send_chat.h @@ -115,7 +115,7 @@ inline void npcChatToChannelEx(const TDataSetRow &senderId, CChatGroup::TGroupTy * Chat group can be constructed from CChatGroup class. * sentence is the sentence to be sent. */ -inline void npcChatToChannelSentence(const TDataSetRow &senderId, CChatGroup::TGroupType groupType, std::string& sentence) +inline void npcChatToChannelSentence(const TDataSetRow &senderId, CChatGroup::TGroupType groupType, ucstring& sentence) { NLNET::CMessage msgout("NPC_CHAT_SENTENCE"); msgout.serial(const_cast(senderId)); From 17aded887779ab4711b373e20e34bce8bf935b98 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 27 Feb 2012 15:19:53 +0100 Subject: [PATCH 180/215] Changed: Support for Debian/Ubuntu multiarch (use -DCMAKE_LIBRARY_ARCHITECTURE=) --- code/CMakeModules/nel.cmake | 98 +++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 21 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index eb2bc24ac..9314a7a85 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -304,27 +304,66 @@ MACRO(NL_SETUP_BUILD) ENDIF(CMAKE_BUILD_TYPE MATCHES "Release") ENDIF(CMAKE_BUILD_TYPE MATCHES "Debug") + SET(HOST_CPU ${CMAKE_SYSTEM_PROCESSOR}) + # Determine target CPU -# IF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86") - IF(NOT CMAKE_SIZEOF_VOID_P) - INCLUDE (CheckTypeSize) - CHECK_TYPE_SIZE("void*" CMAKE_SIZEOF_VOID_P) - ENDIF(NOT CMAKE_SIZEOF_VOID_P) + IF(NOT TARGET_CPU) + SET(TARGET_CPU $ENV{DEB_HOST_GNU_CPU}) + ENDIF(NOT TARGET_CPU) - # Using 32 or 64 bits libraries + # If not specified, use the same CPU as host + IF(NOT TARGET_CPU) + SET(TARGET_CPU ${CMAKE_SYSTEM_PROCESSOR}) + ENDIF(NOT TARGET_CPU) + + IF(TARGET_CPU MATCHES "amd64") + SET(TARGET_CPU "x86_64") + ELSEIF(TARGET_CPU MATCHES "i.86") + SET(TARGET_CPU "x86") + ENDIF(TARGET_CPU MATCHES "amd64") + + # DEB_HOST_ARCH_ENDIAN is 'little' or 'big' + # DEB_HOST_ARCH_BITS is '32' or '64' + + # If target and host CPU are the same + IF("${HOST_CPU}" STREQUAL "${TARGET_CPU}") + # x86-compatible CPU + IF(HOST_CPU MATCHES "x86") + IF(NOT CMAKE_SIZEOF_VOID_P) + INCLUDE (CheckTypeSize) + CHECK_TYPE_SIZE("void*" CMAKE_SIZEOF_VOID_P) + ENDIF(NOT CMAKE_SIZEOF_VOID_P) + + # Using 32 or 64 bits libraries + IF(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(TARGET_CPU "x86_64") + ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8) + SET(TARGET_CPU "x86") + ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8) + ENDIF(HOST_CPU MATCHES "x86") + # TODO: add checks for ARM and PPC + ELSE("${HOST_CPU}" STREQUAL "${TARGET_CPU}") + MESSAGE(STATUS "Compiling on ${HOST_CPU} for ${TARGET_CPU}") + ENDIF("${HOST_CPU}" STREQUAL "${TARGET_CPU}") + + IF(TARGET_CPU STREQUAL "x86_64") + SET(TARGET_X64 1) + SET(PLATFORM_CFLAGS "-DHAVE_X86_64") + ELSEIF(TARGET_CPU STREQUAL "x86") SET(TARGET_X86 1) - IF(CMAKE_SIZEOF_VOID_P EQUAL 8) - SET(ARCH "x86_64") - SET(TARGET_X64 1) - ADD_DEFINITIONS(-DHAVE_X86_64) - ELSE(CMAKE_SIZEOF_VOID_P EQUAL 8) - SET(ARCH "x86") - ADD_DEFINITIONS(-DHAVE_X86) - ENDIF(CMAKE_SIZEOF_VOID_P EQUAL 8) -# ADD_DEFINITIONS(-DHAVE_IA64) -# ENDIF(CMAKE_SYSTEM_PROCESSOR STREQUAL "x86") + SET(PLATFORM_CFLAGS "-DHAVE_X86") + ENDIF(TARGET_CPU STREQUAL "x86_64") - IF(WIN32) + # Fix library paths suffixes for Debian MultiArch + IF(NOT CMAKE_LIBRARY_ARCHITECTURE) + SET(CMAKE_LIBRARY_ARCHITECTURE $ENV{DEB_HOST_MULTIARCH}) + ENDIF(NOT CMAKE_LIBRARY_ARCHITECTURE) + + IF(CMAKE_LIBRARY_ARCHITECTURE) + SET(CMAKE_LIBRARY_PATH "/lib/${CMAKE_LIBRARY_ARCHITECTURE};/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE};${CMAKE_LIBRARY_PATH}") + ENDIF(CMAKE_LIBRARY_ARCHITECTURE) + + IF(MSVC) IF(MSVC10) # /Ox is working with VC++ 2010, but custom optimizations don't exist SET(SPEED_OPTIMIZATIONS "/Ox /GF /GS-") @@ -367,7 +406,16 @@ MACRO(NL_SETUP_BUILD) SET(NL_DEBUG_LINKFLAGS "/NODEFAULTLIB:msvcrt /INCREMENTAL:YES") SET(NL_RELEASE_LINKFLAGS "/OPT:REF /OPT:ICF /INCREMENTAL:NO") ELSE(WIN32) - SET(PLATFORM_CFLAGS "-g -pipe -ftemplate-depth-48 -D_REENTRANT -Wall -ansi -W -Wpointer-arith -Wsign-compare -Wno-deprecated-declarations -Wno-multichar -Wno-unused -fno-strict-aliasing") + IF(HOST_CPU STREQUAL "x86_64" AND TARGET_CPU STREQUAL "x86") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -m32 -march=i686") + ENDIF(HOST_CPU STREQUAL "x86_64" AND TARGET_CPU STREQUAL "x86") + + IF(HOST_CPU STREQUAL "x86" AND TARGET_CPU STREQUAL "x86_64") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -m64") + ENDIF(HOST_CPU STREQUAL "x86" AND TARGET_CPU STREQUAL "x86_64") + + SET(PLATFORM_CFLAGS "{PLATFORM_CFLAGS} -g -D_REENTRANT -pipe -ftemplate-depth-48 -Wall -ansi -W -Wpointer-arith -Wsign-compare -Wno-deprecated-declarations -Wno-multichar -Wno-unused -fno-strict-aliasing") + IF(WITH_COVERAGE) SET(PLATFORM_CFLAGS "-fprofile-arcs -ftest-coverage ${PLATFORM_CFLAGS}") ENDIF(WITH_COVERAGE) @@ -384,7 +432,7 @@ MACRO(NL_SETUP_BUILD) SET(PLATFORM_CXXFLAGS ${PLATFORM_CFLAGS}) IF(NOT APPLE) - SET(PLATFORM_LINKFLAGS "-Wl,--no-undefined -Wl,--as-needed") + SET(PLATFORM_LINKFLAGS "${PLATFORM_LINKFLAGS} -Wl,--no-undefined -Wl,--as-needed") ENDIF(NOT APPLE) SET(NL_DEBUG_CFLAGS "-DNL_DEBUG -D_DEBUG") @@ -453,7 +501,11 @@ MACRO(NL_SETUP_PREFIX_PATHS) IF(WIN32) SET(NL_LIB_PREFIX "../lib" CACHE PATH "Installation path for libraries.") ELSE(WIN32) - SET(NL_LIB_PREFIX "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation path for libraries.") + IF(CMAKE_LIBRARY_ARCHITECTURE) + SET(NL_LIB_PREFIX "${CMAKE_INSTALL_PREFIX}/lib/${CMAKE_LIBRARY_ARCHITECTURE}" CACHE PATH "Installation path for libraries.") + ELSE(CMAKE_LIBRARY_ARCHITECTURE) + SET(NL_LIB_PREFIX "${CMAKE_INSTALL_PREFIX}/lib" CACHE PATH "Installation path for libraries.") + ENDIF(CMAKE_LIBRARY_ARCHITECTURE) ENDIF(WIN32) ENDIF(NOT NL_LIB_PREFIX) @@ -462,7 +514,11 @@ MACRO(NL_SETUP_PREFIX_PATHS) IF(WIN32) SET(NL_DRIVER_PREFIX "../lib" CACHE PATH "Installation path for drivers.") ELSE(WIN32) - SET(NL_DRIVER_PREFIX "${CMAKE_INSTALL_PREFIX}/lib/nel" CACHE PATH "Installation path for drivers.") + IF(CMAKE_LIBRARY_ARCHITECTURE) + SET(NL_DRIVER_PREFIX "${CMAKE_INSTALL_PREFIX}/lib/${CMAKE_LIBRARY_ARCHITECTURE}/nel" CACHE PATH "Installation path for drivers.") + ELSE(CMAKE_LIBRARY_ARCHITECTURE) + SET(NL_DRIVER_PREFIX "${CMAKE_INSTALL_PREFIX}/lib/nel" CACHE PATH "Installation path for drivers.") + ENDIF(CMAKE_LIBRARY_ARCHITECTURE) ENDIF(WIN32) ENDIF(NOT NL_DRIVER_PREFIX) From 86eaabd0a2c8da84cb95972ef96875e7b3b508ba Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 27 Feb 2012 16:10:47 +0100 Subject: [PATCH 181/215] Changed: #1433 Merge changes from patch 1.13 --- code/ryzom/client/src/character_cl.cpp | 89 ++-- code/ryzom/client/src/character_cl.h | 55 +- code/ryzom/client/src/client.cpp | 15 +- code/ryzom/client/src/client_cfg.cpp | 8 +- code/ryzom/client/src/client_cfg.h | 4 +- code/ryzom/client/src/client_chat_manager.cpp | 59 +-- code/ryzom/client/src/commands.cpp | 39 +- code/ryzom/client/src/connection.cpp | 35 +- code/ryzom/client/src/cursor_functions.cpp | 3 + code/ryzom/client/src/entity_cl.cpp | 76 ++- code/ryzom/client/src/entity_cl.h | 27 +- .../src/interface_v3/action_handler_help.cpp | 49 +- .../src/interface_v3/action_handler_help.h | 9 + .../src/interface_v3/action_handler_item.cpp | 140 ++++- .../src/interface_v3/action_phrase_faber.cpp | 103 +++- .../src/interface_v3/action_phrase_faber.h | 5 +- .../src/interface_v3/bot_chat_page_trade.cpp | 17 +- .../client/src/interface_v3/chat_filter.cpp | 2 +- .../src/interface_v3/chat_text_manager.cpp | 27 +- .../client/src/interface_v3/chat_window.cpp | 26 +- .../src/interface_v3/ctrl_base_button.h | 2 + .../client/src/interface_v3/ctrl_button.cpp | 9 +- .../client/src/interface_v3/dbctrl_sheet.cpp | 171 +++++- .../client/src/interface_v3/dbctrl_sheet.h | 29 +- .../interface_v3/dbgroup_list_sheet_trade.cpp | 25 +- .../client/src/interface_v3/dbview_number.cpp | 28 +- .../client/src/interface_v3/group_html.cpp | 493 ++++++++++++++---- .../client/src/interface_v3/group_html.h | 46 +- .../client/src/interface_v3/group_html_cs.cpp | 14 +- .../client/src/interface_v3/group_html_cs.h | 4 +- .../src/interface_v3/group_html_forum.cpp | 11 +- .../src/interface_v3/group_html_forum.h | 4 +- .../src/interface_v3/group_html_mail.cpp | 11 +- .../client/src/interface_v3/group_html_mail.h | 4 +- .../src/interface_v3/group_html_webig.cpp | 150 +++++- .../src/interface_v3/group_html_webig.h | 30 +- .../interface_v3/group_in_scene_bubble.cpp | 126 +++++ .../src/interface_v3/group_in_scene_bubble.h | 3 + .../interface_v3/group_in_scene_user_info.cpp | 209 ++++---- .../client/src/interface_v3/group_table.cpp | 97 +++- .../client/src/interface_v3/group_table.h | 11 + .../client/src/interface_v3/guild_manager.cpp | 61 ++- .../client/src/interface_v3/interface_group.h | 18 +- .../src/interface_v3/interface_manager.cpp | 41 +- .../src/interface_v3/interface_manager.h | 1 + .../src/interface_v3/interface_parser.cpp | 22 +- .../src/interface_v3/inventory_manager.cpp | 80 ++- .../src/interface_v3/inventory_manager.h | 17 +- .../ryzom/client/src/interface_v3/lua_ihm.cpp | 404 +++++++++++++- code/ryzom/client/src/interface_v3/lua_ihm.h | 53 +- .../src/interface_v3/people_interraction.cpp | 82 ++- .../client/src/interface_v3/people_list.cpp | 28 +- .../client/src/interface_v3/player_trade.cpp | 1 + .../client/src/interface_v3/skill_manager.cpp | 2 +- .../ryzom/client/src/interface_v3/view_base.h | 4 + .../client/src/interface_v3/view_bitmap.h | 2 + .../client/src/interface_v3/view_link.cpp | 18 + .../ryzom/client/src/interface_v3/view_link.h | 3 + .../client/src/interface_v3/view_pointer.cpp | 82 ++- .../client/src/interface_v3/view_pointer.h | 1 + .../client/src/interface_v3/view_renderer.cpp | 9 +- .../client/src/interface_v3/view_renderer.h | 1 + code/ryzom/client/src/libwww.cpp | 6 +- code/ryzom/client/src/libwww.h | 5 +- code/ryzom/client/src/login.cpp | 21 +- code/ryzom/client/src/main_loop.cpp | 3 + code/ryzom/client/src/net_manager.cpp | 14 +- code/ryzom/client/src/player_cl.cpp | 189 +++---- .../client/src/string_manager_client.cpp | 41 +- code/ryzom/client/src/string_manager_client.h | 7 +- code/ryzom/client/src/user_entity.cpp | 47 +- code/ryzom/client/src/user_entity.h | 10 + 72 files changed, 2719 insertions(+), 819 deletions(-) diff --git a/code/ryzom/client/src/character_cl.cpp b/code/ryzom/client/src/character_cl.cpp index 1d7c87c2a..694ed9b16 100644 --- a/code/ryzom/client/src/character_cl.cpp +++ b/code/ryzom/client/src/character_cl.cpp @@ -358,17 +358,8 @@ CCharacterCL::CCharacterCL() _EventFactionId = 0; _PvpMode = PVP_MODE::None; - _PvpClan = PVP_CLAN::None; - - for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) - { - _PvpAllies[i] = false; - _PvpEnemies[i] = false; - } - - _ClanCivMaxFame = PVP_CLAN::None; - _ClanCultMaxFame = PVP_CLAN::None; + _LeagueId = 0; _OutpostId = 0; _OutpostSide = OUTPOSTENUMS::UnknownPVPSide; @@ -1842,9 +1833,30 @@ void CCharacterCL::updateVisualPropertyGuildNameID(const NLMISC::TGameCycle &/* //----------------------------------------------- void CCharacterCL::updateVisualPropertyEventFactionID(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { - _EventFactionId = uint32(prop); + // ODD Hack by Ulukyn + //_EventFactionId = uint32(prop); + _PvpMode = uint32(prop); + buildInSceneInterface(); + if (isUser()) + { + uint i; + uint numEntity = (uint)EntitiesMngr.entities().size(); + for (i=0; i(entity); + if (character) + { + if( character->getPvpMode() != 0 && !character->isUser()) + character->buildInSceneInterface (); + } + } + } + } } // updateVisualPropertyEventFactionID // //----------------------------------------------- @@ -1853,8 +1865,8 @@ void CCharacterCL::updateVisualPropertyEventFactionID(const NLMISC::TGameCycle & //----------------------------------------------- void CCharacterCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { - _PvpMode = (uint8)prop; - buildInSceneInterface(); + //_PvpMode = uint32(prop); + //buildInSceneInterface(); } // updateVisualPropertyPvpMode // @@ -1864,18 +1876,27 @@ void CCharacterCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &/* game //----------------------------------------------- void CCharacterCL::updateVisualPropertyPvpClan(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { - // get fames signs from prop - for (uint8 fameIdx = 0; fameIdx < 7; fameIdx++) - { - _PvpAllies[fameIdx] = (prop & (SINT64_CONSTANT(1) << (2*fameIdx))) != 0; - _PvpEnemies[fameIdx] = (prop & (SINT64_CONSTANT(1) << (2*fameIdx+1))) != 0; - } - - _ClanCivMaxFame = PVP_CLAN::TPVPClan((prop & (0x03 << 2*7)) >> 2*7); - _ClanCultMaxFame = PVP_CLAN::TPVPClan(4 + ((prop & (0x03 << 2*8)) >> 2*8)); - + _LeagueId = uint32(prop); buildInSceneInterface(); + if (isUser()) + { + uint i; + uint numEntity = (uint)EntitiesMngr.entities().size(); + for (i=0; i(entity); + if (character) + { + if( character->getPvpMode() != 0 && !character->isUser()) + character->buildInSceneInterface (); + } + } + } + } } // updateVisualPropertyPvpClan // //----------------------------------------------- @@ -4522,10 +4543,6 @@ void CCharacterCL::applyBehaviourFlyingHPs(const CBehaviourContext &bc, const MB else deltaHPColor = ClientCfg.SystemInfoParams["dg"].Color; } - else - { - deltaHPColor = CRGBA(127,127,127); - } } else { @@ -10252,7 +10269,7 @@ NLMISC_COMMAND(pvpMode, "modify pvp mode", "[ ]") if( args.size() == 0 ) { - uint8 pvpMode = playerTarget->getPvpMode(); + uint16 pvpMode = playerTarget->getPvpMode(); string str; if( pvpMode&PVP_MODE::PvpDuel ) str+="duel "; @@ -10270,6 +10287,10 @@ NLMISC_COMMAND(pvpMode, "modify pvp mode", "[ ]") str+="faction "; if( pvpMode&PVP_MODE::PvpFactionFlagged) str+="faction_flagged "; + if( pvpMode&PVP_MODE::PvpZoneSafe) + str+="in_safe_zone "; + if( pvpMode&PVP_MODE::PvpSafe) + str+="safe "; IM->displaySystemInfo(ucstring(str)); nlinfo(" %s",str.c_str()); } @@ -10280,14 +10301,14 @@ NLMISC_COMMAND(pvpMode, "modify pvp mode", "[ ]") fromString(args[1], state); if( state ) { - uint8 currentPVPMode = playerTarget->getPvpMode(); + uint16 currentPVPMode = playerTarget->getPvpMode(); currentPVPMode |= pvpMode; playerTarget->setPvpMode(currentPVPMode); IM->displaySystemInfo(toString(" adding pvp mode %s",args[0].c_str())); } else { - uint8 currentPVPMode = playerTarget->getPvpMode(); + uint16 currentPVPMode = playerTarget->getPvpMode(); currentPVPMode &= ~pvpMode; playerTarget->setPvpMode(currentPVPMode); IM->displaySystemInfo(toString(" removing pvp mode %s",args[0].c_str())); @@ -10297,7 +10318,7 @@ NLMISC_COMMAND(pvpMode, "modify pvp mode", "[ ]") return true; } - +/* NLMISC_COMMAND(pvpClan, "modify pvp clan", "") { if (args.size() != 1) return false; @@ -10319,13 +10340,13 @@ NLMISC_COMMAND(pvpClan, "modify pvp clan", "") if (!playerTarget) return false; - PVP_CLAN::TPVPClan clan = PVP_CLAN::fromString(args[0]); - playerTarget->setPvpClan(clan); +// PVP_CLAN::TPVPClan clan = PVP_CLAN::fromString(args[0]); +// playerTarget->setPvpClan(clan); playerTarget->buildInSceneInterface(); return true; } - +*/ #endif // !FINAL_VERSION #include "r2/editor.h" diff --git a/code/ryzom/client/src/character_cl.h b/code/ryzom/client/src/character_cl.h index a5d7ba880..6c87692d3 100644 --- a/code/ryzom/client/src/character_cl.h +++ b/code/ryzom/client/src/character_cl.h @@ -84,7 +84,7 @@ public: enum { MaxNumAura = 2 }; enum TAtkHeight { - AtkUnknownHeight = 0, + AtkUnkownHeight = 0, AtkLow, AtkMiddle, AtkHigh @@ -368,48 +368,10 @@ public: virtual uint64 getGuildSymbol () const { return _GuildSymbol; } virtual uint32 getEventFactionID() const { return _EventFactionId; } - virtual uint8 getPvpMode() const { return _PvpMode; } - void setPvpMode(uint8 mode) { _PvpMode=mode; } - virtual PVP_CLAN::TPVPClan getPvpClan() const { return _PvpClan; } - void setPvpClan(PVP_CLAN::TPVPClan clan) { _PvpClan=clan; } - - virtual PVP_CLAN::TPVPClan getClanCivMaxFame() const { return _ClanCivMaxFame; } - void setClanCivMaxFame(PVP_CLAN::TPVPClan clan) { _ClanCivMaxFame=clan; } - virtual PVP_CLAN::TPVPClan getClanCultMaxFame() const { return _ClanCultMaxFame; } - void setClanCultMaxFame(PVP_CLAN::TPVPClan clan) { _ClanCultMaxFame=clan; } - - virtual bool isPvpAlly(uint8 faction) const { if (faction < PVP_CLAN::NbClans) return _PvpAllies[faction]; else return false; } - virtual bool isPvpEnnemy(uint8 faction) const { if (faction < PVP_CLAN::NbClans) return _PvpEnemies[faction]; else return false; } - - virtual bool isPvpMarauder() const - { - for (uint8 i = 0; i < 4; i++) - if (!_PvpEnemies[i]) - return false; - return true; - } - - virtual bool isPvpTrytonist() const - { - if (_PvpEnemies[4] && _PvpEnemies[6]) - return true; - return false; - } - - virtual bool isPvpPrimas() const - { - if (_PvpAllies[4] && _PvpAllies[6]) - return true; - return false; - } - - virtual bool isPvpRanger() const - { - for (uint8 i = 0; i < 4; i++) - if (!_PvpAllies[i]) - return false; - return true; - } + virtual uint16 getPvpMode() const { return _PvpMode; } + void setPvpMode(uint16 mode) { _PvpMode=mode; } + virtual uint32 getLeagueID() const { return _LeagueId; } + void setLeagueID(uint32 league) { _LeagueId=league; } virtual uint16 getOutpostId() const { return _OutpostId; } virtual OUTPOSTENUMS::TPVPSide getOutpostSide() const { return _OutpostSide; } @@ -727,12 +689,9 @@ protected: uint32 _GuildNameId; uint64 _GuildSymbol; uint32 _EventFactionId; - uint8 _PvpMode; + uint16 _PvpMode; PVP_CLAN::TPVPClan _PvpClan; - PVP_CLAN::TPVPClan _ClanCivMaxFame; - PVP_CLAN::TPVPClan _ClanCultMaxFame; - bool _PvpAllies[PVP_CLAN::NbClans]; - bool _PvpEnemies[PVP_CLAN::NbClans]; + uint32 _LeagueId; uint16 _OutpostId; OUTPOSTENUMS::TPVPSide _OutpostSide; diff --git a/code/ryzom/client/src/client.cpp b/code/ryzom/client/src/client.cpp index 72fe1c0b1..49cc6e328 100644 --- a/code/ryzom/client/src/client.cpp +++ b/code/ryzom/client/src/client.cpp @@ -422,15 +422,12 @@ int main(int argc, char **argv) string sCmdLine = cmdline; #if FINAL_VERSION - if (sCmdLine.find("/multi") == string::npos) // If '/multi' not found - { - HANDLE mutex = CreateMutex (NULL, false, "RyzomClient"); - if (mutex && GetLastError() == ERROR_ALREADY_EXISTS) - { - delete appContext; - return 0; - } - } + //if (sCmdLine.find("/multi") == string::npos) // If '/multi' not found + //{ + // HANDLE mutex = CreateMutex (NULL, false, "RyzomClient"); + // if (mutex && GetLastError() == ERROR_ALREADY_EXISTS) + // exit (0); + //} initCrashReport (); #endif // FINAL_VERSION diff --git a/code/ryzom/client/src/client_cfg.cpp b/code/ryzom/client/src/client_cfg.cpp index ff270ad1a..54a3d9b45 100644 --- a/code/ryzom/client/src/client_cfg.cpp +++ b/code/ryzom/client/src/client_cfg.cpp @@ -424,6 +424,7 @@ CClientConfig::CClientConfig() PatchWanted = false; #endif PatchUrl = ""; + PatchletUrl = ""; PatchVersion = ""; PatchServer = ""; @@ -433,6 +434,7 @@ CClientConfig::CClientConfig() RingReleaseNotePath = "http://"+WebIgMainDomain+"/releasenotes_ring/index.php"; ReleaseNotePath = "http://"+WebIgMainDomain+"/releasenotes/index.php"; + /////////////// // ANIMATION // // With a bigger angle, rotation is animated. @@ -1044,9 +1046,13 @@ void CClientConfig::setValues() READ_STRING_DEV(ReleaseNotePath) READ_STRING_FV(PatchServer) + ///////////////////////// + // NEW PATCHLET SYSTEM // + READ_STRING_FV(PatchletUrl) + //////////////////////// // WEBIG // - READ_STRING_DEV(WebIgMainDomain); + READ_STRING_FV(WebIgMainDomain); READ_STRINGVECTOR_FV(WebIgTrustedDomains); /////////////// diff --git a/code/ryzom/client/src/client_cfg.h b/code/ryzom/client/src/client_cfg.h index 667606814..86e70c1b8 100644 --- a/code/ryzom/client/src/client_cfg.h +++ b/code/ryzom/client/src/client_cfg.h @@ -286,17 +286,17 @@ struct CClientConfig // NEW PATCHING SYSTEM // bool PatchWanted; std::string PatchUrl; + std::string PatchletUrl; std::string PatchVersion; std::string PatchServer; std::string RingReleaseNotePath; std::string ReleaseNotePath; - //////////////////////// - // WEBIG // std::string WebIgMainDomain; std::vector WebIgTrustedDomains; + /////////////// // ANIMATION // // With a bigger angle, rotation is animated. diff --git a/code/ryzom/client/src/client_chat_manager.cpp b/code/ryzom/client/src/client_chat_manager.cpp index fa4159b1f..e7ef54d02 100644 --- a/code/ryzom/client/src/client_chat_manager.cpp +++ b/code/ryzom/client/src/client_chat_manager.cpp @@ -963,16 +963,10 @@ void CClientChatManager::buildTellSentence(const ucstring &sender, const ucstrin else { // Does the char have a CSR title? - if (CHARACTER_TITLE::isCsrTitle(CEntityCL::getTitleFromName(sender))) csr = ucstring("(CSR) "); + csr = CHARACTER_TITLE::isCsrTitle(CEntityCL::getTitleFromName(sender)) ? ucstring("(CSR) ") : ucstring(""); } - ucstring cur_time; - CCDBNodeLeaf *pNL = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false); - if (pNL && pNL->getValueBool()) - { - cur_time = CInterfaceManager::getTimestampHuman(); - } - result = cur_time + csr + name + ucstring(" ") + CI18N::get("tellsYou") + ucstring(": ") + msg; + result = csr + name + ucstring(" ") + CI18N::get("tellsYou") + ucstring(": ") + msg; } } @@ -1002,23 +996,18 @@ void CClientChatManager::buildChatSentence(TDataSetIndex /* compressedSenderInde if (!catStr.empty()) cat = string("&")+catStr+"&"; + if ( ! cat.empty()) + { + result = msg; + return; + } + // Format the sentence with the provided sender name ucstring senderName = CEntityCL::removeTitleAndShardFromName(sender); - // Add time if not a &bbl& - ucstring cur_time; - if (cat.toString() != "&bbl&") - { - CCDBNodeLeaf *pNL = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false); - if (pNL && pNL->getValueBool()) - { - cur_time = CInterfaceManager::getTimestampHuman(); - } - } - ucstring csr; // Does the char have a CSR title? - if (CHARACTER_TITLE::isCsrTitle(CEntityCL::getTitleFromName(sender))) csr = ucstring("(CSR) "); + csr = CHARACTER_TITLE::isCsrTitle(CEntityCL::getTitleFromName(sender)) ? ucstring("(CSR) ") : ucstring(""); if (UserEntity && senderName == UserEntity->getDisplayName()) { @@ -1026,10 +1015,10 @@ void CClientChatManager::buildChatSentence(TDataSetIndex /* compressedSenderInde switch(type) { case CChatGroup::shout: - result = cat + cur_time + csr + CI18N::get("youShout") + ucstring(": ") + finalMsg; + result = cat + csr + CI18N::get("youShout") + ucstring(": ") + finalMsg; break; default: - result = cat + cur_time + csr + CI18N::get("youSay") + ucstring(": ") + finalMsg; + result = cat + csr + CI18N::get("youSay") + ucstring(": ") + finalMsg; break; } } @@ -1048,10 +1037,10 @@ void CClientChatManager::buildChatSentence(TDataSetIndex /* compressedSenderInde switch(type) { case CChatGroup::shout: - result = cat + cur_time + csr + senderName + ucstring(" ") + CI18N::get("heShout") + ucstring(": ") + finalMsg; + result = cat + csr + senderName + ucstring(" ") + CI18N::get("heShout") + ucstring(": ") + finalMsg; break; default: - result = cat + cur_time + csr + senderName + ucstring(" ") + CI18N::get("heSays") + ucstring(": ") + finalMsg; + result = cat + csr + senderName + ucstring(" ") + CI18N::get("heSays") + ucstring(": ") + finalMsg; break; } } @@ -1185,14 +1174,8 @@ class CHandlerTell : public IActionHandler ucstring finalMsg; CChatWindow::encodeColorTag(prop.getRGBA(), finalMsg, false); - ucstring cur_time; - CCDBNodeLeaf *pNL = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false); - if (pNL && pNL->getValueBool()) - cur_time = CInterfaceManager::getTimestampHuman(); - - ucstring csr; - if (CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw())) csr += ucstring("(CSR) "); - finalMsg += cur_time + csr + CI18N::get("youTell") + ": "; + ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + finalMsg += csr + CI18N::get("youTell") + ": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); CChatWindow::encodeColorTag(prop.getRGBA(), finalMsg, true); finalMsg += message; @@ -1351,8 +1334,18 @@ class CHandlerTalk : public IActionHandler { string str = text.toUtf8(); string cmdWithArgs = str.substr(1); - /* In the chat context, only ' ' is a possible separator */ + + // Get the command name from the string, can contain spaces string cmd = cmdWithArgs.substr(0, cmdWithArgs.find(' ')); + if (cmdWithArgs.find('"') == 0) + { + string::size_type pos = cmdWithArgs.find('"', 1); + if (string::npos != pos) + { + cmd = cmdWithArgs.substr(1, pos - 1); + } + } + if ( NLMISC::ICommand::exists( cmd ) ) { NLMISC::ICommand::execute( cmdWithArgs, g_log ); diff --git a/code/ryzom/client/src/commands.cpp b/code/ryzom/client/src/commands.cpp index 877f80429..f62d8f245 100644 --- a/code/ryzom/client/src/commands.cpp +++ b/code/ryzom/client/src/commands.cpp @@ -192,7 +192,7 @@ NLMISC_COMMAND(where, "Ask information on the position", "") return false; } -NLMISC_COMMAND(who, "Display all players currently in game","[]") +NLMISC_COMMAND(who, "Display all players currently in region","[]") { // Check parameters. if(args.size() > 1) @@ -1177,7 +1177,7 @@ NLMISC_COMMAND(db, "Modify Database"," ") if(node) node->setValue64(value); else - pIM->displaySystemInfo(toString("DB %s don't exist.", args[0].c_str())); + pIM->displaySystemInfo(toString("DB '%s' does not exist.", args[0].c_str())); #else pIM->displaySystemInfo(ucstring("Can't write to DB when in Final Version.")); #endif @@ -1193,7 +1193,7 @@ NLMISC_COMMAND(db, "Modify Database"," ") nlinfo("%s", str.c_str()); } else - pIM->displaySystemInfo(toString("DB %s don't exist.", args[0].c_str())); + pIM->displaySystemInfo(toString("DB '%s' does not exist.", args[0].c_str())); return true; } else @@ -1295,6 +1295,24 @@ NLMISC_COMMAND(setItemName, "set name of items, sbrick, etc.."," ") +{ + if (args.size() < 2) return false; + ucstring name; + name.fromUtf8(args[0]); + ucstring text; + text.fromUtf8(args[1]); + + STRING_MANAGER::CStringManagerClient *pSMC = STRING_MANAGER::CStringManagerClient::instance(); + if (pSMC) + pSMC->replaceDynString(name, text); + else + return false; + return true; +} + + + ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////// @@ -5243,7 +5261,7 @@ bool CUserCommand::execute(const std::string &/* rawCommandString */, const std: { if ((uint)index >= args.size()) { - // Not enough argument + // Not enough arguments pIM->displaySystemInfo (ucstring(CommandName+" : ")+CI18N::get ("uiCommandWrongArgumentCount")); return false; } @@ -5253,11 +5271,18 @@ bool CUserCommand::execute(const std::string &/* rawCommandString */, const std: finalArgs += /*ucstring(*/args[index++]/*).toUtf8()*/; else { - finalArgs += /*ucstring(*/args[index++]/*).toUtf8()*/; while (index 0) ? " " : ""; + // If arg contains spaces then put it in quotes + if (string::npos != args[index].find(" ")) + { + finalArgs += "\"" + args[index++] + "\""; + } + else + { + finalArgs += args[index++]; + } } } } diff --git a/code/ryzom/client/src/connection.cpp b/code/ryzom/client/src/connection.cpp index 1750d253b..32962cd93 100644 --- a/code/ryzom/client/src/connection.cpp +++ b/code/ryzom/client/src/connection.cpp @@ -244,20 +244,20 @@ class CAHOnReloadTestPage: public IActionHandler REGISTER_ACTION_HANDLER (CAHOnReloadTestPage, "on_reload_test_page"); -//void initWebBrowser() -//{ -// CInterfaceManager *pIM = CInterfaceManager::getInstance(); -// pIM->getDbProp("UI:VARIABLES:SCREEN")->setValue32(UI_VARIABLES_SCREEN_WEBSTART); -// -// // start the browser -// CGroupHTML *pGH = dynamic_cast(pIM->getElementFromId(GROUP_BROWSER)); -// -// if (pGH) -// { -// pGH->setActive(true); -// pGH->browse(MainPageURL.c_str()); -// } -//} +void initWebBrowser() +{ + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + //pIM->getDbProp("UI:VARIABLES:SCREEN")->setValue32(UI_VARIABLES_SCREEN_WEBSTART); + + // start the browser + CGroupHTML *pGH = dynamic_cast(pIM->getElementFromId(GROUP_BROWSER)); + + if (pGH) + { + pGH->setActive(true); + pGH->browse(ClientCfg.PatchletUrl.c_str()); + } +} // ------------------------------------------------------------------------------------------------ @@ -392,9 +392,6 @@ bool connection (const string &cookie, const string &fsaddr) pIM->getDbProp ("UI:CURRENT_SCREEN")->setValue32(ClientCfg.Local ? 6 : -1); // TMP TMP CCDBNodeBranch::flushObserversCalls(); - // Init web box - //initWebBrowser(); - // Active inputs Actions.enable(true); EditActions.enable(true); @@ -422,6 +419,10 @@ bool connection (const string &cookie, const string &fsaddr) nlinfo ("PROFILE: %d seconds for connection", (uint32)(ryzomGetLocalTime ()-connStart)/1000); + // Init web box + nlinfo("ok"); + initWebBrowser(); + // TMP TMP if (ClientCfg.Local) { diff --git a/code/ryzom/client/src/cursor_functions.cpp b/code/ryzom/client/src/cursor_functions.cpp index c212dbb0d..2ced655c9 100644 --- a/code/ryzom/client/src/cursor_functions.cpp +++ b/code/ryzom/client/src/cursor_functions.cpp @@ -484,6 +484,9 @@ void checkUnderCursor() if(ContextCur.context("ATTACK")) return; } + + if(testMissionOption(0)) + return; } // Dead else diff --git a/code/ryzom/client/src/entity_cl.cpp b/code/ryzom/client/src/entity_cl.cpp index fbbe0c041..421faad47 100644 --- a/code/ryzom/client/src/entity_cl.cpp +++ b/code/ryzom/client/src/entity_cl.cpp @@ -115,7 +115,7 @@ NLMISC::CRGBA CEntityCL::_UserPackAnimalColor; NLMISC::CRGBA CEntityCL::_PvpEnemyColor; NLMISC::CRGBA CEntityCL::_PvpNeutralColor; NLMISC::CRGBA CEntityCL::_PvpAllyInTeamColor; -NLMISC::CRGBA CEntityCL::_PvpAllyInGuildColor; +NLMISC::CRGBA CEntityCL::_PvpAllyInLeagueColor; NLMISC::CRGBA CEntityCL::_PvpAllyColor; NLMISC::CRGBA CEntityCL::_GMTitleColor[CHARACTER_TITLE::EndGmTitle - CHARACTER_TITLE::BeginGmTitle + 1]; uint8 CEntityCL::_InvalidGMTitleCode = 0xFF; @@ -2295,6 +2295,7 @@ void CEntityCL::onStringAvailable(uint /* stringId */, const ucstring &value) womenTitle = true; } const ucstring replacement(STRING_MANAGER::CStringManagerClient::getTitleLocalizedName(_TitleRaw,womenTitle)); + _Tags = STRING_MANAGER::CStringManagerClient::getTitleInfos(_TitleRaw,womenTitle); if (!replacement.empty() || !ClientCfg.DebugStringManager) { // build the final name @@ -2436,7 +2437,6 @@ public: CEntityCL::_PvpEnemyColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPENEMY")->getValueRGBA(); CEntityCL::_PvpAllyColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPALLY")->getValueRGBA(); CEntityCL::_PvpAllyInTeamColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPALLYINTEAM")->getValueRGBA(); - CEntityCL::_PvpAllyInGuildColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPALLYINGUILD")->getValueRGBA(); CEntityCL::_PvpNeutralColor = pIM->getDbProp("UI:SAVE:ENTITY:COLORS:PVPNEUTRAL")->getValueRGBA(); // don't save these colors in .icfg because players can't change them @@ -2463,7 +2463,7 @@ bool CEntityCL::isTarget () const //----------------------------------------------- -bool CEntityCL::isInGuild () const +bool CEntityCL::isInSameGuild () const { if (Type != Player && Type != User) return false; @@ -2477,6 +2477,33 @@ bool CEntityCL::isInGuild () const //----------------------------------------------- +bool CEntityCL::oneInLeague () const +{ + if (Type != Player && Type != User) + return false; + + const uint32 leagueID = getLeagueID(); + if ((UserEntity && (UserEntity->getLeagueID() != 0)) || leagueID != 0) + return true; + + return false; +} + +//----------------------------------------------- + +bool CEntityCL::isInSameLeague () const +{ + if (Type != Player && Type != User) + return false; + + const uint32 leagueID = getLeagueID(); + if ((leagueID != 0) && UserEntity && (leagueID == UserEntity->getLeagueID())) + return true; + + return false; +} +//----------------------------------------------- + NLMISC::CRGBA CEntityCL::getColor () const { // target @@ -2511,46 +2538,43 @@ NLMISC::CRGBA CEntityCL::getColor () const { if (isEnemy()) { - if (getPvpMode()&PVP_MODE::PvpFactionFlagged || getPvpMode()&PVP_MODE::PvpChallenge) - return _PvpEnemyColor; + if (getPvpMode()&PVP_MODE::PvpFaction) + return CRGBA::CRGBA(min(255, _PvpEnemyColor.R+150), min(255, _PvpEnemyColor.G+150), min(255, _PvpEnemyColor.B+150),_PvpEnemyColor.A); else - return CRGBA(min(255, _PvpEnemyColor.R+150), min(255, _PvpEnemyColor.G+150), min(255, _PvpEnemyColor.B+150),_PvpEnemyColor.A); + return _PvpEnemyColor; } } - // neutral pvp - if (isNeutralPVP()) - { - if (isInTeam()) - return _PvpAllyInTeamColor; - if (isInGuild()) - return _PvpAllyInGuildColor; - return _PvpNeutralColor; - } // ally if (isAlly()) { if (getPvpMode() & PVP_MODE::PvpFactionFlagged) { - if (isInTeam()) - return _PvpAllyInTeamColor; - if(isInGuild()) - return _PvpAllyInGuildColor; - return _PvpAllyColor; + if(isInSameLeague()) + return CRGBA::CRGBA(max(0, _PvpAllyColor.R-100), max(0, _PvpAllyColor.G-100), max(0, _PvpAllyColor.B-100),_PvpAllyColor.A); + return CRGBA::CRGBA(max(0, _PvpAllyInTeamColor.R-100), max(0, _PvpAllyInTeamColor.G-100), max(0, _PvpAllyInTeamColor.B-100),_PvpAllyInTeamColor.A); } else { - if (isInTeam()) - return CRGBA(min(255, _PvpAllyInTeamColor.R+150), min(255, _PvpAllyInTeamColor.G+150), min(255, _PvpAllyInTeamColor.B+150),_PvpAllyInTeamColor.A); - if(isInGuild()) - return CRGBA(min(255, _PvpAllyInGuildColor.R+150), min(255, _PvpAllyInGuildColor.G+150), min(255, _PvpAllyInGuildColor.B+150),_PvpAllyInGuildColor.A); - return CRGBA(min(255, _PvpAllyColor.R+150), min(255, _PvpAllyColor.G+150), min(255, _PvpAllyColor.B+150),_PvpAllyColor.A); + if(isInSameLeague()) + return _PvpAllyColor; + return _PvpAllyInTeamColor; } } + + // neutral pvp + if (isNeutralPVP()) + return _PvpNeutralColor; + // neutral if (isInTeam()) return _GroupColor; - if (isInGuild()) + + // neutral + if (isInSameLeague()) + return CRGBA::CRGBA(min(255, _GroupColor.R+50), min(255, _GroupColor.G+50), min(255, _GroupColor.B+50),_GroupColor.A); + + if (isInSameGuild()) return _GuildColor; } return _EntitiesColor[Type]; diff --git a/code/ryzom/client/src/entity_cl.h b/code/ryzom/client/src/entity_cl.h index 1c79161ea..bb508dd42 100644 --- a/code/ryzom/client/src/entity_cl.h +++ b/code/ryzom/client/src/entity_cl.h @@ -686,7 +686,7 @@ public: virtual bool isNeutralPVP() const { return false; } /// Return true if this player has the viewing properties of a friend (inscene bars...) - virtual bool isViewedAsFriend() const { return isNeutral() || isFriend() || isInTeam() || isInGuild(); } + virtual bool isViewedAsFriend() const { return isNeutral() || isFriend() || isInTeam() || isInSameGuild() || isInSameLeague(); } /// Return the People for the entity (unknown by default) virtual EGSPD::CPeople::TPeople people() const; @@ -733,7 +733,13 @@ public: bool isInTeam () const { return _IsInTeam; } // Return true if this entity is in the user guild - bool isInGuild () const; + bool isInSameGuild () const; + + // Return true if this entity is in the user league + bool isInSameLeague () const; + + // Return true if this entity or the user is in a league + bool oneInLeague () const; // Return true if this entity is a Mount owned by the User bool isUserMount () const {return _IsUserMount;} @@ -746,8 +752,9 @@ public: virtual uint64 getGuildSymbol () const { return 0; } virtual uint32 getEventFactionID() const { return 0; } - virtual uint8 getPvpMode() const { return PVP_MODE::None; } + virtual uint16 getPvpMode() const { return PVP_MODE::None; } virtual PVP_CLAN::TPVPClan getPvpClan() const { return PVP_CLAN::None; } + virtual uint32 getLeagueID() const { return 0; } virtual uint16 getOutpostId() const { return 0; } virtual OUTPOSTENUMS::TPVPSide getOutpostSide() const { return OUTPOSTENUMS::UnknownPVPSide; } @@ -760,6 +767,16 @@ public: return _Title; } + /// Return the entity tags + const ucstring &getTag(uint8 id) const + { + if (_Tags.size() > id) { + return _Tags[id]; + } + static ucstring empty; + return empty; + } + /// Return the raw unparsed entity title const ucstring getTitleRaw() const { @@ -910,6 +927,8 @@ protected: ucstring _EntityName; // Current entity title ucstring _Title; + // Current entity tags + std::vector _Tags; // Current entity title string id std::string _TitleRaw; // Current permanent content symbol for the entity @@ -1006,7 +1025,7 @@ protected: static NLMISC::CRGBA _PvpEnemyColor; static NLMISC::CRGBA _PvpNeutralColor; static NLMISC::CRGBA _PvpAllyInTeamColor; - static NLMISC::CRGBA _PvpAllyInGuildColor; + static NLMISC::CRGBA _PvpAllyInLeagueColor; static NLMISC::CRGBA _PvpAllyColor; // colors for GM players static NLMISC::CRGBA _GMTitleColor[CHARACTER_TITLE::EndGmTitle - CHARACTER_TITLE::BeginGmTitle + 1]; diff --git a/code/ryzom/client/src/interface_v3/action_handler_help.cpp b/code/ryzom/client/src/interface_v3/action_handler_help.cpp index 4c92fe6e0..ddcb324d7 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_help.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_help.cpp @@ -57,6 +57,9 @@ #include "sbrick_manager.h" #include "sphrase_manager.h" #include "action_handler_help.h" +#include "nel/misc/i18n.h" +#include "nel/misc/algo.h" +#include "nel/net/email.h" #include "game_share/mission_desc.h" #include "game_share/inventories.h" #include "game_share/visual_slot_manager.h" @@ -378,7 +381,7 @@ CInterfaceGroup *CInterfaceHelp::activateNextWindow(CDBCtrlSheet *elt, sint forc setup.setupDefaultIDs(); setup.SrcSheet = elt; setup.HelpWindow = group; - setupCreatorName(setup); + //setupCreatorName(setup); // Hide elements by defaults resetSheetHelp(setup); @@ -2064,6 +2067,12 @@ void getItemText (CDBCtrlSheet *item, ucstring &itemText, const CItemSheet*pIS) strFindReplace(itemText, "%r2_comment_text", toString(itemInfo.R2ItemComment)); } break; + case ITEMFAMILY::PET_ANIMAL_TICKET: + { + string nr = (itemInfo.PetNumber > 0) ? toString(itemInfo.PetNumber) : "(slot)" + toString(item->getIndexInDB()); + strFindReplace(itemText, "%petnumber", nr); + } + break; default: { strFindReplace(itemText, "%no_rent", pIS->IsItemNoRent ? CI18N::get("uihelpItemNoRent") : string("")); @@ -2745,8 +2754,15 @@ void setupCreatorName(CSheetHelpSetup &setup) CViewText *vthd = dynamic_cast(setup.HelpWindow->getView("creator_header")); if (vtid != NULL) { - // if not an item, disable the view - if(!setup.SrcSheet || setup.SrcSheet->getType()!=CCtrlSheetInfo::SheetType_Item ) + bool bIsRM = false; + if (setup.SrcSheet) + { + const CItemSheet *pIS= dynamic_cast(SheetMngr.get(CSheetId(setup.SrcSheet->getSheetId()))); + bIsRM = (pIS && pIS->Family == ITEMFAMILY::RAW_MATERIAL); + } + + // if a RM or not an item, disable the view + if(!setup.SrcSheet || bIsRM || setup.SrcSheet->getType()!=CCtrlSheetInfo::SheetType_Item ) { // important else a brick could display a creator name.... vtid->setActive(false); @@ -3569,6 +3585,31 @@ public: }; REGISTER_ACTION_HANDLER( CHandlerAuraModifierTooltip, "aura_modifier_tooltip"); +// *************************************************************************** +class CHandlerUserPaToolTip : public IActionHandler +{ +public: + virtual void execute(CCtrlBase *pCaller, const string &Params) + { + CInterfaceManager *pIM= CInterfaceManager::getInstance(); + + uint8 index; + fromString(Params, index); + --index; // Param is 1-based so subtract 1 + if (index < 0 || index >= MAX_INVENTORY_ANIMAL) + { + return; + } + + ucstring txt; + CCDBNodeLeaf *node = pIM->getDbProp(toString("SERVER:PACK_ANIMAL:BEAST%d:NAME", index)); + if (node && CStringManagerClient::instance()->getDynString(node->getValue32(), txt)) + { + pIM->setContextHelpText(CEntityCL::removeTitleFromName(txt)); + } + } +}; +REGISTER_ACTION_HANDLER( CHandlerUserPaToolTip, "userpa_name_tooltip"); // *************************************************************************** class CHandlerAnimalDeadPopupTooltip : public IActionHandler @@ -3922,7 +3963,7 @@ public: #ifdef NL_OS_WINDOWS if (Driver) { - HWND wnd = Driver->getDisplay(); + HWND wnd = (HWND) Driver->getDisplay(); ShowWindow(wnd, SW_MINIMIZE); } #endif diff --git a/code/ryzom/client/src/interface_v3/action_handler_help.h b/code/ryzom/client/src/interface_v3/action_handler_help.h index 7430ee8cf..48253ea96 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_help.h +++ b/code/ryzom/client/src/interface_v3/action_handler_help.h @@ -64,6 +64,15 @@ void refreshItemHelp(CSheetHelpSetup &setup); // refresh help for a mission void refreshMissionHelp(CSheetHelpSetup &setup, const CPrerequisitInfos &infos); +class CPetAnimalItemInfoWaiter : public IItemInfoWaiter +{ + void infoReceived() + { + //ItemSheet + //ItemSlotId + CClientItemInfo info = getInventory().getItemInfo(ItemSlotId); + } +}; // *************************************************************************** diff --git a/code/ryzom/client/src/interface_v3/action_handler_item.cpp b/code/ryzom/client/src/interface_v3/action_handler_item.cpp index 819b8c0d4..ce5e49307 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_item.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_item.cpp @@ -31,9 +31,11 @@ #include "../net_manager.h" #include "group_menu.h" #include "../global.h" +#include "group_html.h" // #include "game_share/inventories.h" +#include "game_share/bot_chat_types.h" #include "macrocmd_manager.h" #include "inventory_manager.h" @@ -43,6 +45,7 @@ #include "view_bitmap.h" extern CSheetManager SheetMngr; +extern NLMISC::CLog g_log; using namespace std; using namespace NLMISC; @@ -80,8 +83,6 @@ void CInterfaceItemEdition::setCurrWindow(CDBCtrlSheet* ctrlSheet, const std::st _CurrWindow.IsInEditionMode = isInEditionMode; _CurrWindow.begin(); } - - } // ******************************************************************************************** @@ -153,13 +154,26 @@ void CInterfaceItemEdition::CItemEditionWindow::infoReceived() if (itemInfo.CustomText.empty()) display->setTextFormatTaged(ucstring(STRING_MANAGER::CStringManagerClient::getItemLocalizedDescription(pIS->Id))); else - display->setTextFormatTaged(itemInfo.CustomText); + { + ucstring text = itemInfo.CustomText; + string::size_type delimiter = text.find(' '); + if(text.size() > 3 && text[0]=='@' && text[1]=='W' && text[2]=='E' && text[3]=='B') + { + CGroupHTML *pGH = dynamic_cast(pIM->getElementFromId("ui:interface:web_transactions:content:html")); + if (pGH) + pGH->browse(ucstring(text.substr(4, delimiter-4)).toString().c_str()); + if (delimiter == string::npos) + group->setActive(false); + else + text = text.substr(delimiter, text.size()-delimiter); + } + + display->setTextFormatTaged(text); + } } - } } } - } @@ -278,7 +292,21 @@ void CInterfaceItemEdition::CItemEditionWindow::begin() if (itemInfo.CustomText.empty()) display->setTextFormatTaged(ucstring(STRING_MANAGER::CStringManagerClient::getItemLocalizedDescription(pIS->Id))); else - display->setTextFormatTaged(itemInfo.CustomText); + { + ucstring text = itemInfo.CustomText; + string::size_type delimiter = text.find(' '); + if(text.size() > 3 && text[0]=='@' && text[1]=='W' && text[2]=='E' && text[3]=='B') + { + CGroupHTML *pGH = dynamic_cast(pIM->getElementFromId("ui:interface:web_transactions:content:html")); + if (pGH) + pGH->browse(ucstring(text.substr(4, delimiter-4)).toString().c_str()); + if (delimiter == string::npos) + group->setActive(false); + else + text = text.substr(delimiter, text.size()-delimiter); + } + display->setTextFormatTaged(text); + } } else { @@ -287,7 +315,6 @@ void CInterfaceItemEdition::CItemEditionWindow::begin() } } } - } } } @@ -366,7 +393,6 @@ void CInterfaceItemEdition::CItemEditionWindow::validate() text = editBoxLarge->getInputString(); } - if (textValid) { CBitMemStream out; @@ -972,6 +998,12 @@ bool checkCanExchangeItem(CDBCtrlSheet *pCSSrc) bDropOrSell = pIS->canExchangeOrGive(PlayerTrade.BotChatGiftContext); } + // Locked by owner; cannot trade + if (pCSSrc->getLockedByOwner()) + { + return false; + } + // Special case if this is an animal ticket if ((pIS != NULL) && (pIS->Family == ITEMFAMILY::PET_ANIMAL_TICKET)) { @@ -1209,6 +1241,13 @@ class CHandlerDestroyItem : public IActionHandler nlwarning(" no caller sheet found"); return; } + + // Don't destroy locked items + if (item->getLockedByOwner()) + { + return; + } + // if the item is currently selected, removes the selection if (item == CDBCtrlSheet::getCurrSelection()) { @@ -1712,11 +1751,15 @@ class CHandlerItemMenuCheck : public IActionHandler CViewTextMenu *pXpCatalyserUse = dynamic_cast(pMenu->getView("xp_catalyser_use")); CViewTextMenu *pDrop = dynamic_cast(pMenu->getView("drop")); CViewTextMenu *pDestroy = dynamic_cast(pMenu->getView("destroy")); + CViewTextMenu *pLockUnlock = dynamic_cast(pMenu->getView("lockunlock")); CViewTextMenu *pMoveSubMenu = dynamic_cast(pMenu->getView("move")); CViewTextMenu *pMoveToBag = dynamic_cast(pMenu->getView("bag")); CViewTextMenu *pMoveToGuild = dynamic_cast(pMenu->getView("guild")); CViewTextMenu *pMoveToRoom = dynamic_cast(pMenu->getView("room")); CViewTextMenu *pMoveToPa[MAX_INVENTORY_ANIMAL]; + + bool bIsLockedByOwner = pCS->getLockedByOwner(); + for(i=0;i(pMenu->getView(toString("pa%d", i))); @@ -1735,24 +1778,27 @@ class CHandlerItemMenuCheck : public IActionHandler if(pXpCatalyserUse) pXpCatalyserUse->setActive(false); if(pItemTextDisplay) pItemTextDisplay->setActive(false); if(pItemTextEdition) pItemTextEdition->setActive(false); + + if(pLockUnlock) pLockUnlock->setActive(true); + const CItemSheet *pIS = pCS->asItemSheet(); if (invId != INVENTORIES::guild) if (pIS != NULL) { - if (pCrisEnchant && pIS->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) + if (pCrisEnchant && pIS->Family == ITEMFAMILY::CRYSTALLIZED_SPELL && !bIsLockedByOwner) pCrisEnchant->setActive(true); - if (pCrisReload && pIS->Family == ITEMFAMILY::ITEM_SAP_RECHARGE) + if (pCrisReload && pIS->Family == ITEMFAMILY::ITEM_SAP_RECHARGE && !bIsLockedByOwner) pCrisReload->setActive(true); // teleport can be used only from bag (NB: should not exist in mektoub....) - if (pTeleportUse && pIS->Family == ITEMFAMILY::TELEPORT && pCS->getInventoryIndex()==INVENTORIES::bag) + if (pTeleportUse && pIS->Family == ITEMFAMILY::TELEPORT && pCS->getInventoryIndex()==INVENTORIES::bag && !bIsLockedByOwner) { pTeleportUse->setActive(true); } - if (pItemConsume && pIS->IsConsumable == true && pCS->getInventoryIndex()==INVENTORIES::bag) + if (pItemConsume && pIS->IsConsumable == true && pCS->getInventoryIndex()==INVENTORIES::bag && !bIsLockedByOwner) { pItemConsume->setActive(true); } - if (pXpCatalyserUse && pIS->Family == ITEMFAMILY::XP_CATALYSER && pCS->getInventoryIndex()==INVENTORIES::bag) + if (pXpCatalyserUse && pIS->Family == ITEMFAMILY::XP_CATALYSER && pCS->getInventoryIndex()==INVENTORIES::bag && !bIsLockedByOwner) { pXpCatalyserUse->setActive(true); } @@ -1815,9 +1861,10 @@ class CHandlerItemMenuCheck : public IActionHandler for(i=0;isetActive(false); - // additionnaly, cannot drop/destroy an animal item. + // additionnaly, cannot drop/destroy/lock an animal item. if(pDrop) pDrop->setActive(false); if(pDestroy) pDestroy->setActive(false); + if(pLockUnlock) pLockUnlock->setActive(false); } else { @@ -1845,6 +1892,7 @@ class CHandlerItemMenuCheck : public IActionHandler // std case: can drop / destroy if(pDrop) pDrop->setActive(invId!=INVENTORIES::guild); if(pDestroy) pDestroy->setActive(invId!=INVENTORIES::guild); + if(pLockUnlock) pLockUnlock->setActive(invId!=INVENTORIES::guild); } // hide the move entry completely? @@ -1899,6 +1947,29 @@ class CHandlerItemMenuCheck : public IActionHandler pItemTextDisplay->setGrayed(pIM->getDbProp("UI:VARIABLES:ISACTIVE:PHRASE_EDIT_CUSTOM")->getValueBool()); } + if (pCS->getGrayed()) + { + if (pEquip) pEquip->setActive(false); + if (pDestroy) pDestroy->setActive(false); + if (pLockUnlock) pLockUnlock->setActive(false); + if (pMoveSubMenu) pMoveSubMenu->setActive(false); + } + + if (bIsLockedByOwner) + { + if (pLockUnlock) pLockUnlock->setHardText("uimUnlockItem"); + // Cannot drop/destroy if locked by owner + if (pDrop) pDrop->setActive(false); + if (pDestroy) pDestroy->setActive(false); + } + else + { + if (pLockUnlock) pLockUnlock->setHardText("uimLockItem"); + } + + // Only show lock menu item if inventory contains the info + if (pLockUnlock) pLockUnlock->setActive(pCS->canOwnerLock()); + // **** Gray Entries // If ourselves are not available, then gray all @@ -1911,6 +1982,7 @@ class CHandlerItemMenuCheck : public IActionHandler if(pXpCatalyserUse) pXpCatalyserUse->setGrayed(true); if(pDrop) pDrop->setGrayed(true); if(pDestroy) pDestroy->setGrayed(true); + if(pLockUnlock) pLockUnlock->setGrayed(true); if(pMoveSubMenu) pMoveSubMenu->setGrayed(true); if(pMoveToBag) pMoveToBag->setGrayed(true); for(i=0;isetGrayed(false); if(pDrop) pDrop->setGrayed(false); if(pDestroy) pDestroy->setGrayed(false); + if(pLockUnlock) pLockUnlock->setGrayed(false); if(pMoveSubMenu) pMoveSubMenu->setGrayed(false); // check each inventory dest if available @@ -1956,6 +2029,45 @@ class CHandlerItemMenuDeactivate : public IActionHandler }; REGISTER_ACTION_HANDLER( CHandlerItemMenuDeactivate, "item_menu_deactivate" ); + +// *************************************************************************** + +class CHandlerItemMenuBaseCheck : public IActionHandler +{ + void execute (CCtrlBase *pCaller, const std::string &/* sParams */) + { + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + + // Get the ctrl sheet that launched this menu + CDBCtrlSheet *pCS = dynamic_cast(pIM->getCtrlLaunchingModal()); + if (pCS == NULL) return; + INVENTORIES::TInventory invId= (INVENTORIES::TInventory)pCS->getInventoryIndex(); + + // Get the menu launched + CInterfaceGroup *pMenu= dynamic_cast(pCaller); + if(!pMenu) return; + + // Get all needed text entries + CViewTextMenu *pDestroy = dynamic_cast(pMenu->getView("destroy")); + CViewTextMenu *pLockUnlock = dynamic_cast(pMenu->getView("lockunlock")); + + if (pCS->getLockedByOwner()) + { + pLockUnlock->setHardText("uimUnlockItem"); + // Cannot destroy if locked by owner + if (pDestroy) pDestroy->setActive(false); + } + else + { + pLockUnlock->setHardText("uimLockItem"); + if (pDestroy) pDestroy->setActive(true); + } + + } +}; +REGISTER_ACTION_HANDLER( CHandlerItemMenuBaseCheck, "item_menu_base_check" ); + + // *************************************************************************** static void sendMsgUseItem(uint16 slot) { diff --git a/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp b/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp index 53a1258bc..097247f6f 100644 --- a/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp +++ b/code/ryzom/client/src/interface_v3/action_phrase_faber.cpp @@ -33,7 +33,7 @@ #include "group_editbox.h" #include "dbview_bar.h" #include "skill_manager.h" - +#include "game_share/bot_chat_types.h" using namespace std; using namespace NLMISC; @@ -69,7 +69,9 @@ const std::string FaberPhraseItemResultGroup= FaberPhraseWindow + ":header_opene // *************************************************************************** CActionPhraseFaber::CActionPhraseFaber() { - _InventoryMirror.resize(INVENTORIES::NUM_INVENTORY * MAX_PLAYER_INV_ENTRIES); + uint size = MAX_PLAYER_INV_ENTRIES + (MAX_ANIMALINV_ENTRIES * MAX_INVENTORY_ANIMAL) + + MAX_GUILDINV_ENTRIES + MAX_ROOMINV_ENTRIES; + _InventoryMirror.resize(size); _InventoryObsSetup= false; _ExecuteFromItemPlanBrick= NULL; } @@ -275,25 +277,64 @@ void CActionPhraseFaber::fillFaberPlanSelection(const std::string &brickDB, ui } // *************************************************************************** -CItemImage *CActionPhraseFaber::getInvMirrorItemImage(uint slotIndex) +CItemImage *CActionPhraseFaber::getInvMirrorItemImage(uint slotIndex, uint& invId, uint& indexInInv) { - uint invId= slotIndex/MAX_PLAYER_INV_ENTRIES; - uint indexInInv= slotIndex%MAX_PLAYER_INV_ENTRIES; + if (slotIndex < MAX_PLAYER_INV_ENTRIES) + { + invId = INVENTORIES::bag; + indexInInv = slotIndex; + return &getInventory().getBagItem(slotIndex); + } + slotIndex -= MAX_PLAYER_INV_ENTRIES; - // get the item image from bag, steed, pack animal... - if(invId==INVENTORIES::bag && indexInInv=INVENTORIES::pet_animal && invIdgetDbBranch(SERVER_INVENTORY ":GUILD:" + toString(slotIndex)); + static CItemImage image; + image.build(itemBranch); + invId = INVENTORIES::guild; + indexInInv = slotIndex; + return ℑ + } + return NULL; + } + slotIndex -= MAX_GUILDINV_ENTRIES; + + if (slotIndex < MAX_ROOMINV_ENTRIES) + { + if (getInventory().isInventoryAvailable(INVENTORIES::player_room)) + { + CInterfaceManager *im = CInterfaceManager::getInstance(); + CCDBNodeBranch *itemBranch = im->getDbBranch(SERVER_INVENTORY ":ROOM:" + toString(slotIndex)); + static CItemImage image; + image.build(itemBranch); + invId = INVENTORIES::player_room; + indexInInv = slotIndex; + return ℑ + } + return NULL; + } return NULL; } // *************************************************************************** -bool CActionPhraseFaber::isMpAvailable(CItemSheet *mpSheet, uint slotIndex) const +bool CActionPhraseFaber::isMpAvailable(CItemSheet *mpSheet, uint invId, uint slotIndex) const { - uint invId= slotIndex / MAX_PLAYER_INV_ENTRIES; return mpSheet && mpSheet->Family==ITEMFAMILY::RAW_MATERIAL && getInventory().isInventoryAvailable((INVENTORIES::TInventory)invId); } @@ -396,14 +437,14 @@ void CActionPhraseFaber::validateFaberPlanSelection(CSBrickSheet *itemPlanBrick _InventoryMirror[i].reset(); } + uint invId = 0; + uint indexInInv = 0; // Run all the inventories. for(i=0;i<_InventoryMirror.size();i++) { - uint invId= i/MAX_PLAYER_INV_ENTRIES; - uint indexInInv= i%MAX_PLAYER_INV_ENTRIES; - CItemImage *itemImage= getInvMirrorItemImage(i); - - // item found? + CItemImage *itemImage= getInvMirrorItemImage(i, invId, indexInInv); + bool bLockedByOwner = itemImage && itemImage->getLockedByOwner(); + // item found and not locked? if(itemImage) { // setup the origin @@ -413,7 +454,7 @@ void CActionPhraseFaber::validateFaberPlanSelection(CSBrickSheet *itemPlanBrick // The item must be a mp CSheetId sheetId= CSheetId(itemImage->getSheetID()); CItemSheet *mpSheet= dynamic_cast(SheetMngr.get(sheetId)); - if( isMpAvailable(mpSheet, i) ) + if( isMpAvailable(mpSheet, invId, i) && !bLockedByOwner) { _InventoryMirror[i].Sheet= sheetId; _InventoryMirror[i].Quality= itemImage->getQuality(); @@ -422,6 +463,7 @@ void CActionPhraseFaber::validateFaberPlanSelection(CSBrickSheet *itemPlanBrick _InventoryMirror[i].Weight= itemImage->getWeight(); // Bkup original quantity from inventory _InventoryMirror[i].OriginalQuantity= _InventoryMirror[i].Quantity; + _InventoryMirror[i].LockedByOwner= bLockedByOwner; } } } @@ -1311,17 +1353,22 @@ void CActionPhraseFaber::onInventoryChange() return; // Run all the Bag + uint invId = 0; + uint indexInInv = 0; for(i=0;i<_InventoryMirror.size();i++) { - CItemImage *itemImage= getInvMirrorItemImage(i); + CItemImage *itemImage= getInvMirrorItemImage(i, invId, indexInInv); + if(itemImage) { CSheetId sheetId= CSheetId(itemImage->getSheetID()); CItemSheet *mpSheet= dynamic_cast(SheetMngr.get(sheetId)); CItem newInvItem; - // The item must be a mp, and the item must be available - if( isMpAvailable(mpSheet, i) ) + bool bLockedByOwner = itemImage->getLockedByOwner(); + + // The item must be a mp, and the item must be available and unlocked + if( isMpAvailable(mpSheet, invId, i) && !bLockedByOwner) { newInvItem.Sheet= sheetId; newInvItem.Quality= itemImage->getQuality(); @@ -1329,12 +1376,13 @@ void CActionPhraseFaber::onInventoryChange() newInvItem.UserColor= itemImage->getUserColor(); newInvItem.Weight= itemImage->getWeight(); newInvItem.OriginalQuantity= newInvItem.Quantity; + newInvItem.LockedByOwner = bLockedByOwner; } - /* There is 4 cases: + /* There is 5 cases: - no changes => no op. - - new Mp on a empty or non Mp slot. Easy, just add. - - old Mp removed (not same sheetId/quality/userColor) + - new/unlocked Mp on a empty or non Mp slot. Easy, just add. + - old Mp removed (not same sheetId/quality/userColor/locked) - old Mp with quantity changed to be greater - old Mp with quantity changed to be smaller */ @@ -1348,8 +1396,8 @@ void CActionPhraseFaber::onInventoryChange() // If the item was not a mp if(_InventoryMirror[i].Sheet==CSheetId::Unknown) { - // if now it is, easy, just add - if(newInvItem.Sheet!=CSheetId::Unknown) + // if now it is, easy, just add if not locked + if(newInvItem.Sheet!=CSheetId::Unknown && !newInvItem.LockedByOwner) curInvItem= newInvItem; } // else must test change or remove @@ -1358,7 +1406,8 @@ void CActionPhraseFaber::onInventoryChange() bool sameMp; sameMp= curInvItem.Sheet == newInvItem.Sheet && curInvItem.Quality == newInvItem.Quality && - curInvItem.UserColor == newInvItem.UserColor ; + curInvItem.UserColor == newInvItem.UserColor && + curInvItem.LockedByOwner == newInvItem.LockedByOwner; // if the Mp was deleted from this slot, delete it from all faber execution if(!sameMp) diff --git a/code/ryzom/client/src/interface_v3/action_phrase_faber.h b/code/ryzom/client/src/interface_v3/action_phrase_faber.h index 8b792089b..517b25210 100644 --- a/code/ryzom/client/src/interface_v3/action_phrase_faber.h +++ b/code/ryzom/client/src/interface_v3/action_phrase_faber.h @@ -92,6 +92,7 @@ private: uint Selected; // This is the original quantity in inventory sint32 OriginalQuantity; + bool LockedByOwner; CItem() : Sheet(0) { @@ -233,8 +234,8 @@ private: void removeMpSlotThatUseInvSlot(uint invSlot, uint quantityToRemove); // from an index in _InventoryMirror, get the ItemImage - CItemImage *getInvMirrorItemImage(uint slotIndex); - bool isMpAvailable(CItemSheet *mpSheet, uint slotIndex) const; + CItemImage *getInvMirrorItemImage(uint slotIndex, uint& invId, uint& indexInInv); + bool isMpAvailable(CItemSheet *mpSheet, uint invId, uint slotIndex) const; void updateItemResult(); }; diff --git a/code/ryzom/client/src/interface_v3/bot_chat_page_trade.cpp b/code/ryzom/client/src/interface_v3/bot_chat_page_trade.cpp index dcebe792d..1cd5644d4 100644 --- a/code/ryzom/client/src/interface_v3/bot_chat_page_trade.cpp +++ b/code/ryzom/client/src/interface_v3/bot_chat_page_trade.cpp @@ -48,6 +48,7 @@ #include "../sheet_manager.h" #include "../user_entity.h" #include "view_bitmap.h" +#include "nel/misc/common.h" using namespace std::rel_ops; @@ -996,7 +997,7 @@ void CBotChatPageTrade::startSellDialog(CDBCtrlSheet *sheet, CCtrlBase * /* pCal CCtrlTextButton *confirmButton = dynamic_cast(ig->getCtrl("ok")); if (confirmButton) { - confirmButton->setActive( true ); + confirmButton->setActive( sheet->getLockedByOwner() ); confirmButton->setText(CI18N::get("uiSellImmediately")); confirmButton->setDefaultContextHelp(CI18N::get("uittDirectSellButton")); } @@ -1538,10 +1539,12 @@ void CBotChatPageTrade::setupResellGroup(bool sellMode, uint defaultQuantity, CI CViewText *vt= dynamic_cast(cantResellGroup->getView("reason")); if(vt) { - if(resaleFlag==BOTCHATTYPE::ResaleKOBroken) + if(resaleFlag == BOTCHATTYPE::ResaleKOBroken) vt->setHardText("uiCantResaleCauseDamaged"); - else + else if (resaleFlag == BOTCHATTYPE::ResaleKONoTimeLeft) vt->setHardText("uiCantResaleCauseTooLate"); + else + vt->setHardText("uiCantResaleLockedByOwner"); } } // else setup "can_resell:choose_resell group" @@ -2575,9 +2578,9 @@ static DECLARE_INTERFACE_USER_FCT(getPriceWithFame) if(value==-1) result.setUCString(CI18N::get("uiBadPrice")); else if(value==valueFame) - result.setUCString(toString(value)); + result.setUCString(NLMISC::formatThousands(toString(value))); else - result.setUCString(toString("%d (%d)", valueFame, value)); + result.setUCString(NLMISC::formatThousands(toString(valueFame)) + " (" + NLMISC::formatThousands(toString(value)) + ")"); return true; } @@ -2592,8 +2595,8 @@ static DECLARE_INTERFACE_USER_FCT(getBonusOnResale) sint valueHigh= (sint)args[0].getInteger(); sint valueLow= (sint)args[1].getInteger(); - sint diff = valueHigh - valueLow; - result.setUCString(toString("+%d", diff)); + sint diff = valueHigh - valueLow; + result.setUCString("+" + NLMISC::formatThousands(toString(diff))); return true; } diff --git a/code/ryzom/client/src/interface_v3/chat_filter.cpp b/code/ryzom/client/src/interface_v3/chat_filter.cpp index fc2e6c9c3..9609fc1af 100644 --- a/code/ryzom/client/src/interface_v3/chat_filter.cpp +++ b/code/ryzom/client/src/interface_v3/chat_filter.cpp @@ -378,7 +378,7 @@ void CChatTargetFilter::setTargetGroup(CChatGroup::TGroupType groupType, uint32 const bool guildActive = pIM->getDbProp("SERVER:GUILD:NAME")->getValueBool(); switch(groupType) { - case CChatGroup::dyn_chat: entry+="DYN"; break; + case CChatGroup::dyn_chat: entry+="DYN:" + NLMISC::toString(dynamicChannelDbIndex); break; case CChatGroup::say: entry+="SAY"; break; case CChatGroup::shout: entry+="SHOUT"; break; case CChatGroup::team: if(!teamActive) return; entry+="GROUP"; break; diff --git a/code/ryzom/client/src/interface_v3/chat_text_manager.cpp b/code/ryzom/client/src/interface_v3/chat_text_manager.cpp index 391898563..d1488b4dd 100644 --- a/code/ryzom/client/src/interface_v3/chat_text_manager.cpp +++ b/code/ryzom/client/src/interface_v3/chat_text_manager.cpp @@ -148,18 +148,43 @@ CViewBase *CChatTextManager::createMsgText(const ucstring &cstMsg, NLMISC::CRGBA vt->setMultiLineSpace(getTextMultiLineSpace()); vt->setModulateGlobalColor(false); + ucstring cur_time = ""; + static CCDBNodeLeaf* node = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false); + if (node) + { + if (node->getValueBool()) + { + cur_time = CInterfaceManager::getTimestampHuman(); + } + } + // if text contain any color code, set the text formated and white, // otherwise, set text normal and apply global color - if (msg.find(ucstring("@{")) != ucstring::npos) + size_t codePos = msg.find(ucstring("@{")); + if (codePos != ucstring::npos) { + // Prepend the current time (do it after the color if the color at first position. + if (codePos == 0) + { + codePos = msg.find(ucstring("}")); + msg = msg.substr(0, codePos + 1) + cur_time + msg.substr(codePos + 1, msg.length() - codePos); + } + else + { + msg = cur_time + msg; + } + + vt->setTextFormatTaged(msg); vt->setColor(NLMISC::CRGBA::White); } else { + msg = cur_time + msg; vt->setText(msg); vt->setColor(col); } + if (!commandGroup) { return vt; diff --git a/code/ryzom/client/src/interface_v3/chat_window.cpp b/code/ryzom/client/src/interface_v3/chat_window.cpp index 6f13b1bed..773992fc3 100644 --- a/code/ryzom/client/src/interface_v3/chat_window.cpp +++ b/code/ryzom/client/src/interface_v3/chat_window.cpp @@ -472,15 +472,9 @@ void CChatWindow::displayLocalPlayerTell(const ucstring &receiver, const ucstrin CInterfaceProperty prop; prop.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," "); encodeColorTag(prop.getRGBA(), finalMsg, false); - ucstring cur_time; - CCDBNodeLeaf *pNL = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false); - if (pNL && pNL->getValueBool()) - { - cur_time = CInterfaceManager::getTimestampHuman(); - } - ucstring csr; - if (CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw())) csr += ucstring("(CSR) "); - finalMsg += cur_time + csr + CI18N::get("youTell") + ": "; + + ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + finalMsg += csr + CI18N::get("youTell") + ": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); encodeColorTag(prop.getRGBA(), finalMsg, true); finalMsg += msg; @@ -1294,8 +1288,18 @@ public: CChatWindow::_ChatWindowLaunchingCommand = chat; string str = text.toUtf8(); string cmdWithArgs = str.substr(1); - /* In the chat context, only ' ' is a possible separator */ + + // Get the command name from the string, can contain spaces string cmd = cmdWithArgs.substr(0, cmdWithArgs.find(' ')); + if (cmdWithArgs.find('"') == 0) + { + string::size_type pos = cmdWithArgs.find('"', 1); + if (string::npos != pos) + { + cmd = cmdWithArgs.substr(1, pos - 1); + } + } + if ( NLMISC::ICommand::exists( cmd ) ) { NLMISC::ICommand::execute( cmdWithArgs, g_log ); @@ -1314,7 +1318,7 @@ public: } } // Clear input string - pEB->setInputString (string("")); + pEB->setInputString (ucstring("")); CGroupContainer *gc = pEB->getEnclosingContainer(); if (gc) { diff --git a/code/ryzom/client/src/interface_v3/ctrl_base_button.h b/code/ryzom/client/src/interface_v3/ctrl_base_button.h index 06a27b276..c33f0c63e 100644 --- a/code/ryzom/client/src/interface_v3/ctrl_base_button.h +++ b/code/ryzom/client/src/interface_v3/ctrl_base_button.h @@ -115,6 +115,7 @@ public: // @{ // Event part void setActionOnLeftClick (const std::string &actionHandlerName) { _AHOnLeftClickString = actionHandlerName; _AHOnLeftClick = getAH(actionHandlerName, _AHLeftClickParams); } + void setActionOnLeftClickParams(const std::string ¶ms) { _AHOnLeftClickStringParams = params; } void setActionOnRightClick (const std::string &actionHandlerName) { _AHOnRightClick = getAH(actionHandlerName, _AHRightClickParams); } void setActionOnClockTick (const std::string &ahName) { _AHOnClockTick = getAH(ahName, _AHClockTickParams); } void setParamsOnLeftClick (const std::string ¶msHandlerName) { _AHLeftClickParams = paramsHandlerName; } @@ -204,6 +205,7 @@ protected: IActionHandler *_AHOnOver; CStringShared _AHOverParams; std::string _AHOnLeftClickString; + std::string _AHOnLeftClickStringParams; IActionHandler *_AHOnLeftClick; CStringShared _AHLeftClickParams; IActionHandler *_AHOnLeftDblClick; diff --git a/code/ryzom/client/src/interface_v3/ctrl_button.cpp b/code/ryzom/client/src/interface_v3/ctrl_button.cpp index fd1ba049b..0f77fc6fe 100644 --- a/code/ryzom/client/src/interface_v3/ctrl_button.cpp +++ b/code/ryzom/client/src/interface_v3/ctrl_button.cpp @@ -338,7 +338,14 @@ bool CCtrlButton::getMouseOverShape(string &texName, uint8 &rot, CRGBA &col) { if (_AHOnLeftClickString == "browse") { - texName = "curs_pick.tga"; + if (!_AHOnLeftClickStringParams.empty()) + { + texName = "@curs_pick.tga@"+_AHOnLeftClickStringParams; + } + else + { + texName = "curs_pick.tga"; + } rot= 0; col = CRGBA::White; return true; diff --git a/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp b/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp index ce551a0b7..c7afcaf92 100644 --- a/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp +++ b/code/ryzom/client/src/interface_v3/dbctrl_sheet.cpp @@ -45,6 +45,7 @@ #include "../client_sheets/sphrase_sheet.h" #include "game_share/xml_auto_ptr.h" #include "lua_ihm.h" +#include "game_share/bot_chat_types.h" #include "../r2/editor.h" @@ -123,6 +124,14 @@ ucstring CControlSheetTooltipInfoWaiter::infoValidated(CDBCtrlSheet* ctrlSheet, return help; } + +// *************************************************************************** +int CDBCtrlSheet::luaGetDraggedSheet(CLuaState &ls) +{ + CLuaIHM::pushUIOnStack(ls, dynamic_cast(_LastDraggedSheet)); + return 1; +} + // *************************************************************************** int CDBCtrlSheet::luaGetHpBuff(CLuaState &ls) { @@ -179,6 +188,62 @@ int CDBCtrlSheet::luaGetName(CLuaState &ls) return 1; } +// ********************************************************************************************************** +class LuaInfoWaiter : public IItemInfoWaiter +{ +public: + volatile bool done; + +public: + virtual void infoReceived(); +}; + + +void LuaInfoWaiter::infoReceived() +{ + getInventory().removeItemInfoWaiter(this); + this->done = true; +} + +static LuaInfoWaiter luaInfoWaiter; + +// *************************************************************************** +int CDBCtrlSheet::luaGetCreatorName(CLuaState &ls) +{ + uint32 itemSlotId = getInventory().getItemSlotId(this); + CClientItemInfo itemInfo = getInventory().getItemInfo(itemSlotId); + ucstring creatorName; + STRING_MANAGER::CStringManagerClient::instance()->getString(itemInfo.CreatorName, creatorName); + CLuaIHM::push(ls, creatorName); + + return 1; +} + +// *************************************************************************** +int CDBCtrlSheet::luaWaitInfo(CLuaState &ls) +{ + static bool sent = false; + CDBCtrlSheet *ctrlSheet = const_cast(this); + uint32 itemSlotId= getInventory().getItemSlotId(ctrlSheet); + CClientItemInfo itemInfo = getInventory().getItemInfo(itemSlotId); + + if (sent || itemInfo.versionInfo != 0) + { + ls.push((bool)(luaInfoWaiter.done)); + if (luaInfoWaiter.done) + sent = false; + return luaInfoWaiter.done ? 1 : 0; + } + else + { + luaInfoWaiter.ItemSlotId = itemSlotId; + luaInfoWaiter.ItemSheet = this->getSheetId(); + luaInfoWaiter.done = false; + getInventory().addItemInfoWaiter(&luaInfoWaiter); + sent = true; + } + return 0; +} // *************************************************************************** int CDBCtrlSheet::luaBuildCrystallizedSpellListBrick(CLuaState &ls) @@ -1797,8 +1862,16 @@ void CDBCtrlSheet::draw() string params = string("src=") + pCSSrc->getId(); if (!_AHCanDropParams.empty()) { - string sTmp = _AHCanDropParams; - params = sTmp + "|" + params; + if (getAHName(_AHOnCanDrop) == "lua") + { + params = _AHCanDropParams; + strFindReplace(params, "%src", pCSSrc->getId()); + } + else + { + string sTmp = _AHCanDropParams; + params = sTmp + "|" + params; + } } pIM->runActionHandler (_AHOnCanDrop, this, params); } @@ -2138,6 +2211,12 @@ void CDBCtrlSheet::drawSheet (sint32 x, sint32 y, bool draging, bool showSelecti // if a raw material for example, must add special icon text. displayCharBitmaps(_RenderLayer+2, x, y, curSheetColor); + + // Add the lock overlay if needed + if (getLockedByOwner()) + { + rVR.draw11RotFlipBitmap (_RenderLayer+1, x - 2, y + 8, 0, false, rVR.getSystemTextureId(CViewRenderer::ItemLockedByOwnerTexture), curSheetColor); + } } break; // Action @@ -2600,8 +2679,16 @@ bool CDBCtrlSheet::handleEvent (const CEventDescriptor &event) string params = string("src=") + _Id; if (!pCSdest->_AHCanDropParams.empty()) { - string sTmp = pCSdest->_AHCanDropParams; - params = sTmp + "|" + params; + if (getAHName(pCSdest->_AHOnCanDrop) == "lua") + { + params = pCSdest->_AHCanDropParams; + strFindReplace(params, "%src", _Id); + } + else + { + string sTmp = pCSdest->_AHCanDropParams; + params = sTmp + "|" + params; + } } pIM->runActionHandler (pCSdest->_AHOnCanDrop, pCSdest, params); @@ -2612,8 +2699,16 @@ bool CDBCtrlSheet::handleEvent (const CEventDescriptor &event) string params = string("src=") + _Id; if (!pCSdest->_AHDropParams.empty()) { - string sTmp = pCSdest->_AHDropParams; - params = sTmp + "|" + params; // must copy 'drop' params at start because it could be the name of a procedure + if (getAHName(pCSdest->_AHOnDrop) == "lua") + { + params = pCSdest->_AHDropParams; + strFindReplace(params, "%src", _Id); + } + else + { + string sTmp = pCSdest->_AHDropParams; + params = sTmp + "|" + params;// must copy 'drop' params at start because it could be the name of a procedure + } } // call action pIM->runActionHandler (pCSdest->_AHOnDrop, pCSdest, params); @@ -2649,8 +2744,16 @@ bool CDBCtrlSheet::handleEvent (const CEventDescriptor &event) string params = string("src=") + _Id; if (!pList->getCtrlSheetInfo()._AHCanDropParams.empty()) { - string sTmp = pList->getCtrlSheetInfo()._AHCanDropParams; - params = sTmp + "|" + params; + if (getAHName(pList->getCtrlSheetInfo()._AHOnCanDrop) == "lua") + { + params = pList->getCtrlSheetInfo()._AHCanDropParams; + strFindReplace(params, "%src", _Id); + } + else + { + string sTmp = pList->getCtrlSheetInfo()._AHCanDropParams; + params = sTmp + "|" + params; + } } pIM->runActionHandler (pList->getCtrlSheetInfo()._AHOnCanDrop, pList, params); @@ -2661,8 +2764,16 @@ bool CDBCtrlSheet::handleEvent (const CEventDescriptor &event) string params = string("src=") + _Id; if (!pList->getCtrlSheetInfo()._AHDropParams.empty()) { - string sTmp = pList->getCtrlSheetInfo()._AHDropParams; - params = sTmp + "|" + params; // must copy 'drop' params at start because it could be the name of a procedure + if (getAHName(pList->getCtrlSheetInfo()._AHOnDrop) == "lua") + { + params = pList->getCtrlSheetInfo()._AHDropParams; + strFindReplace(params, "%src", _Id); + } + else + { + string sTmp = pList->getCtrlSheetInfo()._AHDropParams; + params = sTmp + "|" + params; // must copy 'drop' params at start because it could be the name of a procedure + } } // call action pIM->runActionHandler (pList->getCtrlSheetInfo()._AHOnDrop, pList, params); @@ -2975,9 +3086,32 @@ void CDBCtrlSheet::getContextHelp(ucstring &help) const } else if(getType() == CCtrlSheetInfo::SheetType_Item) { - const CItemSheet *item = asItemSheet(); - if (item) - help = getItemActualName(); + const CItemSheet *item= asItemSheet(); + if(item) + { + if (item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL || item->Family == ITEMFAMILY::JEWELRY || item->Family == ITEMFAMILY::ARMOR) + { + string luaMethodName = ( (item->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) ? "updateCrystallizedSpellTooltip" : "updateBuffItemTooltip"); + CDBCtrlSheet *ctrlSheet = const_cast(this); + if ( ! getInventory().isItemInfoUpToDate(getInventory().getItemSlotId(ctrlSheet))) + { + // Prepare the waiter + ControlSheetTooltipUpdater.ItemSheet= ctrlSheet->getSheetId(); + ControlSheetTooltipUpdater.LuaMethodName = luaMethodName; + ControlSheetTooltipUpdater.ItemSlotId= getInventory().getItemSlotId(ctrlSheet); + ControlSheetTooltipUpdater.CtrlSheet = ctrlSheet; + + // Add the waiter + getInventory().addItemInfoWaiter(&ControlSheetTooltipUpdater); + } + + help = ControlSheetTooltipUpdater.infoValidated(ctrlSheet, luaMethodName); + + } + else + help= getItemActualName(); + + } else help= _ContextHelp; } @@ -3936,6 +4070,17 @@ void CDBCtrlSheet::setItemResaleFlag(sint32 rf) node->setValue32(rf); } +// *************************************************************************** +bool CDBCtrlSheet::getLockedByOwner() const +{ + return (getItemResaleFlag() == BOTCHATTYPE::ResaleKOLockedByOwner); +} + +// *************************************************************************** +bool CDBCtrlSheet::canOwnerLock() const +{ + return (NULL != getItemResaleFlagPtr()); +} // *************************************************************************** sint32 CDBCtrlSheet::getItemSellerType() const diff --git a/code/ryzom/client/src/interface_v3/dbctrl_sheet.h b/code/ryzom/client/src/interface_v3/dbctrl_sheet.h index 2cf34e722..6bd7706fb 100644 --- a/code/ryzom/client/src/interface_v3/dbctrl_sheet.h +++ b/code/ryzom/client/src/interface_v3/dbctrl_sheet.h @@ -194,16 +194,20 @@ public: void setActionOnLeftClick (const std::string &ActionHandlerName) { _AHOnLeftClick = getAH(ActionHandlerName, _AHLeftClickParams); } void setActionOnRightClick (const std::string &ActionHandlerName) { _AHOnRightClick = getAH(ActionHandlerName, _AHRightClickParams); } void setActionOnDrop (const std::string &ActionHandlerName) { _AHOnDrop = getAH(ActionHandlerName, _AHDropParams); } + void setActionOnCanDrop (const std::string &ActionHandlerName) { _AHOnCanDrop = getAH(ActionHandlerName, _AHCanDropParams); } void setParamsOnLeftClick (const std::string &ParamsHandlerName) { _AHLeftClickParams = ParamsHandlerName; } void setParamsOnRightClick (const std::string &ParamsHandlerName) { _AHRightClickParams = ParamsHandlerName; } void setParamsOnDrop (const std::string &ParamsHandlerName) { _AHDropParams = ParamsHandlerName; } + void setParamsOnCanDrop (const std::string &ParamsHandlerName) { _AHCanDropParams = ParamsHandlerName; } const std::string &getActionOnLeftClick () const { return getAHName(_AHOnLeftClick); } const std::string &getActionOnRightClick () const { return getAHName(_AHOnRightClick); } const std::string &getActionOnDrop () const { return getAHName(_AHOnDrop); } + const std::string &getActionOnCanDrop () const { return getAHName(_AHOnCanDrop); } const std::string &getParamsOnLeftClick () const { return _AHLeftClickParams; } const std::string &getParamsOnRightClick () const { return _AHRightClickParams; } const std::string &getParamsOnDrop () const { return _AHDropParams; } + const std::string &getParamsOnCanDrop () const { return _AHCanDropParams; } void setListMenuLeft (const std::string &cm) { CCtrlSheetInfo::setListMenuLeft(cm); } void setListMenuRight (const std::string &cm) { CCtrlSheetInfo::setListMenuRight(cm); } @@ -215,6 +219,7 @@ public: void setCanDrop (bool cd) { _CanDrop = cd; } bool getCanDrop () const { return _CanDrop; } bool isDragable() { return _Dragable; } + void setDragable(bool dragable) { _Dragable = dragable; } bool isDraging() { return _Draging; } sint32 getDeltaDragX() {return _DeltaDragX;} sint32 getDeltaDragY() {return _DeltaDragY;} @@ -265,19 +270,35 @@ public: REFLECT_SINT32("back", getGuildBack, setGuildBack); REFLECT_SINT32("symbol", getGuildSymbol, setGuildSymbol); REFLECT_BOOL("invert_symbol", getInvertGuildSymbol, setInvertGuildSymbol); + REFLECT_BOOL("dragable", isDragable, setDragable); + REFLECT_BOOL("can_drop", getCanDrop, setCanDrop); + REFLECT_STRING ("left_click", getActionOnLeftClick, setActionOnLeftClick); + REFLECT_STRING ("right_click", getActionOnRightClick, setActionOnRightClick); + REFLECT_STRING ("left_click_params", getParamsOnLeftClick, setParamsOnLeftClick); + REFLECT_STRING ("right_click_params", getParamsOnRightClick, setParamsOnRightClick); + REFLECT_STRING ("on_drop", getActionOnDrop, setActionOnDrop); + REFLECT_STRING ("on_drop_params", getParamsOnDrop, setParamsOnDrop); + REFLECT_STRING ("on_can_drop", getActionOnCanDrop, setActionOnCanDrop); + REFLECT_STRING ("on_can_drop_params", getParamsOnCanDrop, setParamsOnCanDrop); + REFLECT_LUA_METHOD("getDraggedSheet", luaGetDraggedSheet) REFLECT_LUA_METHOD("getHpBuff", luaGetHpBuff) REFLECT_LUA_METHOD("getSapBuff", luaGetSapBuff) REFLECT_LUA_METHOD("getFocusBuff", luaGetFocusBuff) REFLECT_LUA_METHOD("getStaBuff", luaGetStaBuff) REFLECT_LUA_METHOD("getName", luaGetName) + REFLECT_LUA_METHOD("getCreatorName", luaGetCreatorName) + REFLECT_LUA_METHOD("waitInfo", luaWaitInfo) REFLECT_LUA_METHOD("buildCrystallizedSpellListBrick", luaBuildCrystallizedSpellListBrick) REFLECT_EXPORT_END + int luaGetDraggedSheet(CLuaState &ls); int luaGetHpBuff(CLuaState &ls); int luaGetSapBuff(CLuaState &ls); int luaGetFocusBuff(CLuaState &ls); int luaGetStaBuff(CLuaState &ls); int luaGetName(CLuaState &ls); + int luaGetCreatorName(CLuaState &ls); + int luaWaitInfo(CLuaState &ls); int luaBuildCrystallizedSpellListBrick(CLuaState &ls); // hardcode creation. User must setup other CtrlBase value (parent etc...) @@ -355,9 +376,9 @@ public: /// Special ContextHelp for ctrl sheet. virtual void getContextHelp(ucstring &help) const; - /// Special ContextHelp for ctrl sheet. virtual void getContextHelpToolTip(ucstring &help) const; + /** true if an item of another ctrlSheet can be dropped on this slot. * also return true if src is 0, or if _ItemSlot==UNDEFINED */ @@ -494,6 +515,12 @@ public: // set item RESALE_FLAG void setItemResaleFlag(sint32 rf); + // get item locked by owner + bool getLockedByOwner() const; + + // true if the inventory supports owner locking + bool canOwnerLock() const; + // get item SELLER_TYPE. 0 if no DB sint32 getItemSellerType() const; CCDBNodeLeaf *getItemSellerTypePtr() const; diff --git a/code/ryzom/client/src/interface_v3/dbgroup_list_sheet_trade.cpp b/code/ryzom/client/src/interface_v3/dbgroup_list_sheet_trade.cpp index 7511ee8ef..bd30d61f6 100644 --- a/code/ryzom/client/src/interface_v3/dbgroup_list_sheet_trade.cpp +++ b/code/ryzom/client/src/interface_v3/dbgroup_list_sheet_trade.cpp @@ -30,6 +30,7 @@ #include "game_share/pvp_clan.h" #include "../string_manager_client.h" #include "../entity_cl.h" +#include "nel/misc/common.h" using namespace NLMISC; @@ -264,7 +265,7 @@ void CDBGroupListSheetTrade::CSheetChildTrade::updateViewText(CDBGroupListSheetT { STRING_MANAGER::CStringManagerClient *pSMC = STRING_MANAGER::CStringManagerClient::instance(); text += string("\n") + pSMC->getOutpostBuildingLocalizedDescription(CSheetId(Ctrl->getSheetId())); - text += "\n" + CI18N::get("uiBotChatPrice") + toString(pOBS->CostDapper); + text += "\n" + CI18N::get("uiBotChatPrice") + NLMISC::formatThousands(toString(pOBS->CostDapper)); text += CI18N::get("uiBotChatTime") + toString(pOBS->CostTime/60) + CI18N::get("uiBotChatTimeMinute"); if ((pOBS->CostTime % 60) != 0) text += toString(pOBS->CostTime%60) + CI18N::get("uiBotChatTimeSecond"); @@ -341,7 +342,7 @@ void CDBGroupListSheetTrade::CSheetChildTrade::updateViewText(CDBGroupListSheetT if (pIS && pIS->Family == ITEMFAMILY::GUILD_OPTION) { text+= "\n" + CI18N::get("uiBotChatSkillPointCost") + toString(pIS->GuildOption.XPCost); - text+= "\n" + CI18N::get("uiBotChatPrice") + toString(pIS->GuildOption.MoneyCost); + text+= "\n" + CI18N::get("uiBotChatPrice") + NLMISC::formatThousands(toString(pIS->GuildOption.MoneyCost)); guildOption= true; } } @@ -376,10 +377,10 @@ void CDBGroupListSheetTrade::CSheetChildTrade::updateViewText(CDBGroupListSheetT if (LastPrice > 0) { if(displayMulPrice) - text+= "\n" + CI18N::get("uiBotChatPrice") + toString(sint32(LastPrice * priceFactor)) + " (" - + toString( sint32(factor) * sint32(LastPrice * priceFactor) ) + ")"; + text+= "\n" + CI18N::get("uiBotChatPrice") + NLMISC::formatThousands(toString(sint32(LastPrice * priceFactor))) + " (" + + NLMISC::formatThousands(toString( sint32(factor) * sint32(LastPrice * priceFactor) )) + ")"; else - text+= "\n" + CI18N::get("uiBotChatPrice") + toString( sint32(factor * LastPrice * priceFactor) ); + text+= "\n" + CI18N::get("uiBotChatPrice") + NLMISC::formatThousands(toString( sint32(factor * LastPrice * priceFactor) )); } if ((LastFactionPointPrice != 0) && (LastFactionType >= PVP_CLAN::BeginClans) && (LastFactionType <= PVP_CLAN::EndClans)) @@ -390,7 +391,7 @@ void CDBGroupListSheetTrade::CSheetChildTrade::updateViewText(CDBGroupListSheetT text+= "\n"; text+= CI18N::get("uiBotChatFactionType") + PVP_CLAN::toString((PVP_CLAN::TPVPClan)LastFactionType) - + CI18N::get("uiBotChatFactionPointPrice") + toString(LastFactionPointPrice); + + CI18N::get("uiBotChatFactionPointPrice") + NLMISC::formatThousands(toString(LastFactionPointPrice)); } // some additional info for resale @@ -402,10 +403,10 @@ void CDBGroupListSheetTrade::CSheetChildTrade::updateViewText(CDBGroupListSheetT { // append price if(pIS && pIS->Stackable>1 && zeFather->getMultiplyPriceByQuantityFlag()) - text+= CI18N::get("uiBotChatRetirePrice") + toString(LastPriceRetire) + " (" - + toString(factor * LastPriceRetire) + ")"; + text+= CI18N::get("uiBotChatRetirePrice") + NLMISC::formatThousands(toString(LastPriceRetire)) + " (" + + NLMISC::formatThousands(toString(factor * LastPriceRetire)) + ")"; else - text+= CI18N::get("uiBotChatRetirePrice") + toString(factor * LastPriceRetire); + text+= CI18N::get("uiBotChatRetirePrice") + NLMISC::formatThousands(toString(factor * LastPriceRetire)); // set resale time left ucstring fmt= CI18N::get("uiBotChatResaleTimeLeft"); strFindReplace(fmt, "%d", toString(LastResaleTimeLeft/RYZOM_DAY_IN_HOUR)); @@ -482,6 +483,12 @@ bool CDBGroupListSheetTrade::CSheetChildTrade::isSheetValid(CDBGroupListSheetTex return false; } + // Locked by owner; cannot trade + if (Ctrl->getLockedByOwner()) + { + return false; + } + // Check seller type? TSellerTypeFilter stf= father->getSellerTypeFilter(); if( stf != None) diff --git a/code/ryzom/client/src/interface_v3/dbview_number.cpp b/code/ryzom/client/src/interface_v3/dbview_number.cpp index 8ca6e7c94..5ac955659 100644 --- a/code/ryzom/client/src/interface_v3/dbview_number.cpp +++ b/code/ryzom/client/src/interface_v3/dbview_number.cpp @@ -21,6 +21,7 @@ #include "dbview_number.h" #include "interface_manager.h" #include "game_share/xml_auto_ptr.h" +#include "nel/misc/common.h" using namespace std; using namespace NL3D; @@ -102,30 +103,6 @@ bool CDBViewNumber::parse (xmlNodePtr cur, CInterfaceGroup * parentGroup) return true; } -// *************************************************************************** -// Helper function -ucstring formatThousands(const ucstring& s, const ucstring& separator) -{ - int j; - int k; - int topI = s.length() - 1; - - if (topI < 4) return s; - - ucstring ns; - do - { - for (j = topI, k = 0; j >= 0 && k < 3; --j, ++k ) - { - ns = s[j] + ns; // new char is added to front of ns - if( j > 0 && k == 2) ns = separator + ns; // j > 0 means still more digits - } - topI -= 3; - - } while(topI >= 0); - return ns; -} - // *************************************************************************** void CDBViewNumber::checkCoords() { @@ -134,8 +111,7 @@ void CDBViewNumber::checkCoords() if (_Cache != val) { _Cache= val; - static ucstring separator = NLMISC::CI18N::get("uiThousandsSeparator"); - ucstring value = _Format ? formatThousands(toString(val), separator) : toString(val); + ucstring value = _Format ? NLMISC::formatThousands(toString(val)) : toString(val); if (_Positive) setText(val >= 0 ? ( ucstring(_Prefix) + value + ucstring(_Suffix) ) : ucstring("?")); else setText( ucstring(_Prefix) + value + ucstring(_Suffix) ); } diff --git a/code/ryzom/client/src/interface_v3/group_html.cpp b/code/ryzom/client/src/interface_v3/group_html.cpp index 8ce18fb77..388064131 100644 --- a/code/ryzom/client/src/interface_v3/group_html.cpp +++ b/code/ryzom/client/src/interface_v3/group_html.cpp @@ -34,6 +34,7 @@ extern "C" #include "view_link.h" #include "ctrl_scroll.h" #include "ctrl_button.h" +#include "dbctrl_sheet.h" #include "ctrl_text_button.h" #include "action_handler.h" #include "group_paragraph.h" @@ -63,13 +64,55 @@ using namespace NLMISC; CGroupHTML *CGroupHTML::_ConnectingLock = NULL; extern CActionsContext ActionsContext; -// Check if domain is on TrustedDomain -bool CGroupHTML::isTrustedDomain(const string &domain) { +// Check if domain is on TrustedDomain +bool CGroupHTML::isTrustedDomain(const string &domain) +{ vector::iterator it; it = find (ClientCfg.WebIgTrustedDomains.begin(), ClientCfg.WebIgTrustedDomains.end(), domain); return it != ClientCfg.WebIgTrustedDomains.end(); } +void CGroupHTML::setImage(CViewBase * view, const string &file) +{ + CCtrlButton *btn = dynamic_cast(view); + if(btn) + { + btn->setTexture (file); + btn->setTexturePushed(file); + btn->invalidateCoords(); + btn->invalidateContent(); + btn->resetInvalidCoords(); + btn->updateCoords(); + paragraphChange(); + } + else + { + CViewBitmap *btm = dynamic_cast(view); + if(btm) + { + btm->setTexture (file); + btm->invalidateCoords(); + btm->invalidateContent(); + btm->resetInvalidCoords(); + btm->updateCoords(); + paragraphChange(); + } + else + { + CGroupCell *btgc = dynamic_cast(view); + if(btgc) + { + btgc->setTexture (file); + btgc->invalidateCoords(); + btgc->invalidateContent(); + btgc->resetInvalidCoords(); + btgc->updateCoords(); + paragraphChange(); + } + } + } +} + // Get an url and return the local filename with the path where the url image should be string CGroupHTML::localImageName(const string &url) { @@ -99,30 +142,38 @@ void CGroupHTML::addImageDownload(const string &url, CViewBase *img) curl_easy_setopt(curl, CURLOPT_NOPROGRESS, true); curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); - string dest = localImageName(url)+".tmp"; + string dest = localImageName(url); + string tmpdest = localImageName(url)+".tmp"; #ifdef LOG_DL nlwarning("add to download '%s' dest '%s' img %p", url.c_str(), dest.c_str(), img); #endif - // create the local file - if (NLMISC::CFile::fileExists(dest)) - { - CFile::setRWAccess(dest); - NLMISC::CFile::deleteFile(dest); - } - FILE *fp = fopen (dest.c_str(), "wb"); - if (fp == NULL) - { - nlwarning("Can't open file '%s' for writing: code=%d '%s'", dest.c_str (), errno, strerror(errno)); - return; - } - curl_easy_setopt(curl, CURLOPT_FILE, fp); - curl_multi_add_handle(MultiCurl, curl); - Curls.push_back(CDataDownload(curl, url, fp, ImgType, img, "", "")); -#ifdef LOG_DL - nlwarning("adding handle %x, %d curls", curl, Curls.size()); -#endif - RunningCurls++; + // erase the tmp file if exists + if (NLMISC::CFile::fileExists(tmpdest)) + NLMISC::CFile::deleteFile(tmpdest); + + if (!NLMISC::CFile::fileExists(dest)) + { + + FILE *fp = fopen (tmpdest.c_str(), "wb"); + if (fp == NULL) + { + nlwarning("Can't open file '%s' for writing: code=%d '%s'", tmpdest.c_str (), errno, strerror(errno)); + return; + } + curl_easy_setopt(curl, CURLOPT_FILE, fp); + + curl_multi_add_handle(MultiCurl, curl); + Curls.push_back(CDataDownload(curl, url, fp, ImgType, img, "", "")); + #ifdef LOG_DL + nlwarning("adding handle %x, %d curls", curl, Curls.size()); + #endif + RunningCurls++; + } + else + { + setImage(img, dest); + } } void CGroupHTML::initImageDownload() @@ -297,41 +348,11 @@ void CGroupHTML::checkDownloads() for(uint i = 0; i < it->imgs.size(); i++) { // don't display image that are not power of 2 - uint32 w, h; - CBitmap::loadSize (file, w, h); - if (w == 0 || h == 0 || ((!NLMISC::isPowerOf2(w) || !NLMISC::isPowerOf2(h)) && !NL3D::CTextureFile::supportNonPowerOfTwoTextures())) - file.clear(); - - CCtrlButton *btn = dynamic_cast(it->imgs[i]); - if(btn) - { - #ifdef LOG_DL - nlwarning("refresh new downloading image %d button %p", i, it->imgs[i]); - #endif - btn->setTexture (file); - btn->setTexturePushed(file); - btn->invalidateCoords(); - btn->invalidateContent(); - btn->resetInvalidCoords(); - btn->updateCoords(); - paragraphChange(); - } - else - { - CViewBitmap *btm = dynamic_cast(it->imgs[i]); - if(btm) - { - #ifdef LOG_DL - nlwarning("refresh new downloading image %d image %p", i, it->imgs[i]); - #endif - btm->setTexture (file); - btm->invalidateCoords(); - btm->invalidateContent(); - btm->resetInvalidCoords(); - btm->updateCoords(); - paragraphChange(); - } - } + //uint32 w, h; + //CBitmap::loadSize (file, w, h); + //if (w == 0 || h == 0 || ((!NLMISC::isPowerOf2(w) || !NLMISC::isPowerOf2(h)) && !NL3D::CTextureFile::supportNonPowerOfTwoTextures())) + // file.clear(); + setImage(it->imgs[i], file); } } } @@ -446,6 +467,28 @@ void CGroupHTML::beginBuild () } +TStyle CGroupHTML::parseStyle (const string &str_styles) +{ + TStyle styles; + vector elements; + NLMISC::splitString(str_styles, ";", elements); + + for(uint i = 0; i < elements.size(); ++i) + { + vector style; + NLMISC::splitString(elements[i], ":", style); + if (style.size() >= 2) + { + string fullstyle = style[1]; + for (uint j=2; j < style.size(); j++) + fullstyle += ":"+style[j]; + styles[trim(style[0])] = fullstyle; + } + } + + return styles; +} + // *************************************************************************** void CGroupHTML::addText (const char * buf, int len) @@ -556,9 +599,31 @@ void CGroupHTML::addLink (uint element_number, uint /* attribute_number */, HTCh _Link.push_back(""); } } + + for(uint8 i = MY_HTML_A_ACCESSKEY; i < MY_HTML_A_Z_ACTION_SHORTCUT; i++) + { + if (present[i] && value[i]) + { + string title = value[i]; + // nlinfo("key %d = %s", i, title.c_str()); + } + } + //nlinfo("key of TITLE is : %d", MY_HTML_A_Z_ACTION_PARAMS); + if (present[MY_HTML_A_Z_ACTION_PARAMS] && value[MY_HTML_A_Z_ACTION_PARAMS]) + { + string title = value[MY_HTML_A_Z_ACTION_PARAMS]; + _LinkTitle.push_back(title); + } + else + _LinkTitle.push_back(""); } else + { _Link.push_back(""); + _LinkTitle.push_back(""); + } + + } } } @@ -626,7 +691,6 @@ static const char *scanColorComponent(const char *src, uint8 &intensity) return src; } - class CNameToCol { public: @@ -831,6 +895,11 @@ void CGroupHTML::beginElement (uint element_number, const BOOL *present, const c _GlobalColor.push_back(LinkColorGlobalColor); _A.push_back(true); + if (present[MY_HTML_A_TITLE] && value[MY_HTML_A_TITLE]) + _LinkTitle.push_back(value[MY_HTML_A_TITLE]); + if (present[MY_HTML_A_CLASS] && value[MY_HTML_A_CLASS]) + _LinkClass.push_back(value[MY_HTML_A_CLASS]); + // Quick help if (_TrustedDomain && present[MY_HTML_A_Z_ACTION_SHORTCUT] && value[MY_HTML_A_Z_ACTION_SHORTCUT]) { @@ -856,15 +925,87 @@ void CGroupHTML::beginElement (uint element_number, const BOOL *present, const c } } } + break; case HTML_DIV: { if (present[MY_HTML_DIV_NAME] && value[MY_HTML_DIV_NAME]) - { _DivName = value[MY_HTML_DIV_NAME]; + + string instClass; + if (present[MY_HTML_DIV_CLASS] && value[MY_HTML_DIV_CLASS]) + instClass = value[MY_HTML_DIV_CLASS]; + + // use generic template system + if (_TrustedDomain && !instClass.empty() && instClass == "ryzom-ui-grouptemplate") + { + string id; + if (present[MY_HTML_DIV_ID] && value[MY_HTML_DIV_ID]) + id = value[MY_HTML_DIV_ID]; + + string style; + if (present[MY_HTML_DIV_STYLE] && value[MY_HTML_DIV_STYLE]) + style = value[MY_HTML_DIV_STYLE]; + + typedef pair TTmplParam; + vector tmplParams; + + string templateName; + if (!style.empty()) + { + TStyle styles = parseStyle(style); + TStyle::iterator it; + for (it=styles.begin(); it != styles.end(); it++) + { + if ((*it).first == "template") + templateName = (*it).second; + else + tmplParams.push_back(TTmplParam((*it).first, (*it).second)); + } + } + + if (!templateName.empty()) + { + string parentId; + bool haveParentDiv = getDiv() != NULL; + if (haveParentDiv) + parentId = getDiv()->getId(); + else + parentId = _Paragraph->getId(); + + CInterfaceManager *im = CInterfaceManager::getInstance(); + CInterfaceGroup *inst = im->createGroupInstance(templateName, parentId+":"+id, tmplParams); + if (inst) + { + inst->setId(parentId+":"+id); + inst->updateCoords(); + if (haveParentDiv) + { + inst->setParent(getDiv()); + inst->setParentSize(getDiv()); + inst->setParentPos(getDiv()); + inst->setPosRef(Hotspot_TL); + inst->setParentPosRef(Hotspot_TL); + getDiv()->addGroup(inst); + } + else + { + if (!_Paragraph) + { + newParagraph (0); + paragraphChange (); + } + + getParagraph()->addChild(inst); + paragraphChange(); + } + _Divs.push_back(inst); + } + } } } break; + case HTML_FONT: { bool found = false; @@ -898,11 +1039,42 @@ void CGroupHTML::beginElement (uint element_number, const BOOL *present, const c addString(ucstring ("\n")); break; case HTML_BODY: - if (present[HTML_BODY_BGCOLOR] && value[HTML_BODY_BGCOLOR]) { - // Get the color - CRGBA bgColor = getColor (value[HTML_BODY_BGCOLOR]); - setBackgroundColor (bgColor); + if (present[HTML_BODY_BGCOLOR] && value[HTML_BODY_BGCOLOR]) + { + CRGBA bgColor = getColor (value[HTML_BODY_BGCOLOR]); + setBackgroundColor (bgColor); + } + + string style; + if (present[HTML_BODY_STYLE] && value[HTML_BODY_STYLE]) + style = value[HTML_BODY_STYLE]; + + + if (!style.empty()) + { + TStyle styles = parseStyle(style); + TStyle::iterator it; + + it = styles.find("background-repeat"); + bool repeat = (it != styles.end() && it->second == "1"); + + // Webig only + it = styles.find("background-scale"); + bool scale = (it != styles.end() && it->second == "1"); + + it = styles.find("background-image"); + if (it != styles.end()) + { + string image = it->second; + string::size_type texExt = strlwr(image).find("url("); + // Url image + if (texExt != string::npos) + // Remove url() + image = image.substr(4, image.size()-5); + setBackground (image, scale, repeat); + } + } } break; case HTML_FORM: @@ -1000,7 +1172,24 @@ void CGroupHTML::beginElement (uint element_number, const BOOL *present, const c } else { - addImage (value[MY_HTML_IMG_SRC], globalColor); + // Get the option to reload (class==reload) + bool reloadImg = false; + + string style; + if (present[MY_HTML_IMG_STYLE] && value[MY_HTML_IMG_STYLE]) + style = value[MY_HTML_IMG_STYLE]; + + if (!style.empty()) + { + TStyle styles = parseStyle(style); + TStyle::iterator it; + + it = styles.find("reload"); + if (it != styles.end() && (*it).second == "1") + reloadImg = true; + } + + addImage (value[MY_HTML_IMG_SRC], globalColor, reloadImg); } } } @@ -1312,8 +1501,43 @@ void CGroupHTML::beginElement (uint element_number, const BOOL *present, const c if (!_Cells.empty()) { _Cells.back() = new CGroupCell(CViewBase::TCtorParam()); + string style; + if (present[MY_HTML_TD_STYLE] && value[MY_HTML_TD_STYLE]) + style = value[MY_HTML_TD_STYLE]; // Set the cell parameters + if (!style.empty()) + { + TStyle styles = parseStyle(style); + TStyle::iterator it; + + it = styles.find("background-repeat"); + _Cells.back()->setTextureTile(it != styles.end()); + + // Webig only + it = styles.find("background-scale"); + _Cells.back()->setTextureScale(it != styles.end()); + + it = styles.find("background-image"); + if (it != styles.end()) + { + nlinfo("found background-image %s", it->second.c_str()); + string image = (*it).second; + string::size_type texExt = strlwr(image).find("url("); + // Url image + if (texExt != string::npos) + { + // Remove url() + image = image.substr(4, image.size()-5); + addImageDownload(image, _Cells.back()); + // Image in BNP + } + else + { + _Cells.back()->setTexture(image); + } + } + } _Cells.back()->BgColor = _CellParams.back().BgColor; _Cells.back()->Align = _CellParams.back().Align; _Cells.back()->VAlign = _CellParams.back().VAlign; @@ -1435,6 +1659,8 @@ void CGroupHTML::endElement (uint element_number) popIfNotEmpty (_GlobalColor); popIfNotEmpty (_A); popIfNotEmpty (_Link); + popIfNotEmpty (_LinkTitle); + popIfNotEmpty (_LinkClass); break; case HTML_H1: case HTML_H2: @@ -1452,6 +1678,7 @@ void CGroupHTML::endElement (uint element_number) break; case HTML_DIV: _DivName = ""; + popIfNotEmpty (_Divs); break; case HTML_TABLE: @@ -1551,7 +1778,7 @@ void CGroupHTML::endElement (uint element_number) if (addBnpDownload(_ObjectData, _ObjectAction, _ObjectScript, _ObjectMD5Sum)) { CInterfaceManager *pIM = CInterfaceManager::getInstance(); - pIM->executeLuaScript(_ObjectScript, true); + pIM->executeLuaScript("\nlocal __ALLREADYDL__=true\n"+_ObjectScript, true); } _ObjectScript = ""; } @@ -1586,6 +1813,7 @@ void CGroupHTML::endUnparsedElement(const char *buffer, int length) _ParsingLua = false; // execute the embeded lua script CInterfaceManager *pIM = CInterfaceManager::getInstance(); + _LuaScript = "\nlocal __CURRENT_WINDOW__=\""+this->_Id+"\" \n"+_LuaScript; pIM->executeLuaScript(_LuaScript, true); } } @@ -2211,40 +2439,77 @@ void CGroupHTML::addString(const ucstring &str) // Not added ? if (!added) { - CViewLink *newLink = new CViewLink(CViewBase::TCtorParam()); - if (getA()) + if (getA() && string(getLinkClass()) == "ryzom-ui-button") { - newLink->Link = getLink(); - if (!newLink->Link.empty()) - { - newLink->setHTMLView (this); - newLink->setUnderlined (true); - } - } - newLink->setText(tmpStr); - newLink->setColor(getTextColor()); - newLink->setFontSize(getFontSize()); - newLink->setMultiLineSpace((uint)((float)getFontSize()*LineSpaceFontFactor)); - newLink->setMultiLine(true); - newLink->setModulateGlobalColor(getGlobalColor()); - // newLink->setLineAtBottom (true); + string buttonTemplate = DefaultButtonGroup; + // Action handler parameters : "name=group_html_id|form=id_of_the_form|submit_button=button_name" + string param = "name=" + this->_Id + "|url=" + getLink(); - if (getA() && !newLink->Link.empty()) - { - getParagraph()->addChildLink(newLink); + CInterfaceManager *im = CInterfaceManager::getInstance(); + typedef pair TTmplParam; + vector tmplParams; + tmplParams.push_back(TTmplParam("id", "")); + tmplParams.push_back(TTmplParam("onclick", "browse")); + tmplParams.push_back(TTmplParam("onclick_param", param)); + tmplParams.push_back(TTmplParam("active", "true")); + CInterfaceGroup *buttonGroup = im->createGroupInstance(buttonTemplate, _Paragraph->getId(), tmplParams); + if (buttonGroup) + { + + // Add the ctrl button + CCtrlTextButton *ctrlButton = dynamic_cast(buttonGroup->getCtrl("button")); + if (!ctrlButton) ctrlButton = dynamic_cast(buttonGroup->getCtrl("b")); + if (ctrlButton) + { + ctrlButton->setModulateGlobalColorAll (false); + + // Translate the tooltip + ctrlButton->setDefaultContextHelp(ucstring::makeFromUtf8(getLinkTitle())); + ctrlButton->setText(tmpStr); + } + getParagraph()->addChild (buttonGroup); + paragraphChange (); + } + } else { - getParagraph()->addChild(newLink); + CViewLink *newLink = new CViewLink(CViewBase::TCtorParam()); + if (getA()) + { + newLink->Link = getLink(); + newLink->LinkTitle = getLinkTitle(); + if (!newLink->Link.empty()) + { + newLink->setHTMLView (this); + newLink->setUnderlined (true); + } + } + newLink->setText(tmpStr); + newLink->setColor(getTextColor()); + newLink->setFontSize(getFontSize()); + newLink->setMultiLineSpace((uint)((float)getFontSize()*LineSpaceFontFactor)); + newLink->setMultiLine(true); + newLink->setModulateGlobalColor(getGlobalColor()); + // newLink->setLineAtBottom (true); + + if (getA() && !newLink->Link.empty()) + { + getParagraph()->addChildLink(newLink); + } + else + { + getParagraph()->addChild(newLink); + } + paragraphChange (); } - paragraphChange (); } } } // *************************************************************************** -void CGroupHTML::addImage(const char *img, bool globalColor) +void CGroupHTML::addImage(const char *img, bool globalColor, bool reloadImg) { // In a paragraph ? if (_Paragraph) @@ -2268,6 +2533,7 @@ void CGroupHTML::addImage(const char *img, bool globalColor) newImage->Link = getLink(); newImage->setHTMLView (this); }*/ + newImage->setRenderLayer(getRenderLayer()+1); newImage->setTexture (finalUrl); newImage->setModulateGlobalColor(globalColor); @@ -2284,7 +2550,7 @@ void CGroupHTML::addImage(const char *img, bool globalColor) // 2/ if it doesn't work, try to load the image in cache // image = localImageName(img); - if (lookupLocalFile (finalUrl, image.c_str(), false)) + if (!reloadImg && lookupLocalFile (finalUrl, image.c_str(), false)) { // No more text in this text view _CurrentViewLink = NULL; @@ -2314,7 +2580,9 @@ void CGroupHTML::addImage(const char *img, bool globalColor) else*/ getParagraph()->addChild(newImage); paragraphChange (); - } else { + } + else + { // // 3/ if it doesn't work, display a placeholder and ask to dl the image into the cache @@ -2527,6 +2795,9 @@ CCtrlButton *CGroupHTML::addButton(CCtrlButton::EType type, const std::string &/ ctrlButton->setInstantContextHelp(true); ctrlButton->setToolTipParent(TTMouse); + ctrlButton->setToolTipParentPosRef(Hotspot_TTAuto); + ctrlButton->setToolTipPosRef(Hotspot_TTAuto); + ctrlButton->setActionOnLeftClickParams(tooltip); } getParagraph()->addChild (ctrlButton); @@ -2556,6 +2827,7 @@ void CGroupHTML::clearContext() _UL.clear(); _A.clear(); _Link.clear(); + _LinkTitle.clear(); _Tables.clear(); _Cells.clear(); _TR.clear(); @@ -2632,6 +2904,9 @@ CInterfaceGroup *CGroupHTML::getCurrentGroup() void CGroupHTML::addGroup (CInterfaceGroup *group, uint beginSpace) { + if (!group) + return; + // Remove previous paragraph if empty if (_Paragraph && (_Paragraph->getNumChildren() == 0)) { @@ -2796,7 +3071,30 @@ void CGroupHTML::setBackgroundColor (const CRGBA &bgcolor) { // Change the background color bitmap->setColor (bgcolor); - bitmap->setModulateGlobalColor(true); + bitmap->setModulateGlobalColor(false); + } + } +} + +// *************************************************************************** + +void CGroupHTML::setBackground (const string &bgtex, bool scale, bool tile) +{ + // Should have a child named bg + CViewBase *view = getView (DefaultBackgroundBitmapView); + if (view) + { + CViewBitmap *bitmap = dynamic_cast (view); + if (bitmap) + { + bitmap->setParentPosRef(Hotspot_TL); + bitmap->setPosRef(Hotspot_TL); + bitmap->setX(0); + bitmap->setY(0); + bitmap->setRenderLayer(-2); + bitmap->setScale(scale); + bitmap->setTile(tile); + addImageDownload(bgtex, view); } } } @@ -2897,9 +3195,6 @@ void CGroupHTML::handle () _Browsing = true; updateRefreshButton(); - // Add custom get params - addHTTPGetParams (finalUrl); - // Save new url _URL = finalUrl; @@ -2910,6 +3205,10 @@ void CGroupHTML::handle () initLibWWW(); _TrustedDomain = isTrustedDomain(setCurrentDomain(finalUrl)); + // Add custom get params + addHTTPGetParams (finalUrl, _TrustedDomain); + + // Get the final URL C3WSmartPtr uri = HTParse(finalUrl.c_str(), NULL, PARSE_ALL); @@ -3030,7 +3329,7 @@ void CGroupHTML::handle () HTParseFormInput(formfields, (_PostFormSubmitButton + "_y=0").c_str()); // Add custom params - addHTTPPostParams (formfields); + addHTTPPostParams(formfields, _TrustedDomain); // Reset the title if(_TitlePrefix.empty()) @@ -3146,13 +3445,13 @@ void CGroupHTML::endBuild () // *************************************************************************** -void CGroupHTML::addHTTPGetParams (string &/* url */) +void CGroupHTML::addHTTPGetParams (string &/* url */, bool /*trustedDomain*/) { } // *************************************************************************** -void CGroupHTML::addHTTPPostParams (HTAssocList * /* formfields */) +void CGroupHTML::addHTTPPostParams (HTAssocList * /* formfields */, bool /*trustedDomain*/) { } @@ -3401,7 +3700,7 @@ int CGroupHTML::luaRefresh(CLuaState &ls) // *************************************************************************** int CGroupHTML::luaRemoveContent(CLuaState &ls) { - const char *funcName = "refresh"; + const char *funcName = "removeContent"; CLuaIHM::checkArgCount(ls, funcName, 0); removeContent(); return 0; diff --git a/code/ryzom/client/src/interface_v3/group_html.h b/code/ryzom/client/src/interface_v3/group_html.h index d155c6d3f..098fbb4ff 100644 --- a/code/ryzom/client/src/interface_v3/group_html.h +++ b/code/ryzom/client/src/interface_v3/group_html.h @@ -30,6 +30,8 @@ #include "ctrl_button.h" #include "group_table.h" +typedef std::map TStyle; + extern "C" { #include "WWWInit.h" @@ -147,6 +149,7 @@ public: std::string DefaultCheckBoxBitmapPushed; std::string DefaultCheckBoxBitmapOver; std::string DefaultBackgroundBitmapView; + std::string CurrentLinkTitle; // Browser home std::string Home; @@ -216,10 +219,10 @@ protected : virtual void endUnparsedElement(const char *buffer, int length); // Add GET params to the url - virtual void addHTTPGetParams (std::string &url); + virtual void addHTTPGetParams (std::string &url, bool trustedDomain); // Add POST params to the libwww list - virtual void addHTTPPostParams (HTAssocList *formfields); + virtual void addHTTPPostParams (HTAssocList *formfields, bool trustedDomain); // the current request is terminated virtual void requestTerminated(HTRequest *request); @@ -227,6 +230,9 @@ protected : // Get Home URL virtual std::string home(); + // Parse style html tag + TStyle parseStyle(const std::string &str_styles); + // Handle some work at each pass virtual void handle (); @@ -251,7 +257,7 @@ protected : void addString(const ucstring &str); // Add an image in the current paragraph - void addImage(const char *image, bool globalColor); + void addImage(const char *image, bool globalColor, bool reloadImg=false); // Add a text area in the current paragraph CInterfaceGroup *addTextArea (const std::string &templateName, const char *name, uint rows, uint cols, bool multiLine, const ucstring &content); @@ -266,6 +272,9 @@ protected : // Set the background color void setBackgroundColor (const NLMISC::CRGBA &bgcolor); + // Set the background + void setBackground (const std::string &bgtex, bool scale, bool tile); + // Force the current string to be in a single string void flushString(); @@ -281,7 +290,7 @@ protected : // Current URL std::string _URL; - // Current DOMAIN + // Current DOMAIN bool _TrustedDomain; // Title prefix @@ -397,6 +406,30 @@ protected : return _Link.back().c_str(); } + std::vector _LinkTitle; + inline const char *getLinkTitle() const + { + if (_LinkTitle.empty()) + return ""; + return _LinkTitle.back().c_str(); + } + std::vector _LinkClass; + inline const char *getLinkClass() const + { + if (_LinkClass.empty()) + return ""; + return _LinkClass.back().c_str(); + } + + // Divs (i.e. interface group) + std::vector _Divs; + inline CInterfaceGroup *getDiv() const + { + if (_Divs.empty()) + return NULL; + return _Divs.back(); + } + // Tables std::vector _Tables; inline CGroupTable *getTable() const @@ -475,6 +508,7 @@ protected : NoWrap = false; } NLMISC::CRGBA BgColor; + std::string Style; CGroupCell::TAlign Align; CGroupCell::TVAlign VAlign; sint32 LeftMargin; @@ -582,9 +616,9 @@ private: void checkImageDownload(); void addImageDownload(const std::string &url, CViewBase *img); std::string localImageName(const std::string &url); + bool isTrustedDomain(const std::string &domain); - - + void setImage(CViewBase *view, const std::string &file); // BnpDownload system void initBnpDownload(); diff --git a/code/ryzom/client/src/interface_v3/group_html_cs.cpp b/code/ryzom/client/src/interface_v3/group_html_cs.cpp index 66d884437..13d7dda24 100644 --- a/code/ryzom/client/src/interface_v3/group_html_cs.cpp +++ b/code/ryzom/client/src/interface_v3/group_html_cs.cpp @@ -24,6 +24,7 @@ #include "group_html_cs.h" #include "game_share/xml_auto_ptr.h" #include "../client_cfg.h" +#include "interface_manager.h" // used for login cookie to be sent to the web server #include "../net_manager.h" @@ -49,7 +50,7 @@ CGroupHTMLCS::~CGroupHTMLCS() // *************************************************************************** -void CGroupHTMLCS::addHTTPGetParams (string &url) +void CGroupHTMLCS::addHTTPGetParams (string &url, bool /*trustedDomain*/) { url += ((url.find('?') != string::npos) ? "&" : "?"); @@ -69,7 +70,7 @@ void CGroupHTMLCS::addHTTPGetParams (string &url) // *************************************************************************** -void CGroupHTMLCS::addHTTPPostParams (HTAssocList *formfields) +void CGroupHTMLCS::addHTTPPostParams (HTAssocList *formfields, bool /*trustedDomain*/) { std::vector parameters; getParameters (parameters, false); @@ -120,6 +121,15 @@ void CGroupHTMLCS::getParameters (std::vector ¶meters, bool enco string s = getDebugInformation(); s += getSystemInformation(); + static bool webIgReady = false; + + if (!webIgReady) // Webig is ready when getParameters of CGroupHTMLCS is called + { + webIgReady = true; + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + pIM->executeLuaScript("game:onWebIgReady()"); + } + // For each line string::size_type startOfLine = 0; string::size_type endOfLine; diff --git a/code/ryzom/client/src/interface_v3/group_html_cs.h b/code/ryzom/client/src/interface_v3/group_html_cs.h index a37b2b50c..e3c8a765e 100644 --- a/code/ryzom/client/src/interface_v3/group_html_cs.h +++ b/code/ryzom/client/src/interface_v3/group_html_cs.h @@ -39,8 +39,8 @@ public: ~CGroupHTMLCS(); // From CGroupHTML - virtual void addHTTPGetParams (std::string &url); - virtual void addHTTPPostParams (HTAssocList *formfields); + virtual void addHTTPGetParams (std::string &url, bool trustedDomain); + virtual void addHTTPPostParams (HTAssocList *formfields, bool trustedDomain); virtual std::string home(); private: diff --git a/code/ryzom/client/src/interface_v3/group_html_forum.cpp b/code/ryzom/client/src/interface_v3/group_html_forum.cpp index b0fd96642..53d1058d9 100644 --- a/code/ryzom/client/src/interface_v3/group_html_forum.cpp +++ b/code/ryzom/client/src/interface_v3/group_html_forum.cpp @@ -51,9 +51,9 @@ CGroupHTMLForum::~CGroupHTMLForum() // *************************************************************************** -void CGroupHTMLForum::addHTTPGetParams (string &url) +void CGroupHTMLForum::addHTTPGetParams (string &url, bool /*trustedDomain*/) { - ucstring user_name = UserEntity->getDisplayName (); + ucstring user_name = UserEntity->getLoginName (); const SGuild &guild = CGuildManager::getInstance()->getGuild(); string gname = guild.Name.toUtf8(); @@ -83,9 +83,9 @@ void CGroupHTMLForum::addHTTPGetParams (string &url) // *************************************************************************** -void CGroupHTMLForum::addHTTPPostParams (HTAssocList *formfields) +void CGroupHTMLForum::addHTTPPostParams (HTAssocList *formfields, bool /*trustedDomain*/) { - ucstring user_name = UserEntity->getDisplayName (); + ucstring user_name = UserEntity->getLoginName (); const SGuild &guild = CGuildManager::getInstance()->getGuild(); string gname = guild.Name.toUtf8(); @@ -115,12 +115,13 @@ string CGroupHTMLForum::home () void CGroupHTMLForum::handle () { - // Do nothing if WebServer is not initialized +/* // Do nothing if WebServer is not initialized if (!WebServer.empty()) { Home = WebServer+"forum.php"; CGroupHTML::handle (); } +*/ } // *************************************************************************** diff --git a/code/ryzom/client/src/interface_v3/group_html_forum.h b/code/ryzom/client/src/interface_v3/group_html_forum.h index 8458609c6..fe233fecc 100644 --- a/code/ryzom/client/src/interface_v3/group_html_forum.h +++ b/code/ryzom/client/src/interface_v3/group_html_forum.h @@ -39,8 +39,8 @@ public: ~CGroupHTMLForum(); // From CGroupHTML - virtual void addHTTPGetParams (std::string &url); - virtual void addHTTPPostParams (HTAssocList *formfields); + virtual void addHTTPGetParams (std::string &url, bool trustedDomain); + virtual void addHTTPPostParams (HTAssocList *formfields, bool trustedDomain); virtual std::string home(); virtual void handle (); diff --git a/code/ryzom/client/src/interface_v3/group_html_mail.cpp b/code/ryzom/client/src/interface_v3/group_html_mail.cpp index 2c0d9d225..9c723d45b 100644 --- a/code/ryzom/client/src/interface_v3/group_html_mail.cpp +++ b/code/ryzom/client/src/interface_v3/group_html_mail.cpp @@ -50,9 +50,9 @@ CGroupHTMLMail::~CGroupHTMLMail() // *************************************************************************** -void CGroupHTMLMail::addHTTPGetParams (string &url) +void CGroupHTMLMail::addHTTPGetParams (string &url, bool /*trustedDomain*/) { - ucstring user_name = UserEntity->getDisplayName (); + ucstring user_name = UserEntity->getLoginName (); url += ((url.find('?') != string::npos) ? "&" : "?") + string("shard=") + toString(CharacterHomeSessionId) + string("&user_login=") + user_name.toString() + @@ -62,9 +62,9 @@ void CGroupHTMLMail::addHTTPGetParams (string &url) // *************************************************************************** -void CGroupHTMLMail::addHTTPPostParams (HTAssocList *formfields) +void CGroupHTMLMail::addHTTPPostParams (HTAssocList *formfields, bool /*trustedDomain*/) { - ucstring user_name = UserEntity->getDisplayName (); + ucstring user_name = UserEntity->getLoginName (); HTParseFormInput(formfields, ("shard="+toString(CharacterHomeSessionId)).c_str()); HTParseFormInput(formfields, ("user_login="+user_name.toString()).c_str()); HTParseFormInput(formfields, ("session_cookie="+NetMngr.getLoginCookie().toString()).c_str()); @@ -85,11 +85,12 @@ string CGroupHTMLMail::home () void CGroupHTMLMail::handle () { // Do nothing if WebServer is not initialized - if (!WebServer.empty()) +/* if (!WebServer.empty()) { Home = WebServer+"mailbox.php"; CGroupHTML::handle (); } +*/ } // *************************************************************************** diff --git a/code/ryzom/client/src/interface_v3/group_html_mail.h b/code/ryzom/client/src/interface_v3/group_html_mail.h index bb72a3b78..51f363c3c 100644 --- a/code/ryzom/client/src/interface_v3/group_html_mail.h +++ b/code/ryzom/client/src/interface_v3/group_html_mail.h @@ -39,8 +39,8 @@ public: ~CGroupHTMLMail(); // From CGroupHTML - virtual void addHTTPGetParams (std::string &url); - virtual void addHTTPPostParams (HTAssocList *formfields); + virtual void addHTTPGetParams (std::string &url, bool trustedDomain); + virtual void addHTTPPostParams (HTAssocList *formfields, bool trustedDomain); virtual std::string home(); virtual void handle (); diff --git a/code/ryzom/client/src/interface_v3/group_html_webig.cpp b/code/ryzom/client/src/interface_v3/group_html_webig.cpp index 4a13d9806..e4a55ab48 100644 --- a/code/ryzom/client/src/interface_v3/group_html_webig.cpp +++ b/code/ryzom/client/src/interface_v3/group_html_webig.cpp @@ -20,6 +20,7 @@ #include "game_share/xml_auto_ptr.h" #include "../client_cfg.h" #include "../user_entity.h" +#include "../entities.h" #include "interface_manager.h" // used for login cookie to be sent to the web server @@ -55,7 +56,7 @@ static string getWebAuthKey() // authkey = uint32 cid = NetMngr.getLoginCookie().getUserId() * 16 + PlayerSelectedSlot; string rawKey = toString(CharacterHomeSessionId) + - UserEntity->getDisplayName().toString() + + UserEntity->getLoginName().toString() + toString(cid) + NetMngr.getLoginCookie().toString(); string key = getMD5((const uint8*)rawKey.c_str(), (uint32)rawKey.size()).toString(); @@ -64,18 +65,72 @@ static string getWebAuthKey() return key; } -void addWebIGParams (string &url) +void addWebIGParams (string &url, bool trustedDomain) { if(!UserEntity || !NetMngr.getLoginCookie().isValid()) return; uint32 cid = NetMngr.getLoginCookie().getUserId() * 16 + PlayerSelectedSlot; url += ((url.find('?') != string::npos) ? "&" : "?") + string("shardid=") + toString(CharacterHomeSessionId) + - string("&name=") + UserEntity->getDisplayName().toUtf8() + - string("&cid=") + toString(cid) + - string("&authkey=") + getWebAuthKey() + - string("&ig=1") + - string("&lang=") + CI18N::getCurrentLanguageCode(); + string("&name=") + UserEntity->getLoginName().toUtf8() + + string("&lang=") + CI18N::getCurrentLanguageCode() + + string("&ig=1"); + if (trustedDomain) + { + url += string("&cid=") + toString(cid) + + string("&authkey=") + getWebAuthKey(); + + if (url.find('$') != string::npos) + { + strFindReplace(url, "$datasetid$", toString(UserEntity->dataSetId())); + strFindReplace(url, "$gender$", GSGENDER::toString(UserEntity->getGender())); + strFindReplace(url, "$displayName$", UserEntity->getDisplayName().toString()); + strFindReplace(url, "$posx$", toString(UserEntity->pos().x)); + strFindReplace(url, "$posy$", toString(UserEntity->pos().y)); + strFindReplace(url, "$posz$", toString(UserEntity->pos().z)); + strFindReplace(url, "$post$", toString(atan2(UserEntity->front().y, UserEntity->front().x))); + + // Target fields + const char *dbPath = "UI:VARIABLES:TARGET:SLOT"; + CInterfaceManager *im = CInterfaceManager::getInstance(); + CCDBNodeLeaf *node = im->getDbProp(dbPath, false); + if (node && (uint8)node->getValue32() != (uint8) CLFECOMMON::INVALID_SLOT) + { + CEntityCL *target = EntitiesMngr.entity((uint) node->getValue32()); + if (target) + { + strFindReplace(url, "$tdatasetid$", toString(target->dataSetId())); + strFindReplace(url, "$tdisplayName$", target->getDisplayName().toString()); + strFindReplace(url, "$tposx$", toString(target->pos().x)); + strFindReplace(url, "$tposy$", toString(target->pos().y)); + strFindReplace(url, "$tposz$", toString(target->pos().z)); + strFindReplace(url, "$tpost$", toString(atan2(target->front().y, target->front().x))); + strFindReplace(url, "$tsheet$", target->sheetId().toString()); + string type; + if (target->isFauna()) + type = "fauna"; + else if (target->isNPC()) + type = "npc"; + else if (target->isPlayer()) + type = "player"; + else if (target->isUser()) + type = "user"; + strFindReplace(url, "$ttype$", target->sheetId().toString()); + } + else + { + strFindReplace(url, "$tdatasetid$", ""); + strFindReplace(url, "$tdisplayName$", ""); + strFindReplace(url, "$tposx$", ""); + strFindReplace(url, "$tposy$", ""); + strFindReplace(url, "$tposz$", ""); + strFindReplace(url, "$tpost$", ""); + strFindReplace(url, "$tsheet$", ""); + strFindReplace(url, "$ttype$", ""); + } + } + } + } } // *************************************************************************** @@ -204,7 +259,7 @@ struct CWebigNotificationThread : public NLMISC::IRunnable while (true) { string url = "http://"+ClientCfg.WebIgMainDomain+"/start/index.php?app=notif&rnd="+randomString(); - addWebIGParams(url); + addWebIGParams(url, true); get(url); nlSleep(10*60*1000); } @@ -230,11 +285,66 @@ void startWebigNotificationThread() // *************************************************************************** +// *************************************************************************** +NLMISC_REGISTER_OBJECT(CViewBase, CGroupHTMLAuth, std::string, "auth_html"); + +CGroupHTMLAuth::CGroupHTMLAuth(const TCtorParam ¶m) +: CGroupHTML(param) +{ +} + +// *************************************************************************** + +CGroupHTMLAuth::~CGroupHTMLAuth() +{ +} + +void CGroupHTMLAuth::addHTTPGetParams (string &url, bool trustedDomain) +{ + addWebIGParams(url, trustedDomain); +} + +// *************************************************************************** + +void CGroupHTMLAuth::addHTTPPostParams (HTAssocList *formfields, bool trustedDomain) +{ + if(!UserEntity) return; + + uint32 cid = NetMngr.getLoginCookie().getUserId() * 16 + PlayerSelectedSlot; + HTParseFormInput(formfields, ("shardid="+toString(CharacterHomeSessionId)).c_str()); + HTParseFormInput(formfields, ("name="+UserEntity->getLoginName().toUtf8()).c_str()); + HTParseFormInput(formfields, ("lang="+CI18N::getCurrentLanguageCode()).c_str()); + HTParseFormInput(formfields, "ig=1"); + if (trustedDomain) + { + HTParseFormInput(formfields, ("cid="+toString(cid)).c_str()); + HTParseFormInput(formfields, ("authkey="+getWebAuthKey()).c_str()); + } +} + +// *************************************************************************** + +string CGroupHTMLAuth::home () +{ + return Home; +} + +// *************************************************************************** + +void CGroupHTMLAuth::handle () +{ + CGroupHTML::handle (); +} + +// *************************************************************************** +// *************************************************************************** + + // *************************************************************************** NLMISC_REGISTER_OBJECT(CViewBase, CGroupHTMLWebIG, std::string, "webig_html"); CGroupHTMLWebIG::CGroupHTMLWebIG(const TCtorParam ¶m) -: CGroupHTML(param) +: CGroupHTMLAuth(param) { startWebigNotificationThread(); } @@ -245,24 +355,18 @@ CGroupHTMLWebIG::~CGroupHTMLWebIG() { } -void CGroupHTMLWebIG::addHTTPGetParams (string &url) +// *************************************************************************** + +void CGroupHTMLWebIG::addHTTPGetParams (string &url, bool trustedDomain) { - addWebIGParams(url); + CGroupHTMLAuth::addHTTPGetParams(url, trustedDomain); } // *************************************************************************** -void CGroupHTMLWebIG::addHTTPPostParams (HTAssocList *formfields) +void CGroupHTMLWebIG::addHTTPPostParams (HTAssocList *formfields, bool trustedDomain) { - if(!UserEntity) return; - - uint32 cid = NetMngr.getLoginCookie().getUserId() * 16 + PlayerSelectedSlot; - HTParseFormInput(formfields, ("shardid="+toString(CharacterHomeSessionId)).c_str()); - HTParseFormInput(formfields, ("name="+UserEntity->getDisplayName().toUtf8()).c_str()); - HTParseFormInput(formfields, ("cid="+toString(cid)).c_str()); - HTParseFormInput(formfields, ("authkey="+getWebAuthKey()).c_str()); - HTParseFormInput(formfields, "ig=1"); - HTParseFormInput(formfields, ("lang="+CI18N::getCurrentLanguageCode()).c_str()); + CGroupHTMLAuth::addHTTPPostParams(formfields, trustedDomain); } // *************************************************************************** @@ -276,8 +380,6 @@ string CGroupHTMLWebIG::home () void CGroupHTMLWebIG::handle () { -// Home = "http://atys.ryzom.com/start/index.php"; - CGroupHTML::handle (); + CGroupHTMLAuth::handle (); } -// *************************************************************************** diff --git a/code/ryzom/client/src/interface_v3/group_html_webig.h b/code/ryzom/client/src/interface_v3/group_html_webig.h index 15485d7dc..5967f671d 100644 --- a/code/ryzom/client/src/interface_v3/group_html_webig.h +++ b/code/ryzom/client/src/interface_v3/group_html_webig.h @@ -20,10 +20,32 @@ #include "nel/misc/types_nl.h" #include "group_html.h" +/** +* Auth HTML group +*/ +class CGroupHTMLAuth : public CGroupHTML +{ +public: + + // Constructor + CGroupHTMLAuth(const TCtorParam ¶m); + ~CGroupHTMLAuth(); + + // From CGroupHTML + virtual void addHTTPGetParams (std::string &url, bool trustedDomain); + virtual void addHTTPPostParams (HTAssocList *formfields, bool trustedDomain); + virtual std::string home(); + virtual void handle (); + +private: + +}; + + /** * WebIG HTML group */ -class CGroupHTMLWebIG : public CGroupHTML +class CGroupHTMLWebIG : public CGroupHTMLAuth { public: @@ -31,9 +53,9 @@ public: CGroupHTMLWebIG(const TCtorParam ¶m); ~CGroupHTMLWebIG(); - // From CGroupHTML - virtual void addHTTPGetParams (std::string &url); - virtual void addHTTPPostParams (HTAssocList *formfields); + /// From CGroupHTMLAuth + virtual void addHTTPGetParams (std::string &url, bool trustedDomain); + virtual void addHTTPPostParams (HTAssocList *formfields, bool trustedDomain); virtual std::string home(); virtual void handle (); diff --git a/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp b/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp index 85238ad7d..cc0d3e82f 100644 --- a/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp +++ b/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp @@ -973,6 +973,132 @@ void CGroupInSceneBubbleManager::dynChatOpen (uint32 nBotUID, uint32 nBotName, c UserEntity->interlocutor( pChar->slot() ); } +// *************************************************************************** + +void CGroupInSceneBubbleManager::webIgChatOpen (uint32 nBotUID, string text, const vector &strs, const vector &links) +{ + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + // If the character doesn't exist in view field -> do not display the bubble + + CEntityCL *pEntity = EntitiesMngr.getEntityByCompressedIndex(nBotUID); + CCharacterCL *pChar = dynamic_cast(pEntity); + if (pChar == NULL) + { + nlwarning("character probably too far"); + return; + } + + // Look if we get a bubble with this nBotUID ? + uint32 pos, j; + for (pos = 0; pos < _DynBubbles.size(); ++pos) + { + if (_DynBubbles[pos].BotUID == nBotUID) + break; + } + + // If the bubble doesn't exist -> create + CGroupInSceneBubble *bubble = NULL; + string id; + if (pos == _DynBubbles.size()) + { + uint32 i = 0; + while (getDynBubble(i) != NULL) i++; + id = "in_scene_webig_bubble_" + toString(i); + // Create the instance + std::vector > templateParams; + templateParams.push_back (std::pair("id", id)); + + CInterfaceGroup *group = pIM->createGroupInstance ("webig_3dbulle_L", "ui:interface", templateParams); + if (group == NULL) + { + nlwarning("cannot create webig_3dbulle_L"); + return; + } + // Link to the interface + pIM->addWindowToMasterGroup("ui:interface", group); + CInterfaceGroup *pRoot = dynamic_cast(pIM->getElementFromId("ui:interface")); + group->setParent(pRoot); + if (pRoot) + pRoot->addGroup (group); + group->setActive(false); + + bubble = dynamic_cast(group); + if (bubble == NULL) + { + nlwarning("cannot cast to CGroupInSceneBubble"); + return; + } + CDynBubble dynBubble; + dynBubble.BotName = 0; + dynBubble.BotUID = nBotUID; + dynBubble.Bubble = bubble; + _DynBubbles.push_back(dynBubble); + } + else + { + bubble = _DynBubbles[pos].Bubble; + // Remove from group to delete if in the same frame + for (j=0; j<_GroupToDelete.size(); j++) + if (_GroupToDelete[j] == bubble) + { + _GroupToDelete.erase(_GroupToDelete.begin()+j); + break; + } + } + + // Update the bubble's texts + + ucstring ucText; + ucText.fromUtf8(text); + bubble->setText(ucText); + id = bubble->getId() + ":header_opened:window:"; + CViewText *pVT; + CCtrlLink *pCL; + + _DynBubbles[pos].DescWaiting = 0; + + for (j = 0; j < 8; ++j) + { + pVT = dynamic_cast(bubble->getElement(id+"opt"+toString(j))); + if (pVT != NULL) + { + pVT->setActive(false); + pVT->setText(ucstring("")); + } + pCL = dynamic_cast(bubble->getElement(id+"optb"+toString(j))); + if (pCL != NULL) pCL->setActive(false); + } + + for (j = 0; j < strs.size(); ++j) + { + string fullid = id+"opt"+toString(j); + pVT = dynamic_cast(bubble->getElement(id+"opt"+toString(j))); + if (pVT != NULL) + { + pVT->setActive(true); + ucstring optionText; + optionText.fromUtf8(strs[j]); + pVT->setText(optionText); + pCL = dynamic_cast(bubble->getElement(id+"optb"+toString(j))); + if (pCL != NULL) + { + pCL->setActionOnLeftClick("browse"); + pCL->setParamsOnLeftClick("name=ui:interface:web_transactions:content:html|show=0|url="+links[j]); + //pCL->setActionOnLeftClickParams("name=ui:interface:web_transactions:content:html|url="+links[j]); + pCL->setActive(true); + + } + } + } + + // Link bubble to the character + pChar->setBubble(bubble); + + // Make the npc face the character + UserEntity->interlocutor( pChar->slot() ); +} + + uint32 CGroupInSceneBubbleManager::CDynBubble::getOptionStringId(uint option) { if (!Bubble) return 0; diff --git a/code/ryzom/client/src/interface_v3/group_in_scene_bubble.h b/code/ryzom/client/src/interface_v3/group_in_scene_bubble.h index 5e7cd4546..635e8c47e 100644 --- a/code/ryzom/client/src/interface_v3/group_in_scene_bubble.h +++ b/code/ryzom/client/src/interface_v3/group_in_scene_bubble.h @@ -64,6 +64,9 @@ public: // Open a Dynamic Chat void dynChatOpen (uint32 nBotUID, uint32 nBotName, const std::vector &DynStrs); + // Open a Dynamic Chat from webig + void webIgChatOpen (uint32 nBotUID, std::string sBotName, const std::vector &strs, const std::vector &links); + // Close a Dynamic Chat void dynChatClose (uint32 nBotUID); diff --git a/code/ryzom/client/src/interface_v3/group_in_scene_user_info.cpp b/code/ryzom/client/src/interface_v3/group_in_scene_user_info.cpp index dcc3895f7..d9fa10707 100644 --- a/code/ryzom/client/src/interface_v3/group_in_scene_user_info.cpp +++ b/code/ryzom/client/src/interface_v3/group_in_scene_user_info.cpp @@ -97,7 +97,7 @@ CRGBA CGroupInSceneUserInfo::BarColorHPNegative = CRGBA(127, 32, 0); #define nlfsinfo2 if ( _Entity->isForageSource() ) nlinfo -CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) +CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (CEntityCL *entity) { // Get the interface manager CInterfaceManager *pIM = CInterfaceManager::getInstance(); @@ -129,6 +129,7 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) bool forageSourceBarDisplayed= false; bool needPvPLogo= false; bool permanentContent = false; + bool rpTags = false; bool displayMissionIcons = pIM->getDbProp("UI:SAVE:INSCENE:FRIEND:MISSION_ICON")->getValueBool(); // Names @@ -136,6 +137,12 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) ucstring theTribeName; ucstring entityName = entity->getDisplayName(); ucstring entityTitle = entity->getTitle(); + + ucstring entityTag1 = entity->getTag(1); + ucstring entityTag2 = entity->getTag(2); + ucstring entityTag3 = entity->getTag(3); + ucstring entityTag4 = entity->getTag(4); + string entityPermanentContent = entity->getPermanentStatutIcon(); // Active fields and bars @@ -160,6 +167,8 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) } else if(npcFriendAndNeutral) { + string dbEntry; + getBarSettings( pIM, user, entity->isPlayer(), _friend, dbEntry, bars ); // For RoleMasters, merchants etc... must display name and function, and nothing else for(uint i=0;igetDbProp(dbEntry+"RPTAGS")->getValueBool(); } else { @@ -176,6 +186,7 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) getBarSettings( pIM, user, entity->isPlayer(), _friend, dbEntry, bars ); name = !entityName.empty() && pIM->getDbProp(dbEntry+"NAME")->getValueBool(); title = !entityTitle.empty() && pIM->getDbProp(dbEntry+"TITLE")->getValueBool(); + rpTags = (!entityTag1.empty() || !entityTag2.empty() || !entityTag3.empty() || !entityTag4.empty() ) && pIM->getDbProp(dbEntry+"RPTAGS")->getValueBool(); // if name is empty but not title, title is displayed as name if (!title && entityName.empty() && !entityTitle.empty() && pIM->getDbProp(dbEntry+"NAME")->getValueBool()) title = true; @@ -369,6 +380,26 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) else stringSpace += textH; + if (rpTags) + { + CPlayerCL * pPlayer = dynamic_cast(entity); + CViewBitmap *bitmap; + if (pPlayer == NULL || (pPlayer != NULL && pPlayer->getPvpMode() & PVP_MODE::PvpFaction)) + { + bitmap = dynamic_cast(leftGroup->getView ("rp_logo_1")); + if (bitmap) + bitmap->setTexture(entityTag1.toString()); + bitmap = dynamic_cast(leftGroup->getView ("rp_logo_2")); + if (bitmap) + bitmap->setTexture(entityTag2.toString()); + } + bitmap = dynamic_cast(leftGroup->getView ("rp_logo_3")); + if (bitmap) + bitmap->setTexture(entityTag3.toString()); + bitmap = dynamic_cast(leftGroup->getView ("rp_logo_4")); + if (bitmap) + bitmap->setTexture(entityTag4.toString()); + } // Get the permanent content bitmap if(permanentContent) @@ -539,162 +570,96 @@ CGroupInSceneUserInfo *CGroupInSceneUserInfo::build (class CEntityCL *entity) info->_MissionTarget = bitmap; } - CViewBase * pvpCivLogo = info->getView ("pvp_faction_civ_logo"); - CViewBase * pvpCultLogo = info->getView ("pvp_faction_cult_logo"); + CViewBase * pvpFactionLogo = info->getView ("pvp_faction_logo"); + CViewBase * pvpOutpostLogo = info->getView ("pvp_outpost_logo"); + CViewBase * pvpDuelLogo = info->getView ("pvp_duel_logo"); CPlayerCL * pPlayer = dynamic_cast(entity); if (pPlayer == NULL) needPvPLogo = false; - // set or inactive pvp logos - bool needCivPvpLogo = needPvPLogo; - bool needCultPvpLogo = needPvPLogo; - if (pPlayer != NULL && needPvPLogo && pvpCivLogo && pvpCultLogo) + if (pPlayer != NULL && needPvPLogo) { - uint8 civToDisplay = (uint8)(pPlayer->getClanCivMaxFame() & 0xFF); - uint8 cultToDisplay = (uint8)(pPlayer->getClanCultMaxFame() & 0xFF); - - if (!entity->isUser()) + if (pvpFactionLogo) { - bool civEnnemies = false; - for (uint8 i = 0; i < 4; i++) + pvpFactionLogo->setActive(true); + CViewBitmap * pvpFactionBitmap = dynamic_cast(pvpFactionLogo); + if( pvpFactionBitmap ) { - if ( (pPlayer->isPvpAlly(i) && UserEntity->isPvpEnnemy(i)) || - (pPlayer->isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) ) + if (user) { - civEnnemies = true; - if ( civToDisplay == i) - break; - else - civToDisplay = i; - } - - if (!civEnnemies) - { - if ( (pPlayer->isPvpAlly(i) && UserEntity->isPvpAlly(i)) || - (pPlayer->isPvpEnnemy(i) && UserEntity->isPvpEnnemy(i)) ) + if (pPlayer->getPvpMode() & PVP_MODE::PvpChallenge) { - if ( civToDisplay == i) - break; - else - civToDisplay = i; + pvpFactionBitmap->setTexture("ico_curse.tga"); + } + else if (pPlayer->getPvpMode() & PVP_MODE::PvpFaction) + { + if (pPlayer->getPvpMode() & PVP_MODE::PvpZoneSafe) + pvpFactionBitmap->setTexture("pvp_neutral.tga"); + else + pvpFactionBitmap->setTexture("pvp_enemy_tag.tga"); + } + else if (pPlayer->getPvpMode() & PVP_MODE::PvpFactionFlagged) + { + if (pPlayer->getPvpMode() & PVP_MODE::PvpSafe) + pvpFactionBitmap->setTexture("pvp_neutral.tga"); + else + pvpFactionBitmap->setTexture("pvp_enemy_flag.tga"); } - } - } - - - for (uint8 i = 4; i < 7; i++) - { - if ( (pPlayer->isPvpAlly(i) && UserEntity->isPvpEnnemy(i)) || - (pPlayer->isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) ) - { - if ( cultToDisplay == i) - break; else - cultToDisplay = i; - } - - if ( (pPlayer->isPvpAlly(i) && UserEntity->isPvpAlly(i)) || - (pPlayer->isPvpEnnemy(i) && UserEntity->isPvpEnnemy(i)) ) - { - if ( cultToDisplay == i) - break; - else - cultToDisplay = i; - } - } - } - - if ((pPlayer->getPvpMode() & PVP_MODE::PvpFaction) || (pPlayer->getPvpMode() & PVP_MODE::PvpFactionFlagged)) - { - CViewBitmap * pvpCivLogoBmp = dynamic_cast(pvpCivLogo); - if( pvpCivLogoBmp ) - { - if (pPlayer->isPvpAlly(civToDisplay)) - { - if (pPlayer->isPvpRanger()) - pvpCivLogoBmp->setTexture("pvp_ally_ranger.tga"); - else - pvpCivLogoBmp->setTexture("pvp_ally_"+toString(civToDisplay)+".tga"); - } - else if (pPlayer->isPvpEnnemy(civToDisplay)) - { - if (pPlayer->isPvpMarauder()) - pvpCivLogoBmp->setTexture("pvp_enemy_marauder.tga"); - else - pvpCivLogoBmp->setTexture("pvp_enemy_"+toString(civToDisplay)+".tga"); + pvpFactionLogo->setActive(false); } else { - needCivPvpLogo = false; - } - } - - CViewBitmap * pvpCultLogoBmp = dynamic_cast(pvpCultLogo); - if( pvpCultLogoBmp ) - { - if (pPlayer->isPvpAlly(cultToDisplay)) - { - if (pPlayer->isPvpPrimas()) - pvpCultLogoBmp->setTexture("pvp_ally_primas.tga"); + if (pPlayer->getPvpMode() & PVP_MODE::PvpChallenge) + pvpFactionBitmap->setTexture("ico_curse.tga"); + else if (pPlayer->isNeutralPVP()) + pvpFactionBitmap->setTexture("pvp_neutral.tga"); + else if (pPlayer->isAlly() && (pPlayer->getPvpMode() & PVP_MODE::PvpFactionFlagged)) + pvpFactionBitmap->setTexture("pvp_ally_flag.tga"); + else if (pPlayer->isAlly() && (pPlayer->getPvpMode() & PVP_MODE::PvpFaction)) + pvpFactionBitmap->setTexture("pvp_ally_tag.tga"); + else if (pPlayer->isEnemy() && (pPlayer->getPvpMode() & PVP_MODE::PvpFactionFlagged)) + pvpFactionBitmap->setTexture("pvp_enemy_flag.tga"); + else if (pPlayer->isEnemy() && (pPlayer->getPvpMode() & PVP_MODE::PvpFaction)) + pvpFactionBitmap->setTexture("pvp_enemy_tag.tga"); + else if (pPlayer->getPvpMode() & PVP_MODE::PvpFactionFlagged) + pvpFactionBitmap->setTexture("pvp_enemy_flag.tga"); + else if (pPlayer->getPvpMode() & PVP_MODE::PvpFaction) + pvpFactionBitmap->setTexture("pvp_enemy_tag.tga"); else - pvpCultLogoBmp->setTexture("pvp_ally_"+toString(cultToDisplay)+".tga"); - } - else if (pPlayer->isPvpEnnemy(cultToDisplay)) - { - if (pPlayer->isPvpTrytonist()) - pvpCultLogoBmp->setTexture("pvp_enemy_trytonist.tga"); - else - pvpCultLogoBmp->setTexture("pvp_enemy_"+toString(cultToDisplay)+".tga"); - } - else - { - needCultPvpLogo = false; + pvpFactionLogo->setActive(false); } } } - else - { - needCivPvpLogo = false; - needCultPvpLogo = false; - } - - CViewBase * pvpOutpostLogo = info->getView ("pvp_outpost_logo"); + if (pvpOutpostLogo) { - if( pPlayer->getOutpostId() == 0 ) - { + if( pPlayer->getOutpostId() != 0 ) + pvpOutpostLogo->setActive(true); + else pvpOutpostLogo->setActive(false); - } } - - CViewBase * pvpDuelLogo = info->getView ("pvp_duel_logo"); + if (pvpDuelLogo) { - if( !(pPlayer->getPvpMode()&PVP_MODE::PvpDuel || pPlayer->getPvpMode()&PVP_MODE::PvpChallenge) ) - { + if( pPlayer->getPvpMode()&PVP_MODE::PvpDuel ) + pvpDuelLogo->setActive(true); + else pvpDuelLogo->setActive(false); - } } + } else { - CViewBase * pvpOutpostLogo = info->getView ("pvp_outpost_logo"); + if (pvpFactionLogo) + pvpFactionLogo->setActive(false); if (pvpOutpostLogo) pvpOutpostLogo->setActive(false); - - CViewBase * pvpDuelLogo = info->getView ("pvp_duel_logo"); if (pvpDuelLogo) - pvpDuelLogo->setActive(false); + pvpDuelLogo->setActive(false); } - - if (pvpCivLogo) - pvpCivLogo->setActive(needCivPvpLogo); - - if (pvpCultLogo) - pvpCultLogo->setActive(needCultPvpLogo); - } // No bar and no string ? diff --git a/code/ryzom/client/src/interface_v3/group_table.cpp b/code/ryzom/client/src/interface_v3/group_table.cpp index 48ec926a4..890a8b0fe 100644 --- a/code/ryzom/client/src/interface_v3/group_table.cpp +++ b/code/ryzom/client/src/interface_v3/group_table.cpp @@ -55,6 +55,9 @@ CGroupCell::CGroupCell(const TCtorParam ¶m) IgnoreMaxWidth = false; IgnoreMinWidth = false; AddChildW = false; + _UserTexture = false; + _TextureTiled = false; + _TextureScaled = false; setEnclosedGroupDefaultParams(); addGroup (Group); } @@ -200,20 +203,62 @@ void CGroupCell::draw () } // Draw the background - if (BgColor.A != 0) + if (_UserTexture || BgColor.A != 0) { CViewRenderer &rVR = pIM->getViewRenderer(); - CRGBA finalColor; - finalColor.modulateFromColor (BgColor, pIM->getGlobalColor()); - - // Get the parent table - if (getParent ()) + if (_UserTexture) { - CGroupTable *table = static_cast (getParent ()); - finalColor.A = (uint8) (((uint16) table->CurrentAlpha * (uint16) finalColor.A) >> 8); + CRGBA col; + if (BgColor.A == 0 ) + col = CRGBA(255,255,255,255); + else + col = BgColor; + + + if (_TextureScaled && !_TextureTiled) + { + rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, + _WReal, _HReal, + 0, false, + _TextureId, + col ); + } + else + { + if (!_TextureTiled) + { + rVR.draw11RotFlipBitmap (_RenderLayer, _XReal, _YReal, + 0, false, + _TextureId, + col); + } + else + { + rVR.drawRotFlipBitmapTiled(_RenderLayer, _XReal, _YReal, + _WReal, _HReal, + 0, false, + _TextureId, + 0, + col); + } + } + } + else + { + CRGBA finalColor; + finalColor.modulateFromColor (BgColor, pIM->getGlobalColor()); - rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), finalColor); + // Get the parent table + if (getParent ()) + { + CGroupTable *table = static_cast (getParent ()); + finalColor.A = (uint8) (((uint16) table->CurrentAlpha * (uint16) finalColor.A) >> 8); + } + + //nlinfo("Blank Texture"); + rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), finalColor); + } } CInterfaceGroup::draw (); @@ -231,6 +276,40 @@ sint32 CGroupCell::getMinUsedW() const return Group->getMinUsedW(); } + +// ---------------------------------------------------------------------------- +void CGroupCell::setTexture(const std::string & TxName) +{ + if (TxName.empty() || TxName == "none") + { + _UserTexture = false; + nlinfo("Set no texture"); + } + else + { + nlinfo("Set texture to cell : %s", TxName.c_str()); + _UserTexture = true; + _TextureId.setTexture (TxName.c_str (), 0, 0, -1, -1, false); + } +} + +// ---------------------------------------------------------------------------- +void CGroupCell::setTextureTile(bool tiled) +{ + if (tiled) + nlinfo("Set texture is Tiled"); + _TextureTiled = tiled; +} + +// ---------------------------------------------------------------------------- +void CGroupCell::setTextureScale(bool scaled) +{ + if (scaled) + nlinfo("Set texture is Scaled : %s"); + _TextureScaled = scaled; +} + + // ---------------------------------------------------------------------------- NLMISC_REGISTER_OBJECT(CViewBase, CGroupTable, std::string, "table"); diff --git a/code/ryzom/client/src/interface_v3/group_table.h b/code/ryzom/client/src/interface_v3/group_table.h index 08cdad11e..888d6dbe3 100644 --- a/code/ryzom/client/src/interface_v3/group_table.h +++ b/code/ryzom/client/src/interface_v3/group_table.h @@ -80,6 +80,12 @@ public: // The cell color NLMISC::CRGBA BgColor; + // Texture + CViewRenderer::CTextureId _TextureId; /// Accelerator + bool _UserTexture; + bool _TextureTiled; + bool _TextureScaled; + // Alignment TAlign Align; TVAlign VAlign; @@ -90,6 +96,11 @@ public: // The cell is nowrap bool NoWrap; + + void setTexture(const std::string & TxName); + void setTextureTile(bool tiled); + void setTextureScale(bool scaled); + private: void setEnclosedGroupDefaultParams(); }; diff --git a/code/ryzom/client/src/interface_v3/guild_manager.cpp b/code/ryzom/client/src/interface_v3/guild_manager.cpp index c9bb4ec01..8d0066816 100644 --- a/code/ryzom/client/src/interface_v3/guild_manager.cpp +++ b/code/ryzom/client/src/interface_v3/guild_manager.cpp @@ -358,15 +358,54 @@ void CGuildManager::update() // If all is valid no more need update and if guild is opened update the interface if (bAllValid) { + CCDBNodeLeaf* node = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_ONLINE_OFFLINE_NOTIFICATIONS_CB", false); + if (node && node->getValueBool()) + { + // See if we need to show any online/offline messages + static vector CachedGuildMembers; + ucstring onlineMessage = CI18N::get("uiPlayerOnline"); + ucstring offlineMessage = CI18N::get("uiPlayerOffline"); + + for (uint i = 0; i < _GuildMembers.size(); ++i) + { + for (uint j = 0; j < CachedGuildMembers.size(); ++j) + { + if ((CachedGuildMembers[j].Name == _GuildMembers[i].Name) && + (CachedGuildMembers[j].Online != _GuildMembers[i].Online)) + { + ucstring msg = (_GuildMembers[i].Online != ccs_offline) ? onlineMessage : offlineMessage; + strFindReplace(msg, "%s", _GuildMembers[i].Name); + string cat = getStringCategory(msg, msg); + map::const_iterator it; + NLMISC::CRGBA col = CRGBA::Yellow; + it = ClientCfg.SystemInfoParams.find(toLower(cat)); + if (it != ClientCfg.SystemInfoParams.end()) + { + col = it->second.Color; + } + bool dummy; + PeopleInterraction.ChatInput.Guild.displayMessage(msg, col, 2, &dummy); + break; + } + } + } + + CachedGuildMembers.clear(); + for (uint i = 0; i < _GuildMembers.size(); ++i) + { + CachedGuildMembers.push_back(_GuildMembers[i]); + } + } + // Search for UserEntity to find our own grade if ((UserEntity != NULL) && (_GuildMembers.size() > 0)) { uint i; _Grade = EGSPD::CGuildGrade::Member; - string sUserName = strlwr(UserEntity->getEntityName().toString()); + ucstring sUserName = toLower(UserEntity->getEntityName()); for (i = 0; i < _GuildMembers.size(); ++i) { - if (strlwr(_GuildMembers[i].Name.toString()) == sUserName) + if (toLower(_GuildMembers[i].Name) == sUserName) { _Grade = _GuildMembers[i].Grade; break; @@ -1048,10 +1087,26 @@ class CAHGuildSheetSetLeader : public IActionHandler { virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */) { - sendMsgSetGrade(EGSPD::CGuildGrade::Leader); + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + + // Ask if they are sure + pIM->validMessageBox(CInterfaceManager::QuestionIconMsg, CI18N::get("uiQSetLeader"), "guild_member_do_change_leader"); } }; REGISTER_ACTION_HANDLER (CAHGuildSheetSetLeader, "guild_member_chg_to_leader"); + +// *************************************************************************** +class CAHGuildSheetSetLeaderConfirm : public IActionHandler +{ + virtual void execute (CCtrlBase * /* pCaller */, const string &/* Params */) + { + // Leadership change confirmed + sendMsgSetGrade(EGSPD::CGuildGrade::Leader); + } +}; +REGISTER_ACTION_HANDLER (CAHGuildSheetSetLeaderConfirm, "guild_member_do_change_leader"); + + // *************************************************************************** class CAHGuildSheetSetHighOfficer : public IActionHandler diff --git a/code/ryzom/client/src/interface_v3/interface_group.h b/code/ryzom/client/src/interface_v3/interface_group.h index 5079c1899..432176704 100644 --- a/code/ryzom/client/src/interface_v3/interface_group.h +++ b/code/ryzom/client/src/interface_v3/interface_group.h @@ -176,13 +176,17 @@ public: void setRightClickHandlerParams(const std::string ¶ms) { _AHOnRightClickParams = params; } void setOnActiveHandler(const std::string &h) { _AHOnActive = getAH(h,_AHOnActiveParams); } void setOnActiveParams(const std::string &p) { _AHOnActiveParams = p; } - std::string getOnActiveParams() const { return _AHOnActiveParams; } void setOnDeactiveHandler(const std::string &h) { _AHOnDeactive = getAH(h,_AHOnDeactiveParams); } void setOnDeactiveParams(const std::string &p) { _AHOnDeactiveParams = p; } - std::string getOnDeactiveParams() const { return _AHOnDeactiveParams; } const std::string &getLeftClickHandler() const { return getAHName(_AHOnLeftClick); } const std::string &getLeftClickHandlerParams() const { return _AHOnLeftClickParams; } + const std::string &getRightClickHandler() const { return getAHName(_AHOnRightClick); } + const std::string &getRightClickHandlerParams() const { return _AHOnRightClickParams; } + const std::string &getOnActiveHandler() const { return getAHName(_AHOnActive); } + const std::string &getOnActiveParams() const { return _AHOnActiveParams; } + const std::string &getOnDeactiveHandler() const { return getAHName(_AHOnDeactive); } + const std::string &getOnDeactiveParams() const { return _AHOnDeactiveParams; } // find a sub view/ctrl/group in this group from its id int luaFind(CLuaState &ls); @@ -205,8 +209,18 @@ public: REFLECT_LUA_METHOD("delGroup", luaDelGroup); REFLECT_LUA_METHOD("getNumGroups", luaGetNumGroups); REFLECT_LUA_METHOD("getGroup", luaGetGroup); + REFLECT_STRING ("left_click", getLeftClickHandler, setLeftClickHandler); + REFLECT_STRING ("right_click", getRightClickHandler, setRightClickHandler); + REFLECT_STRING ("left_click_params", getLeftClickHandlerParams, setLeftClickHandlerParams); + REFLECT_STRING ("right_click_params", getRightClickHandlerParams, setRightClickHandlerParams); + REFLECT_STRING ("on_active", getOnActiveHandler, setOnActiveHandler); REFLECT_STRING ("on_active_params", getOnActiveParams, setOnActiveParams); + REFLECT_STRING ("on_deactive", getOnDeactiveHandler, setOnDeactiveHandler); REFLECT_STRING ("on_deactive_params", getOnDeactiveParams, setOnDeactiveParams); + REFLECT_STRING ("on_enter", getAHOnEnter, setAHOnEnter); + REFLECT_STRING ("on_enter_params", getAHOnEnterParams, setAHOnEnterParams); + REFLECT_STRING ("on_escape", getAHOnEscape, setAHOnEscape); + REFLECT_STRING ("on_escape_params", getAHOnEscapeParams, setAHOnEscapeParams); REFLECT_SINT32 ("ofsx", getOfsX, setOfsX); REFLECT_SINT32 ("ofsy", getOfsY, setOfsY); REFLECT_BOOL("child_resize_w", getResizeFromChildW, setResizeFromChildW); diff --git a/code/ryzom/client/src/interface_v3/interface_manager.cpp b/code/ryzom/client/src/interface_v3/interface_manager.cpp index 27b8f9197..8e0851cf6 100644 --- a/code/ryzom/client/src/interface_v3/interface_manager.cpp +++ b/code/ryzom/client/src/interface_v3/interface_manager.cpp @@ -824,6 +824,21 @@ void CInterfaceManager::initInGame() { gc->setTarget(gc->getSavedTarget()); } + + CCDBNodeLeaf *node = getDbProp("UI:SAVE:CHATLOG_STATE", false); + if (node) + { + _LogState = (node->getValue32() != 0); + } + + if (_LogState) + { + displaySystemInfo(CI18N::get("uiLogTurnedOn")); + } + else + { + displaySystemInfo(CI18N::get("uiLogTurnedOff")); + } } // ------------------------------------------------------------------------------------------------ @@ -2372,7 +2387,8 @@ void CInterfaceManager::drawContextHelp () if(newCtrl) { // get the text - newCtrl->getContextHelpToolTip(_ContextHelpText); + //newCtrl->getContextHelpToolTip(_ContextHelpText); + newCtrl->getContextHelp(_ContextHelpText); // UserDefined context help if( !newCtrl->getContextHelpActionHandler().empty() ) { @@ -3408,6 +3424,13 @@ CCDBNodeLeaf* CInterfaceManager::getDbProp(const std::string & name, bool bCreat return pDBNL; } +// ------------------------------------------------------------------------------------------------ +void CInterfaceManager::delDbProp(const std::string & name) +{ + if (name.empty()) return; + _DbRootNode->removeNode( ICDBNode::CTextId(name) ); +} + // ------------------------------------------------------------------------------------------------ CCDBNodeBranch *CInterfaceManager::getDbBranch(const std::string &name) { @@ -5759,7 +5782,7 @@ bool CInterfaceManager::executeLuaScript(const std::string &luaScript, bool smal std::string msg = e.luaWhat(); char filename[MAX_PATH]; char exceptionName[MAX_PATH]; - int line; + uint32 line; // Hamster: quick fix on AJM code but sscanf is still awfull if (sscanf(msg.c_str(), "%s: %s.lua:%d:",exceptionName, filename, &line) == 3) // NB: test not exact here, but should work in 99,9 % of cases { @@ -5770,6 +5793,20 @@ bool CInterfaceManager::executeLuaScript(const std::string &luaScript, bool smal else // AJM: handle the other 0.1% of cases { // Yoyo: seems that previous test doesn't work.... btw, must still print the message please... + std::vector error; + splitString(msg.c_str(), ":", error); + if (error.size() > 3) + { + std::vector contextList; + explode(luaScript, string("\n"), contextList); + fromString(error[2], line); + if (line >= 3 && contextList.size() >= line) + msg = error[0]+": \n>>> "+contextList[line-3]+"\n>>> "+contextList[line-2]+"\n>>> "+contextList[line-1]+"\nError:"+error[2]+": "+error[3]; + else if (line >= 2 && contextList.size() >= line) + msg = error[0]+": \n>>>"+contextList[line-2]+"\n>>>"+contextList[line-1]+"\nError:"+error[2]+": "+error[3]; + else if (line >= 1 && contextList.size() >= line) + msg = error[0]+": \n>>>"+contextList[line-1]+"\nError:"+error[2]+": "+error[3]; + } nlwarning(formatLuaErrorNlWarn(msg).c_str()); displaySystemInfo(formatLuaErrorSysInfo(msg)); } diff --git a/code/ryzom/client/src/interface_v3/interface_manager.h b/code/ryzom/client/src/interface_v3/interface_manager.h index ced23130f..7ee6d1951 100644 --- a/code/ryzom/client/src/interface_v3/interface_manager.h +++ b/code/ryzom/client/src/interface_v3/interface_manager.h @@ -232,6 +232,7 @@ public: CCDBNodeBranch *getDB() const { return _DbRootNode; } // yoyo: should avoid to try creating DbPropr with this system... very dangerous CCDBNodeLeaf* getDbProp (const std::string & name, bool bCreate=true); + void delDbProp(const std::string & name); // get a Db Branch by its name. NULL if don't exist or not a branch (never try to create it) CCDBNodeBranch *getDbBranch(const std::string &name); // return the DB as an int32. return 0 if the DB does not exist (never create) diff --git a/code/ryzom/client/src/interface_v3/interface_parser.cpp b/code/ryzom/client/src/interface_v3/interface_parser.cpp index 56b95be88..109b3d5cd 100644 --- a/code/ryzom/client/src/interface_v3/interface_parser.cpp +++ b/code/ryzom/client/src/interface_v3/interface_parser.cpp @@ -517,7 +517,7 @@ bool CInterfaceParser::parseInterface (const std::vector & strings, { bool ok; - bool needCheck = checkInData; + bool needCheck = false; #if !FINAL_VERSION needCheck = false; @@ -554,10 +554,12 @@ bool CInterfaceParser::parseInterface (const std::vector & strings, string::size_type pos = filename.find ("@"); if (pos != string::npos) { - std::string bigFilename = CBigFile::getInstance().getBigFileName(filename.substr(0, pos)); - std::string path = "data/"+filename.substr(0, pos); - - isInData = bigFilename.find(path) != std::string::npos; + vector bigFilePaths; + CBigFile::getInstance().getBigFilePaths(bigFilePaths); + if (CBigFile::getInstance().getBigFileName(filename.substr(0, pos)) != "data/"+filename.substr(0, pos)) + isInData = false; + else + isInData = true; } if ((needCheck && !isInData) || !file.open (CPath::lookup(firstFileName))) @@ -4685,7 +4687,7 @@ bool CInterfaceParser::loadLUA(const std::string &fileName, std::string &error) { // get file - bool needCheck = true; + bool needCheck = false; #if !FINAL_VERSION needCheck = false; @@ -4702,10 +4704,10 @@ bool CInterfaceParser::loadLUA(const std::string &fileName, std::string &error) std::string::size_type pos = pathName.find("@"); if (pos != string::npos) { - std::string bigFilename = CBigFile::getInstance().getBigFileName(pathName.substr(0, pos)); - std::string path = "data/"+pathName.substr(0, pos); - - isInData = bigFilename.find(path) != std::string::npos; + if (CBigFile::getInstance().getBigFileName(pathName.substr(0, pos)) != "data/"+pathName.substr(0, pos)) + isInData = false; + else + isInData = true; } if (needCheck && !isInData) diff --git a/code/ryzom/client/src/interface_v3/inventory_manager.cpp b/code/ryzom/client/src/interface_v3/inventory_manager.cpp index 9d20c0661..421076364 100644 --- a/code/ryzom/client/src/interface_v3/inventory_manager.cpp +++ b/code/ryzom/client/src/interface_v3/inventory_manager.cpp @@ -44,12 +44,14 @@ #include "../sheet_manager.h" #include "game_share/slot_equipment.h" #include "game_share/animal_status.h" +#include "game_share/bot_chat_types.h" #include "../client_cfg.h" using namespace std; using namespace NLMISC; +extern NLMISC::CLog g_log; // Context help extern void contextHelp (const std::string &help); @@ -131,6 +133,7 @@ void CItemImage::build(CCDBNodeBranch *branch) Weight = dynamic_cast(branch->getNode(ICDBNode::CTextId("WEIGHT"), false)); NameId = dynamic_cast(branch->getNode(ICDBNode::CTextId("NAMEID"), false)); InfoVersion= dynamic_cast(branch->getNode(ICDBNode::CTextId("INFO_VERSION"), false)); + ResaleFlag = dynamic_cast(branch->getNode(ICDBNode::CTextId("RESALE_FLAG"), false)); // Should always have at least those one:(ie all but Price) nlassert(Sheet && Quality && Quantity && UserColor && Weight && NameId && InfoVersion); @@ -1998,6 +2001,9 @@ bool SBagOptions::parse(xmlNodePtr cur, CInterfaceGroup * /* parentGroup */) prop = xmlGetProp (cur, (xmlChar*)"filter_missmp"); if (prop) DbFilterMissMP = pIM->getDbProp(prop); + prop = xmlGetProp (cur, (xmlChar*)"filter_tp"); + if (prop) DbFilterTP = pIM->getDbProp(prop); + return true; } @@ -2041,6 +2047,13 @@ bool SBagOptions::isSomethingChanged() LastDbFilterMissMP = (DbFilterMissMP->getValue8() != 0); } + if (DbFilterTP != NULL) + if ((DbFilterTP->getValue8() != 0) != LastDbFilterTP) + { + bRet = true; + LastDbFilterTP = (DbFilterTP->getValue8() != 0); + } + return bRet; } @@ -2054,25 +2067,33 @@ bool SBagOptions::canDisplay(CDBCtrlSheet *pCS) const bool bFilterTool = getFilterTool(); bool bFilterMP = getFilterMP(); bool bFilterMissMP = getFilterMissMP(); + bool bFilterTP = getFilterTP(); const CItemSheet *pIS = pCS->asItemSheet(); if (pIS != NULL) { // Armor - if ((pIS->Family == ITEMFAMILY::ARMOR) || (pIS->Family == ITEMFAMILY::JEWELRY)) + if ((pIS->Family == ITEMFAMILY::ARMOR) || + (pIS->Family == ITEMFAMILY::JEWELRY)) if (!bFilterArmor) bDisplay = false; // Weapon - if ((pIS->Family == ITEMFAMILY::SHIELD) || (pIS->Family == ITEMFAMILY::MELEE_WEAPON) || - (pIS->Family == ITEMFAMILY::RANGE_WEAPON) || (pIS->Family == ITEMFAMILY::AMMO) || - (pIS->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) || (pIS->Family == ITEMFAMILY::ITEM_SAP_RECHARGE) || + if ((pIS->Family == ITEMFAMILY::SHIELD) || + (pIS->Family == ITEMFAMILY::MELEE_WEAPON) || + (pIS->Family == ITEMFAMILY::RANGE_WEAPON) || + (pIS->Family == ITEMFAMILY::AMMO) || + (pIS->Family == ITEMFAMILY::CRYSTALLIZED_SPELL) || + (pIS->Family == ITEMFAMILY::ITEM_SAP_RECHARGE) || (pIS->Family == ITEMFAMILY::BRICK) ) if (!bFilterWeapon) bDisplay = false; // Tool - if ((pIS->Family == ITEMFAMILY::CRAFTING_TOOL) || (pIS->Family == ITEMFAMILY::HARVEST_TOOL) || - (pIS->Family == ITEMFAMILY::TAMING_TOOL) || (pIS->Family == ITEMFAMILY::TRAINING_TOOL) || - (pIS->Family == ITEMFAMILY::BAG) || (pIS->Family == ITEMFAMILY::PET_ANIMAL_TICKET) ) + if ((pIS->Family == ITEMFAMILY::CRAFTING_TOOL) || + (pIS->Family == ITEMFAMILY::HARVEST_TOOL) || + (pIS->Family == ITEMFAMILY::TAMING_TOOL) || + (pIS->Family == ITEMFAMILY::TRAINING_TOOL) || + (pIS->Family == ITEMFAMILY::BAG) || + (pIS->Family == ITEMFAMILY::PET_ANIMAL_TICKET) ) if (!bFilterTool) bDisplay = false; // MP @@ -2081,9 +2102,15 @@ bool SBagOptions::canDisplay(CDBCtrlSheet *pCS) const // Mission MP if ((pIS->Family == ITEMFAMILY::MISSION_ITEM) || + (pIS->Family == ITEMFAMILY::XP_CATALYSER) || + (pIS->Family == ITEMFAMILY::CONSUMABLE) || ((pIS->Family == ITEMFAMILY::RAW_MATERIAL) && !pIS->canBuildSomeItemPart())) if (!bFilterMissMP) bDisplay = false; - + + // Teleporter Pacts + if ((pIS->Family == ITEMFAMILY::TELEPORT)) + if (!bFilterTP) bDisplay = false; + // Jobs Items if (pIS->Id.toString().substr(0, 6) == "rpjob_") bDisplay = false; @@ -2776,6 +2803,37 @@ public: REGISTER_ACTION_HANDLER( CHandlerInvAutoEquip, "inv_auto_equip" ); +// ********************************************************************************************************** +class CHandlerLockInvItem : public IActionHandler +{ + void execute (CCtrlBase *pCaller, const std::string &sParams) + { + // get the calling item + CDBCtrlSheet *item = CDBCtrlSheet::getCurrSelSheet(); + if ( ! item) + { + nlwarning(" no caller sheet found"); + return; + } + + string lock = "1"; + if (item->getLockedByOwner()) + { + lock = "0"; + } + + uint32 slot = item->getIndexInDB(); + uint32 inv = item->getInventoryIndex(); + INVENTORIES::TInventory inventory = INVENTORIES::UNDEFINED; + inventory = (INVENTORIES::TInventory)(inv); + if (inventory == INVENTORIES::UNDEFINED) + { + return; + } + NLMISC::ICommand::execute("a lockItem " + INVENTORIES::toString(inventory) + " " + toString(slot) + " " + lock, g_log); + } +}; +REGISTER_ACTION_HANDLER( CHandlerLockInvItem, "lock_inv_item" ); // *************************************************************************** // Inventory Temporary @@ -2844,11 +2902,11 @@ class CHandlerInvTempAll : public IActionHandler nlctassert(MAX_INVENTORY_ANIMAL==4); if (pInv->isInventoryAvailable(INVENTORIES::pet_animal1)) - BagsBulk.push_back(pair (pInv->getBagBulk(2), pInv->getMaxBagBulk(2))); + BagsBulk.push_back(pair (pInv->getBagBulk(1), pInv->getMaxBagBulk(1))); if (pInv->isInventoryAvailable(INVENTORIES::pet_animal2)) - BagsBulk.push_back(pair (pInv->getBagBulk(3), pInv->getMaxBagBulk(3))); + BagsBulk.push_back(pair (pInv->getBagBulk(2), pInv->getMaxBagBulk(2))); if (pInv->isInventoryAvailable(INVENTORIES::pet_animal3)) - BagsBulk.push_back(pair (pInv->getBagBulk(4), pInv->getMaxBagBulk(4))); + BagsBulk.push_back(pair (pInv->getBagBulk(3), pInv->getMaxBagBulk(3))); if (pInv->isInventoryAvailable(INVENTORIES::pet_animal4)) BagsBulk.push_back(pair (pInv->getBagBulk(4), pInv->getMaxBagBulk(4))); diff --git a/code/ryzom/client/src/interface_v3/inventory_manager.h b/code/ryzom/client/src/interface_v3/inventory_manager.h index a5c332ba7..12de9ad98 100644 --- a/code/ryzom/client/src/interface_v3/inventory_manager.h +++ b/code/ryzom/client/src/interface_v3/inventory_manager.h @@ -25,6 +25,7 @@ #include "game_share/item_infos.h" #include "game_share/temp_inventory_mode.h" #include "game_share/inventories.h" +#include "game_share/bot_chat_types.h" class CCDBNodeBranch; class CDBCtrlSheet; @@ -62,6 +63,7 @@ public: CCDBNodeLeaf *Weight; CCDBNodeLeaf *NameId; CCDBNodeLeaf *InfoVersion; + CCDBNodeLeaf *ResaleFlag; public: // ctor @@ -77,6 +79,8 @@ public: uint32 getWeight() const { return (uint32) (Weight ? Weight->getValue32() : 0); } uint32 getNameId() const { return (uint32) (NameId ? NameId->getValue32() : 0); } uint8 getInfoVersion() const { return (uint8) (InfoVersion ? (uint8) InfoVersion->getValue8() : 0); } + uint8 getResaleFlag() const { return (uint8) (ResaleFlag ? (uint8) ResaleFlag->getValue8() : 0); } + bool getLockedByOwner() const { return (bool) (ResaleFlag ? (ResaleFlag->getValue8() == BOTCHATTYPE::ResaleKOLockedByOwner) : false); } // void setSheetID(uint32 si) { if (Sheet) Sheet->setValue32((sint32) si); } void setQuality(uint16 quality) { if (Quality) Quality->setValue16((sint16) quality); } @@ -86,6 +90,7 @@ public: void setWeight(uint32 wgt) { if (Weight) Weight->setValue32((sint32) wgt); } void setNameId(uint32 nid) { if (NameId) NameId->setValue32((sint32) nid); } void setInfoVersion(uint8 iv) { if (InfoVersion) InfoVersion->setValue8((sint8) iv); } + void setResaleFlag(uint8 resale) { if (ResaleFlag) ResaleFlag->setValue8(resale); } }; @@ -504,18 +509,20 @@ struct SBagOptions CCDBNodeLeaf *DbFilterTool; CCDBNodeLeaf *DbFilterMP; CCDBNodeLeaf *DbFilterMissMP; + CCDBNodeLeaf *DbFilterTP; bool LastDbFilterArmor; bool LastDbFilterWeapon; bool LastDbFilterTool; bool LastDbFilterMP; bool LastDbFilterMissMP; + bool LastDbFilterTP; // ----------------------- SBagOptions() { InvType = CInventoryManager::InvUnknown; - DbFilterArmor = DbFilterWeapon = DbFilterTool = DbFilterMP = DbFilterMissMP = NULL; - LastDbFilterArmor = LastDbFilterWeapon = LastDbFilterTool = LastDbFilterMP = LastDbFilterMissMP = false; + DbFilterArmor = DbFilterWeapon = DbFilterTool = DbFilterMP = DbFilterMissMP = DbFilterTP = NULL; + LastDbFilterArmor = LastDbFilterWeapon = LastDbFilterTool = LastDbFilterMP = LastDbFilterMissMP = LastDbFilterTP = false; } bool parse (xmlNodePtr cur, CInterfaceGroup *parentGroup); @@ -552,6 +559,12 @@ struct SBagOptions return (DbFilterMissMP->getValue8()!=0); } + bool getFilterTP() const + { + if (DbFilterTP == NULL) return true; + return (DbFilterTP->getValue8() != 0); + } + // Return true if the sheet can be displayed due to filters bool canDisplay(CDBCtrlSheet *pCS) const; }; diff --git a/code/ryzom/client/src/interface_v3/lua_ihm.cpp b/code/ryzom/client/src/interface_v3/lua_ihm.cpp index 15dd9872a..44e9a3ba7 100644 --- a/code/ryzom/client/src/interface_v3/lua_ihm.cpp +++ b/code/ryzom/client/src/interface_v3/lua_ihm.cpp @@ -143,7 +143,7 @@ extern NLMISC::CLog g_log; extern CContinentManager ContinentMngr; extern uint8 PlayerSelectedSlot; extern CClientChatManager ChatMngr; -extern void addWebIGParams (string &url); +extern void addWebIGParams (string &url, bool trustedDomain); // declare ostream << operator for ucstring -> registration of ucstring iin luabind will build a 'tostring' function from it std::ostream &operator<<(std::ostream &str, const ucstring &value) @@ -1323,6 +1323,7 @@ void CLuaIHM::registerIHM(CLuaState &ls) ls.registerFunc("getIndexInDB", getIndexInDB); ls.registerFunc("getUIId", getUIId); ls.registerFunc("createGroupInstance", createGroupInstance); + ls.registerFunc("createRootGroupInstance", createRootGroupInstance); ls.registerFunc("createUIElement", createUIElement); ls.registerFunc("launchContextMenuInGame", launchContextMenuInGame); ls.registerFunc("parseInterfaceFromString", parseInterfaceFromString); @@ -1356,6 +1357,19 @@ void CLuaIHM::registerIHM(CLuaState &ls) ls.registerFunc("enableModalWindow", enableModalWindow); ls.registerFunc("disableModalWindow", disableModalWindow); ls.registerFunc("getPlayerPos", getPlayerPos); + ls.registerFunc("getPlayerFront", getPlayerFront); + ls.registerFunc("getPlayerDirection", getPlayerDirection); + ls.registerFunc("getPlayerGender", getPlayerGender); + ls.registerFunc("getPlayerName", getPlayerName); + ls.registerFunc("getPlayerTitleRaw", getPlayerTitleRaw); + ls.registerFunc("getPlayerTitle", getPlayerTitle); + ls.registerFunc("getTargetPos", getTargetPos); + ls.registerFunc("getTargetFront", getTargetFront); + ls.registerFunc("getTargetDirection", getTargetDirection); + ls.registerFunc("getTargetGender", getTargetGender); + ls.registerFunc("getTargetName", getTargetName); + ls.registerFunc("getTargetTitleRaw", getTargetTitleRaw); + ls.registerFunc("getTargetTitle", getTargetTitle); ls.registerFunc("addSearchPathUser", addSearchPathUser); ls.registerFunc("displaySystemInfo", displaySystemInfo); ls.registerFunc("disableContextHelpForControl", disableContextHelpForControl); @@ -1363,6 +1377,7 @@ void CLuaIHM::registerIHM(CLuaState &ls) ls.registerFunc("setWeatherValue", setWeatherValue); ls.registerFunc("getWeatherValue", getWeatherValue); ls.registerFunc("getCompleteIslands", getCompleteIslands); + ls.registerFunc("displayBubble", displayBubble); ls.registerFunc("getIslandId", getIslandId); ls.registerFunc("getClientCfgVar", getClientCfgVar); ls.registerFunc("isPlayerFreeTrial", isPlayerFreeTrial); @@ -1370,14 +1385,18 @@ void CLuaIHM::registerIHM(CLuaState &ls) ls.registerFunc("isInRingMode", isInRingMode); ls.registerFunc("getUserRace", getUserRace); ls.registerFunc("getSheet2idx", getSheet2idx); + ls.registerFunc("getTargetSlot", getTargetSlot); + ls.registerFunc("getSlotDataSetId", getSlotDataSetId); // Through LUABind API lua_State *L= ls.getStatePointer(); luabind::module(L) [ + LUABIND_FUNC(addDbProp), LUABIND_FUNC(getDbProp), LUABIND_FUNC(setDbProp), + LUABIND_FUNC(delDbProp), LUABIND_FUNC(debugInfo), LUABIND_FUNC(rawDebugInfo), LUABIND_FUNC(dumpCallStack), @@ -1456,9 +1475,16 @@ void CLuaIHM::registerIHM(CLuaState &ls) luabind::def("shellExecute", CMiscFunctions::shellExecute), LUABIND_FUNC(getPlayerLevel), + LUABIND_FUNC(getPlayerVpa), + LUABIND_FUNC(getPlayerVpb), + LUABIND_FUNC(getPlayerVpc), LUABIND_FUNC(getTargetLevel), LUABIND_FUNC(getTargetForceRegion), LUABIND_FUNC(getTargetLevelForce), + LUABIND_FUNC(getTargetSheet), + LUABIND_FUNC(getTargetVpa), + LUABIND_FUNC(getTargetVpb), + LUABIND_FUNC(getTargetVpc), LUABIND_FUNC(isTargetNPC), LUABIND_FUNC(isTargetPlayer), // return 'true' if the target is an npc LUABIND_FUNC(isTargetUser), @@ -1686,12 +1712,60 @@ void CLuaIHM::setDbProp(const std::string &dbProp, sint32 value) // Write to the DB if found CInterfaceManager *pIM= CInterfaceManager::getInstance(); CCDBNodeLeaf *node= pIM->getDbProp(dbProp, false); + if(node) node->setValue32(value); else debugInfo(toString("setDbProp(): '%s' dbProp Not found", dbProp.c_str())); } +void CLuaIHM::delDbProp(const string &dbProp) +{ + //H_AUTO(Lua_CLuaIHM_setDbProp) + // Do not allow Write on SERVER: or LOCAL: + static const string dbServer= "SERVER:"; + static const string dbLocal= "LOCAL:"; + static const string dbLocalR2= "LOCAL:R2"; + if( (0==dbProp.compare(0, dbServer.size(), dbServer)) || + (0==dbProp.compare(0, dbLocal.size(), dbLocal)) + ) + { + if (0!=dbProp.compare(0, dbLocalR2.size(), dbLocalR2)) + { + nlstop; + throw ELuaIHMException("setDbProp(): You are not allowed to write on 'SERVER:...' or 'LOCAL:...' database"); + } + } + + // Write to the DB if found + CInterfaceManager *pIM= CInterfaceManager::getInstance(); + pIM->delDbProp(dbProp); +} + +void CLuaIHM::addDbProp(const std::string &dbProp, sint32 value) +{ + //H_AUTO(Lua_CLuaIHM_setDbProp) + // Do not allow Write on SERVER: or LOCAL: + static const std::string dbServer= "SERVER:"; + static const std::string dbLocal= "LOCAL:"; + static const std::string dbLocalR2= "LOCAL:R2"; + if( (0==dbProp.compare(0, dbServer.size(), dbServer)) || + (0==dbProp.compare(0, dbLocal.size(), dbLocal)) + ) + { + if (0!=dbProp.compare(0, dbLocalR2.size(), dbLocalR2)) + { + nlstop; + throw ELuaIHMException("setDbProp(): You are not allowed to write on 'SERVER:...' or 'LOCAL:...' database"); + } + } + + // Write to the DB if found + CInterfaceManager *pIM= CInterfaceManager::getInstance(); + CCDBNodeLeaf *node= pIM->getDbProp(dbProp, true); + if(node) + node->setValue32(value); +} // *************************************************************************** void CLuaIHM::debugInfo(const std::string &cstDbg) @@ -1865,7 +1939,21 @@ std::string CLuaIHM::getDefine(const std::string &def) // *************************************************************************** -static CEntityCL *getTargetSlot() +static sint32 getTargetSlotNr() +{ + const char *dbPath = "UI:VARIABLES:TARGET:SLOT"; + CInterfaceManager *im = CInterfaceManager::getInstance(); + CCDBNodeLeaf *node = im->getDbProp(dbPath, false); + if (!node) return NULL; + if ((uint8) node->getValue32() == (uint8) CLFECOMMON::INVALID_SLOT) + { + return NULL; + } + return node->getValue32(); +} + +// *************************************************************************** +static CEntityCL *getTargetEntity() { const char *dbPath = "UI:VARIABLES:TARGET:SLOT"; CInterfaceManager *im = CInterfaceManager::getInstance(); @@ -1878,6 +1966,12 @@ static CEntityCL *getTargetSlot() return EntitiesMngr.entity((uint) node->getValue32()); } +// *************************************************************************** +static CEntityCL *getSlotEntity(uint slot) +{ + return EntitiesMngr.entity(slot); +} + // *************************************************************************** sint32 CLuaIHM::getPlayerLevel() { @@ -1890,10 +1984,31 @@ sint32 CLuaIHM::getPlayerLevel() return sint32(maxskill); } +// *************************************************************************** +sint64 CLuaIHM::getPlayerVpa() +{ + sint64 prop = CInterfaceManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64(); + return prop; +} + +// *************************************************************************** +sint64 CLuaIHM::getPlayerVpb() +{ + sint64 prop = CInterfaceManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->getValue64(); + return prop; +} + +// *************************************************************************** +sint64 CLuaIHM::getPlayerVpc() +{ + sint64 prop = CInterfaceManager::getInstance()->getDbProp("SERVER:Entities:E0:P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->getValue64(); + return prop; +} + // *************************************************************************** sint32 CLuaIHM::getTargetLevel() { - CEntityCL *target = getTargetSlot(); + CEntityCL *target = getTargetEntity(); if (!target) return -1; if ( target->isPlayer() ) { @@ -1913,10 +2028,52 @@ sint32 CLuaIHM::getTargetLevel() return -1; } +// *************************************************************************** +ucstring CLuaIHM::getTargetSheet() +{ + CEntityCL *target = getTargetEntity(); + if (!target) return ""; + + return target->sheetId().toString(); +} + +// *************************************************************************** +sint64 CLuaIHM::getTargetVpa() +{ + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + + sint64 prop = CInterfaceManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", getTargetSlotNr())+":P"+toString("%d", CLFECOMMON::PROPERTY_VPA))->getValue64(); + + return prop; +} + +// *************************************************************************** +sint64 CLuaIHM::getTargetVpb() +{ + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + + sint64 prop = CInterfaceManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", getTargetSlotNr())+":P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->getValue64(); + + return prop; +} + +// *************************************************************************** +sint64 CLuaIHM::getTargetVpc() +{ + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + + sint64 prop = CInterfaceManager::getInstance()->getDbProp("SERVER:Entities:E"+toString("%d", getTargetSlotNr())+":P"+toString("%d", CLFECOMMON::PROPERTY_VPB))->getValue64(); + + return prop; +} + // *************************************************************************** sint32 CLuaIHM::getTargetForceRegion() { - CEntityCL *target = getTargetSlot(); + CEntityCL *target = getTargetEntity(); if (!target) return -1; if ( target->isPlayer() ) { @@ -1944,7 +2101,7 @@ sint32 CLuaIHM::getTargetForceRegion() // *************************************************************************** sint32 CLuaIHM::getTargetLevelForce() { - CEntityCL *target = getTargetSlot(); + CEntityCL *target = getTargetEntity(); if (!target) return -1; if ( target->isPlayer() ) { @@ -1972,7 +2129,7 @@ sint32 CLuaIHM::getTargetLevelForce() // *************************************************************************** bool CLuaIHM::isTargetNPC() { - CEntityCL *target = getTargetSlot(); + CEntityCL *target = getTargetEntity(); if (!target) return false; return target->isNPC(); } @@ -1980,7 +2137,7 @@ bool CLuaIHM::isTargetNPC() // *************************************************************************** bool CLuaIHM::isTargetPlayer() { - CEntityCL *target = getTargetSlot(); + CEntityCL *target = getTargetEntity(); if (!target) return false; return target->isPlayer(); } @@ -1989,7 +2146,7 @@ bool CLuaIHM::isTargetPlayer() // *************************************************************************** bool CLuaIHM::isTargetUser() { - CEntityCL *target = getTargetSlot(); + CEntityCL *target = getTargetEntity(); if (!target) return false; return target->isUser(); } @@ -1998,15 +2155,15 @@ bool CLuaIHM::isTargetUser() bool CLuaIHM::isPlayerInPVPMode() { if (!UserEntity) return false; - return (UserEntity->getPvpMode() & PVP_MODE::PvpFaction || UserEntity->getPvpMode() & PVP_MODE::PvpFactionFlagged || UserEntity->getPvpMode() & PVP_MODE::PvpZoneFaction); + return (UserEntity->getPvpMode() & PVP_MODE::PvpFaction || UserEntity->getPvpMode() & PVP_MODE::PvpFactionFlagged || UserEntity->getPvpMode() & PVP_MODE::PvpZoneFaction) != 0; } // *************************************************************************** bool CLuaIHM::isTargetInPVPMode() { - CEntityCL *target = getTargetSlot(); + CEntityCL *target = getTargetEntity(); if (!target) return false; - return (target->getPvpMode() & PVP_MODE::PvpFaction || target->getPvpMode() & PVP_MODE::PvpFactionFlagged || target->getPvpMode() & PVP_MODE::PvpZoneFaction); + return (target->getPvpMode() & PVP_MODE::PvpFaction || target->getPvpMode() & PVP_MODE::PvpFactionFlagged || target->getPvpMode() & PVP_MODE::PvpZoneFaction) != 0; } // *************************************************************************** @@ -2368,6 +2525,53 @@ int CLuaIHM::createGroupInstance(CLuaState &ls) return 1; } +// *************************************************************************** +int CLuaIHM::createRootGroupInstance(CLuaState &ls) +{ + //H_AUTO(Lua_CLuaIHM_createGroupInstance) + const char *funcName = "createRootGroupInstance"; + CLuaIHM::checkArgCount(ls, funcName, 3); + CLuaIHM::checkArgType(ls, funcName, 1, LUA_TSTRING); + CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); + CLuaIHM::checkArgType(ls, funcName, 3, LUA_TTABLE); + std::vector > templateParams; + CLuaObject params; + params.pop(ls); + ENUM_LUA_TABLE(params, it) + { + if (!it.nextKey().isString()) + { + nlwarning("%s : bad key encountered with type %s, string expected.", funcName, it.nextKey().getTypename()); + continue; + } + if (!it.nextValue().isString()) + { + nlwarning("%s : bad value encountered with type %s for key %s, string expected.", funcName, it.nextValue().getTypename(), it.nextKey().toString().c_str()); + continue; + } + templateParams.push_back(std::pair(it.nextKey().toString(), it.nextValue().toString())); // strange compilation bug here when I use std::make_pair ... :( + } + CInterfaceManager *im = CInterfaceManager::getInstance(); + CInterfaceGroup *result = im->createGroupInstance(ls.toString(1), "ui:interface:"+string(ls.toString(2)), templateParams); + if (!result) + { + ls.pushNil(); + } + else + { + result->setId("ui:interface:"+string(ls.toString(2))); + result->updateCoords(); + im->addWindowToMasterGroup("ui:interface", result); + CInterfaceGroup *pRoot = dynamic_cast(im->getElementFromId("ui:interface")); + result->setParent(pRoot); + if (pRoot) + pRoot->addGroup(result); + result->setActive(true); + CLuaIHM::pushUIOnStack(ls, result); + } + return 1; +} + // *************************************************************************** int CLuaIHM::createUIElement(CLuaState &ls) { @@ -2407,6 +2611,42 @@ int CLuaIHM::createUIElement(CLuaState &ls) return 1; } + +// *************************************************************************** +int CLuaIHM::displayBubble(CLuaState &ls) +{ + //H_AUTO(Lua_CLuaIHM_createUIElement) + const char *funcName = "displayBubble"; + CLuaIHM::checkArgCount(ls, funcName, 3); + CLuaIHM::checkArgType(ls, funcName, 1, LUA_TNUMBER); + CLuaIHM::checkArgType(ls, funcName, 2, LUA_TSTRING); + CLuaIHM::checkArgType(ls, funcName, 3, LUA_TTABLE); + std::vector strs; + std::vector links; + CLuaObject params; + params.pop(ls); + ENUM_LUA_TABLE(params, it) + { + if (!it.nextKey().isString()) + { + nlwarning("%s : bad key encountered with type %s, string expected.", funcName, it.nextKey().getTypename()); + continue; + } + if (!it.nextValue().isString()) + { + nlwarning("%s : bad value encountered with type %s for key %s, string expected.", funcName, it.nextValue().getTypename(), it.nextKey().toString().c_str()); + continue; + } + links.push_back(it.nextValue().toString()); + strs.push_back(it.nextKey().toString()); + } + + InSceneBubbleManager.webIgChatOpen((uint32)ls.toNumber(1), ls.toString(2), strs, links); + + return 1; +} + + // *************************************************************************** int CLuaIHM::getCompleteIslands(CLuaState &ls) { @@ -3523,7 +3763,7 @@ void CLuaIHM::browseNpcWebPage(const std::string &htmlId, const std::string &url string("&lang=") + ClientCfg.getHtmlLanguageCode() + string("&guild_name=") + guildName; } -/* +/* Already added by GroupHtml if(webig) { // append special webig auth params @@ -4242,6 +4482,126 @@ int CLuaIHM::getPlayerPos(CLuaState &ls) return 3; } +// *************************************************************************** +int CLuaIHM::getPlayerFront(CLuaState &ls) +{ + checkArgCount(ls, "getPlayerFront", 0); + ls.push(atan2(UserEntity->front().y, UserEntity->front().x)); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getPlayerDirection(CLuaState &ls) +{ + checkArgCount(ls, "getPlayerDirection", 0); + ls.push(atan2(UserEntity->dir().y, UserEntity->dir().x)); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getPlayerGender(CLuaState &ls) +{ + checkArgCount(ls, "getPlayerGender", 0); + ls.push((lua_Number)(UserEntity->getGender())); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getPlayerName(CLuaState &ls) +{ + checkArgCount(ls, "getPlayerName", 0); + ls.push(UserEntity->getEntityName().toUtf8()); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getPlayerTitleRaw(CLuaState &ls) +{ + checkArgCount(ls, "getPlayerTitleRaw", 0); + ls.push(UserEntity->getTitleRaw().toUtf8()); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getPlayerTitle(CLuaState &ls) +{ + checkArgCount(ls, "getPlayerTitle", 0); + ls.push(UserEntity->getTitle().toUtf8()); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getTargetPos(CLuaState &ls) +{ + checkArgCount(ls, "getTargetPos", 0); + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + ls.push(target->pos().x); + ls.push(target->pos().y); + ls.push(target->pos().z); + return 3; +} + +// *************************************************************************** +int CLuaIHM::getTargetFront(CLuaState &ls) +{ + checkArgCount(ls, "getTargetFront", 0); + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + ls.push(atan2(target->front().y, target->front().x)); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getTargetDirection(CLuaState &ls) +{ + checkArgCount(ls, "getTargetDirection", 0); + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + ls.push(atan2(target->dir().y, target->dir().x)); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getTargetGender(CLuaState &ls) +{ + checkArgCount(ls, "getTargetGender", 0); + CCharacterCL* target = (CCharacterCL*)getTargetEntity(); + if (!target) return (int)GSGENDER::unknown; + ls.push((lua_Number)(target->getGender())); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getTargetName(CLuaState &ls) +{ + checkArgCount(ls, "getTargetName", 0); + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + ls.push(target->getEntityName().toUtf8()); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getTargetTitleRaw(CLuaState &ls) +{ + checkArgCount(ls, "getTargetTitleRaw", 0); + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + ls.push(target->getTitleRaw().toUtf8()); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getTargetTitle(CLuaState &ls) +{ + checkArgCount(ls, "getTargetTitle", 0); + CEntityCL *target = getTargetEntity(); + if (!target) return 0; + ls.push(target->getTitle().toUtf8()); + return 1; +} + // *************************************************************************** int CLuaIHM::addSearchPathUser(CLuaState &ls) { @@ -4437,3 +4797,23 @@ int CLuaIHM::getSheet2idx(CLuaState &ls) return 1; } +// *************************************************************************** +int CLuaIHM::getTargetSlot(CLuaState &ls) +{ + uint32 slot = (uint32)getTargetSlotNr(); + ls.push((lua_Number)slot); + return 1; +} + +// *************************************************************************** +int CLuaIHM::getSlotDataSetId(CLuaState &ls) +{ + CLuaIHM::checkArgCount(ls, "getSlotDataSetId", 1); + CLuaIHM::checkArgType(ls, "getSlotDataSetId", 1, LUA_TNUMBER); + + uint32 slot = (uint32)ls.toNumber(1); + CEntityCL *e = getSlotEntity(slot); + string id = toString(e->dataSetId()); + ls.push(id); + return 1; +} diff --git a/code/ryzom/client/src/interface_v3/lua_ihm.h b/code/ryzom/client/src/interface_v3/lua_ihm.h index 6a11a5c91..12d8b5459 100644 --- a/code/ryzom/client/src/interface_v3/lua_ihm.h +++ b/code/ryzom/client/src/interface_v3/lua_ihm.h @@ -185,9 +185,16 @@ private: // LUA exported Functions with luabind static sint32 getPlayerLevel(); // get max level among player skills (magi, combat, crafting ,foraging) + static sint64 getPlayerVpa(); + static sint64 getPlayerVpb(); + static sint64 getPlayerVpc(); static sint32 getTargetLevel(); // get current, precise level of the selected target, or -1 if there's no such selected target static sint32 getTargetForceRegion(); // get 'force region' for current target, or -1 if there's no selected target - static sint32 getTargetLevelForce(); // get 'level force' for current target, or -1 if there's no selected target + static sint32 getTargetLevelForce(); // get 'level force' for current target, or -1 if there's no selected target + static ucstring getTargetSheet(); // get the name of the target sheet (like 'zoha2old.creature') + static sint64 getTargetVpa(); + static sint64 getTargetVpb(); + static sint64 getTargetVpc(); static bool isTargetNPC(); // return 'true' if the target is an npc static bool isTargetPlayer(); // return 'true' if the target is a player static bool isTargetUser(); // return 'true' if the target is the user @@ -202,10 +209,12 @@ private: static bool isInGame(); static uint32 getPlayerSelectedSlot(); static bool isPlayerSlotNewbieLand(uint32 slot); // test if one of the player slot is a newbieland one, if not so, client must be patched in order to continue - static uint32 getLocalTime(); + static uint32 getLocalTime(); static double getPreciseLocalTime(); static sint32 getDbProp(const std::string &dbProp); // return 0 if not found. static void setDbProp(const std::string &dbProp, sint32 value); // Nb: the db prop is not created if not present. + static void addDbProp(const std::string &dbProp, sint32 value); // Nb: the db prop is created if not present. + static void delDbProp(const std::string &dbProp); static std::string getDefine(const std::string &def); static void messageBox(const ucstring &text); static void messageBox(const ucstring &text, const std::string &masterGroup); @@ -286,17 +295,24 @@ private: static int getUICaller(CLuaState &ls); // params: none. return: CInterfaceElement* (nil if error) static int getCurrentWindowUnder(CLuaState &ls); // params: none. return: CInterfaceElement* (nil if none) static int getUI(CLuaState &ls); // params: "ui:interface:...". return: CInterfaceElement* (nil if error), an additionnal boolean parameter - // can specify verbose display when the element is note found (default is true) + // can specify verbose display when the element is note found (default is true) static int createGroupInstance(CLuaState &ls); // params : param 1 = template name, - // param 2 = id of parent where the instance will be inserted - // param 3 = table with ("template_param", "template_param_value") key/value pairs + // param 2 = id of parent where the instance will be inserted + // param 3 = table with ("template_param", "template_param_value") key/value pairs // such as { id="foo", x="10" } etc. -> returns a new instance of the template, or nil on fail + static int createRootGroupInstance(CLuaState &ls); // params : param 1 = template name, + // param 2 = id of parent where the instance will be inserted + // param 3 = table with ("template_param", "template_param_value") key/value pairs + // such as { id="foo", x="10" } etc. -> returns a new instance of the template, or nil on fail static int createUIElement(CLuaState &ls); // params : param 1 = template name, - // param 2 = id of parent where the instance will be inserted - // param 3 = table with ("template_param", "template_param_value") key/value pairs + // param 2 = id of parent where the instance will be inserted + // param 3 = table with ("template_param", "template_param_value") key/value pairs // such as { id="foo", x="10" } etc. -> returns a new instance of the template, or nil on fail - + static int displayBubble(CLuaState &ls); // params : param 1 = bot id + // param 2 = text + // param 3 = table with all strings and urls + // {"main text"="http:///", "text option 1"="http:///", "text option 2"="http:///") etc... static int getIndexInDB(CLuaState &ls); // params: CDBCtrlSheet*.... return: index, or 0 if error static int getUIId(CLuaState &ls); // params: CInterfaceElement*. return: ui id (empty if error) static int runAH(CLuaState &ls); // params: CInterfaceElement *, "ah", "params". return: none @@ -334,9 +350,9 @@ private: static int getServerSeason(CLuaState &ls); // get the last season sent by the server // 0->auto, computed locally from the current day (or not received from server yet) // 1->server force spring - // 2->' ' ' summer - // 3->' ' ' autumn - // 4->' ' ' winter + // 2->' ' ' summer + // 3->' ' ' autumn + // 4->' ' ' winter static int computeCurrSeason(CLuaState &ls); // compute current displayed season (1->spring, etc .) static int getAutoSeason(CLuaState &ls); // compute automatic season that would be at this time (1->spring, etc .) @@ -345,6 +361,19 @@ private: static int enableModalWindow(CLuaState &ls); static int disableModalWindow(CLuaState &ls); static int getPlayerPos(CLuaState &ls); + static int getPlayerFront(CLuaState &ls); + static int getPlayerDirection(CLuaState &ls); + static int getPlayerGender(CLuaState &ls); + static int getPlayerName(CLuaState &ls); + static int getPlayerTitleRaw(CLuaState &ls); + static int getPlayerTitle(CLuaState &ls); + static int getTargetPos(CLuaState &ls); + static int getTargetFront(CLuaState &ls); + static int getTargetDirection(CLuaState &ls); + static int getTargetGender(CLuaState &ls); + static int getTargetName(CLuaState &ls); + static int getTargetTitleRaw(CLuaState &ls); + static int getTargetTitle(CLuaState &ls); static int addSearchPathUser(CLuaState &ls); static int getClientCfgVar(CLuaState &ls); static int isPlayerFreeTrial(CLuaState &ls); @@ -352,6 +381,8 @@ private: static int isInRingMode(CLuaState &ls); static int getUserRace(CLuaState &ls); static int getSheet2idx(CLuaState &ls); + static int getTargetSlot(CLuaState &ls); + static int getSlotDataSetId(CLuaState &ls); // LUA functions exported for Dev only (debug) diff --git a/code/ryzom/client/src/interface_v3/people_interraction.cpp b/code/ryzom/client/src/interface_v3/people_interraction.cpp index e98013f9a..a92d73ce3 100644 --- a/code/ryzom/client/src/interface_v3/people_interraction.cpp +++ b/code/ryzom/client/src/interface_v3/people_interraction.cpp @@ -40,6 +40,7 @@ #include "../net_manager.h" #include "../connection.h" #include "group_tab.h" +#include "guild_manager.h" // Game share #include "game_share/entity_types.h" // NeL @@ -1418,7 +1419,47 @@ void CPeopleInterraction::updateContactInList(uint32 contactId, TCharConnectionS { sint index = FriendList.getIndexFromContactId(contactId); if (index != -1) - FriendList.setOnline(index, online); + { + if (FriendList.getOnline(index) != online) + { + // Only do work if online status has changed + FriendList.setOnline(index, online); + + CCDBNodeLeaf* node = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_ONLINE_OFFLINE_NOTIFICATIONS_CB", false); + if (node && node->getValueBool()) + { + // Only show the message if this player is not in my guild (because then the guild manager will show a message) + std::vector GuildMembers = CGuildManager::getInstance()->getGuildMembers(); + bool bOnlyFriend = true; + ucstring name = toLower(FriendList.getName(index)); + for (uint i = 0; i < GuildMembers.size(); ++i) + { + if (toLower(GuildMembers[i].Name) == name) + { + bOnlyFriend = false; + break; + } + } + + // Player is not in my guild + if (bOnlyFriend) + { + ucstring msg = (online != ccs_offline) ? CI18N::get("uiPlayerOnline") : CI18N::get("uiPlayerOffline"); + strFindReplace(msg, "%s", FriendList.getName(index)); + string cat = getStringCategory(msg, msg); + map::const_iterator it; + NLMISC::CRGBA col = CRGBA::Yellow; + it = ClientCfg.SystemInfoParams.find(toLower(cat)); + if (it != ClientCfg.SystemInfoParams.end()) + { + col = it->second.Color; + } + bool dummy; + PeopleInterraction.ChatInput.AroundMe.displayMessage(msg, col, 2, &dummy); + } + } + } + } } else { @@ -1988,6 +2029,39 @@ public: }; REGISTER_ACTION_HANDLER( CHandlerDismissMember, "dismiss_member"); +//================================================================================================================= +// Set the leader of the team +class CHandlerSetTeamLeader : public IActionHandler +{ +public: + void execute (CCtrlBase * /* pCaller */, const std::string &/* sParams */) + { + // retrieve the index of the people + CPeopleList *list; + uint peopleIndex; + if (PeopleInterraction.getPeopleFromCurrentMenu(list, peopleIndex)) + { + if (list == &PeopleInterraction.TeamList) // check for good list + { + /* + const string msgName = "TEAM:SET_LEADER"; + CBitMemStream out; + if(GenericMsgHeaderMngr.pushNameToStream(msgName, out)) + { + uint8 teamMember = (uint8)(peopleIndex); + out.serial(teamMember); + NetMngr.push(out); + //nlinfo("impulseCallBack : %s %d sent", msgName.c_str(), teamMember); + } + else + nlwarning("command 'set_leader': unknown message named '%s'.", msgName.c_str()); + */ + NLMISC::ICommand::execute("a setTeamLeader " + toString(peopleIndex), g_log); + } + } + } +}; +REGISTER_ACTION_HANDLER( CHandlerSetTeamLeader, "set_team_leader"); //================================================================================================================= // Set a successor for the team @@ -3236,6 +3310,12 @@ NLMISC_COMMAND(chatLog, "", "") if (pIM->getLogState()) pIM->displaySystemInfo(CI18N::get("uiLogTurnedOn")); + CCDBNodeLeaf *node = pIM->getDbProp("UI:SAVE:CHATLOG_STATE", false); + if (node) + { + node->setValue32(pIM->getLogState() ? 1 : 0); + } + return true; }; diff --git a/code/ryzom/client/src/interface_v3/people_list.cpp b/code/ryzom/client/src/interface_v3/people_list.cpp index ac4764e15..c9b95a14a 100644 --- a/code/ryzom/client/src/interface_v3/people_list.cpp +++ b/code/ryzom/client/src/interface_v3/people_list.cpp @@ -211,7 +211,8 @@ bool CPeopleList::sortExByOnline(const CPeople& a, const CPeople& b) { return (name_a < name_b); } - else { + else + { // Compare online status switch (a.Online) { @@ -467,14 +468,8 @@ void CPeopleList::displayLocalPlayerTell(const ucstring &receiver, uint index, c return; } - ucstring cur_time; - CCDBNodeLeaf *pNL = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false); - if (pNL && pNL->getValueBool()) - cur_time = CInterfaceManager::getTimestampHuman(); - - ucstring csr; - if (CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw())) csr += ucstring("(CSR) "); - ucstring finalMsg = cur_time + csr + CI18N::get("youTell") + ": " + msg; + ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + ucstring finalMsg = csr + CI18N::get("youTell") + ": " + msg; // display msg with good color CInterfaceProperty prop; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); @@ -783,10 +778,6 @@ void CPeopleList::setOnline(uint index, TCharConnectionState online) _Peoples[index].Online = online; - // If the people goes offline remove eventually opened chat - if (online == ccs_offline) - openCloseChat(index, false); - updatePeopleMenu(index); } @@ -944,14 +935,8 @@ class CHandlerContactEntry : public IActionHandler ucstring final; CChatWindow::encodeColorTag(prop.getRGBA(), final, false); - ucstring cur_time; - CCDBNodeLeaf *pNL = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false); - if (pNL && pNL->getValueBool()) - cur_time = CInterfaceManager::getTimestampHuman(); - - ucstring csr; - if (CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw())) csr += ucstring("(CSR) "); - final += cur_time + csr + CI18N::get("youTell")+": "; + ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + final += csr + CI18N::get("youTell")+": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); CChatWindow::encodeColorTag(prop.getRGBA(), final, true); final += text; @@ -962,7 +947,6 @@ class CHandlerContactEntry : public IActionHandler strFindReplace(final, CI18N::get("youTell"), s); CInterfaceManager::getInstance()->log(final); } - } } }; diff --git a/code/ryzom/client/src/interface_v3/player_trade.cpp b/code/ryzom/client/src/interface_v3/player_trade.cpp index 7307110d9..ebc618ec7 100644 --- a/code/ryzom/client/src/interface_v3/player_trade.cpp +++ b/code/ryzom/client/src/interface_v3/player_trade.cpp @@ -139,6 +139,7 @@ void CPlayerTrade::restoreItem(CDBCtrlSheet *exchangeSlot) im.getBagItem(emptySlot).setWeight((uint32) exchangeSlot->getItemWeight()); im.getBagItem(emptySlot).setNameId(exchangeSlot->getItemNameId()); im.getBagItem(emptySlot).setInfoVersion(exchangeSlot->getItemInfoVersion()); + im.getBagItem(emptySlot).setResaleFlag(exchangeSlot->getItemResaleFlag()); } diff --git a/code/ryzom/client/src/interface_v3/skill_manager.cpp b/code/ryzom/client/src/interface_v3/skill_manager.cpp index 9d59c5bcb..c5fec7eb8 100644 --- a/code/ryzom/client/src/interface_v3/skill_manager.cpp +++ b/code/ryzom/client/src/interface_v3/skill_manager.cpp @@ -1042,7 +1042,7 @@ void CSkillManager::setPlayerTitle(const std::string &name) // *************************************************************************** // *************************************************************************** -#define GROUP_TITLE_COMBO "ui:interface:info_player_skills:content:basics_skills:title:player_title" +#define GROUP_TITLE_COMBO "ui:interface:info_player_skills:content:webinfos:title:player_title" // *************************************************************************** class CHandlerTitleInit: public IActionHandler diff --git a/code/ryzom/client/src/interface_v3/view_base.h b/code/ryzom/client/src/interface_v3/view_base.h index b568014a6..f4faaa204 100644 --- a/code/ryzom/client/src/interface_v3/view_base.h +++ b/code/ryzom/client/src/interface_v3/view_base.h @@ -70,6 +70,10 @@ public: // from CInterfaceElement virtual void visit(CInterfaceElementVisitor *visitor); + + // special for mouse over : return true and fill the name of the cursor to display + virtual bool getMouseOverShape(std::string &/* texName */, uint8 &/* rot */, NLMISC::CRGBA &/* col */) { return false; } + }; diff --git a/code/ryzom/client/src/interface_v3/view_bitmap.h b/code/ryzom/client/src/interface_v3/view_bitmap.h index 9cfd9de09..670c6fdf7 100644 --- a/code/ryzom/client/src/interface_v3/view_bitmap.h +++ b/code/ryzom/client/src/interface_v3/view_bitmap.h @@ -72,6 +72,8 @@ public: bool getScale() const { return _Scale; } void setScale (bool s) { _Scale = s; } + bool getTile() const { return _Tile; } + void setTile (bool s) { _Tile = s; } void setColor (const NLMISC::CRGBA &r) { _Color = r; } // Reflected diff --git a/code/ryzom/client/src/interface_v3/view_link.cpp b/code/ryzom/client/src/interface_v3/view_link.cpp index 4b0426006..61421133e 100644 --- a/code/ryzom/client/src/interface_v3/view_link.cpp +++ b/code/ryzom/client/src/interface_v3/view_link.cpp @@ -42,5 +42,23 @@ void CViewLink::setHTMLView(CGroupHTML *html) HTML = html; } +// *************************************************************************** +bool CViewLink::getMouseOverShape(string &texName, uint8 &rot, CRGBA &col) +{ + if (HTML != NULL) + { + if (!LinkTitle.empty()) + { + texName = LinkTitle; + rot= 0; + col = CRGBA::White; + return true; + } + } + + return false; +} + + // *************************************************************************** diff --git a/code/ryzom/client/src/interface_v3/view_link.h b/code/ryzom/client/src/interface_v3/view_link.h index 7a77f2b81..ba30fafd2 100644 --- a/code/ryzom/client/src/interface_v3/view_link.h +++ b/code/ryzom/client/src/interface_v3/view_link.h @@ -38,8 +38,11 @@ public: // The URI std::string Link; + std::string LinkTitle; + // Set the main group void setHTMLView(class CGroupHTML *html); + bool getMouseOverShape(std::string &texName, uint8 &rot, NLMISC::CRGBA &col); protected: diff --git a/code/ryzom/client/src/interface_v3/view_pointer.cpp b/code/ryzom/client/src/interface_v3/view_pointer.cpp index e6e6dcaa2..27d3dae86 100644 --- a/code/ryzom/client/src/interface_v3/view_pointer.cpp +++ b/code/ryzom/client/src/interface_v3/view_pointer.cpp @@ -70,6 +70,7 @@ CViewPointer::CViewPointer (const TCtorParam ¶m) _Color = CRGBA(255,255,255,255); _LastHightLight = NULL; _StringMode = false; + _ForceStringMode = false; _StringCursor = NULL; } @@ -255,6 +256,41 @@ void CViewPointer::draw () return; } + const vector &vUP = pIM->getViewsUnderPointer (); + + for(uint i=0;i(vUP[i]); + if (vLink != NULL) + { + string tooltip; + uint8 rot; + + if (vLink->getMouseOverShape(tooltip, rot, col)) + { + setString(ucstring(tooltip)); + sint32 texId = rVR.getTextureIdFromName ("curs_pick.tga"); + + CInterfaceGroup *stringCursor = IsMouseCursorHardware() ? _StringCursorHardware : _StringCursor; + if (stringCursor) + { + stringCursor->setX(_PointerX); + stringCursor->setY(_PointerY); + stringCursor->updateCoords(); + stringCursor->draw(); + // if in hardware mode, force to draw the default cursor no matter what.. + if (IsMouseCursorHardware()) + drawCursor(texId, col, 0); + } + else + { + drawCursor(texId, col, 0); + } + return; + } + } + } + // Draw if capture right pCB = pIM->getCapturePointerRight(); if (pCB != NULL) @@ -521,16 +557,50 @@ bool CViewPointer::drawPan(CCtrlBase* pCB, NLMISC::CRGBA col) // -------------------------------------------------------------------------------------------------------------------- bool CViewPointer::drawCustom(CCtrlBase* pCB) { - std::string texName; + string texName; uint8 rot; NLMISC::CRGBA col; if (pCB->getMouseOverShape(texName, rot, col)) { - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - CViewRenderer &rVR = pIM->getViewRenderer(); - sint32 texId = rVR.getTextureIdFromName (texName); - drawCursor(texId, col, 0); - return true; + if (texName[0] == '@') + { + const string &tooltipInfos = texName.substr(1); + string tooltip; + vector tooltipInfosList; + splitString(tooltipInfos, "@", tooltipInfosList); + texName = tooltipInfosList[0]; + tooltip = tooltipInfosList[1]; + nlinfo(tooltip.c_str()); + setString(ucstring(tooltip)); + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + CViewRenderer &rVR = pIM->getViewRenderer(); + sint32 texId = rVR.getTextureIdFromName (texName); + + CInterfaceGroup *stringCursor = IsMouseCursorHardware() ? _StringCursorHardware : _StringCursor; + if (stringCursor) + { + stringCursor->setX(_PointerX); + stringCursor->setY(_PointerY); + stringCursor->updateCoords(); + stringCursor->draw(); + // if in hardware mode, force to draw the default cursor no matter what.. + if (IsMouseCursorHardware()) + drawCursor(texId, col, 0); + } + else + { + drawCursor(texId, col, 0); + } + return true; + } + else + { + CInterfaceManager *pIM = CInterfaceManager::getInstance(); + CViewRenderer &rVR = pIM->getViewRenderer(); + sint32 texId = rVR.getTextureIdFromName (texName); + drawCursor(texId, col, 0); + return true; + } } return false; } diff --git a/code/ryzom/client/src/interface_v3/view_pointer.h b/code/ryzom/client/src/interface_v3/view_pointer.h index 0ade8b2c7..42a20b0a0 100644 --- a/code/ryzom/client/src/interface_v3/view_pointer.h +++ b/code/ryzom/client/src/interface_v3/view_pointer.h @@ -153,6 +153,7 @@ private: // Cursor mode bool _StringMode; + bool _ForceStringMode; CInterfaceGroup *_StringCursor; CInterfaceGroup *_StringCursorHardware; ucstring _ContextString; diff --git a/code/ryzom/client/src/interface_v3/view_renderer.cpp b/code/ryzom/client/src/interface_v3/view_renderer.cpp index 6a300275e..b3e419e5c 100644 --- a/code/ryzom/client/src/interface_v3/view_renderer.cpp +++ b/code/ryzom/client/src/interface_v3/view_renderer.cpp @@ -743,8 +743,8 @@ void CViewRenderer::loadTextures (const std::string &textureFileName, const std: image.UVMin.V = uvMinV; image.UVMax.U = uvMaxU; image.UVMax.V = uvMaxV; - sTGAname = tgaName; - sTGAname = toLower(sTGAname); + sTGAname = toLower(string(tgaName)); + string::size_type stripPng = sTGAname.find(".png"); if (stripPng != string::npos) { @@ -752,6 +752,7 @@ void CViewRenderer::loadTextures (const std::string &textureFileName, const std: sTGAname[stripPng + 2] = 'g'; sTGAname[stripPng + 3] = 'a'; } + image.Name = sTGAname; image.GlobalTexturePtr = &(_GlobalTextures.back()); if (getTextureIdFromName(sTGAname) != -1) @@ -1029,6 +1030,7 @@ sint32 CViewRenderer::getTextureIdFromName (const string &sName) const // convert to lowCase string nameLwr = toLower(sName); + string::size_type stripPng = nameLwr.find(".png"); if (stripPng != string::npos) { @@ -1036,7 +1038,7 @@ sint32 CViewRenderer::getTextureIdFromName (const string &sName) const nameLwr[stripPng + 2] = 'g'; nameLwr[stripPng + 3] = 'a'; } - + // Search in map TTextureMap::const_iterator it= _TextureMap.find(nameLwr); if( it==_TextureMap.end() ) @@ -1531,6 +1533,7 @@ void CViewRenderer::initSystemTextures() addSystemTexture(RegenTexture, "regen.tga"); addSystemTexture(RegenBackTexture, "regen_back.tga"); addSystemTexture(GlowStarTexture, "glow_star_24.tga"); + addSystemTexture(ItemLockedByOwnerTexture, "r2ed_toolbar_lock_small.tga"); } diff --git a/code/ryzom/client/src/interface_v3/view_renderer.h b/code/ryzom/client/src/interface_v3/view_renderer.h index ea7583dfa..cea807a23 100644 --- a/code/ryzom/client/src/interface_v3/view_renderer.h +++ b/code/ryzom/client/src/interface_v3/view_renderer.h @@ -75,6 +75,7 @@ public: RegenTexture, RegenBackTexture, GlowStarTexture, + ItemLockedByOwnerTexture, NumSystemTextures, }; diff --git a/code/ryzom/client/src/libwww.cpp b/code/ryzom/client/src/libwww.cpp index 966aeffa6..a1c0ce826 100644 --- a/code/ryzom/client/src/libwww.cpp +++ b/code/ryzom/client/src/libwww.cpp @@ -225,7 +225,10 @@ HTAttr p_attr[] = HTAttr div_attr[] = { - HTML_ATTR(DIV,NAME), + HTML_ATTR(DIV,CLASS), + HTML_ATTR(DIV,ID), + HTML_ATTR(DIV,NAME), + HTML_ATTR(DIV,STYLE), { 0 } }; @@ -535,6 +538,7 @@ const std::string &setCurrentDomain(const std::string &url) return HTTPCurrentDomain; } + void initLibWWW() { static bool initialized = false; diff --git a/code/ryzom/client/src/libwww.h b/code/ryzom/client/src/libwww.h index 41db90a6c..2c6ed2ae0 100644 --- a/code/ryzom/client/src/libwww.h +++ b/code/ryzom/client/src/libwww.h @@ -206,7 +206,10 @@ enum enum { - HTML_ATTR(DIV,NAME) = 0, + HTML_ATTR(DIV,CLASS) = 0, + HTML_ATTR(DIV,ID), + HTML_ATTR(DIV,NAME), + HTML_ATTR(DIV,STYLE), }; diff --git a/code/ryzom/client/src/login.cpp b/code/ryzom/client/src/login.cpp index d1dee738d..e43d6c8f5 100644 --- a/code/ryzom/client/src/login.cpp +++ b/code/ryzom/client/src/login.cpp @@ -738,8 +738,10 @@ void initLoginScreen() if(!l.empty()) { CGroupEditBox *pGEB = dynamic_cast(pIM->getElementFromId(CTRL_EDITBOX_LOGIN)); - if (pGEB != NULL) + if (pGEB != NULL && (pGEB->getInputString().empty())) + { pGEB->setInputString(l); + } pIM->runActionHandler("set_keyboard_focus", NULL, "target=" CTRL_EDITBOX_PASSWORD "|select_all=false"); } else @@ -1792,7 +1794,6 @@ class CAHOpenURL : public IActionHandler } else { - DWORD ret = 0; LPVOID lpMsgBuf; FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | @@ -1922,21 +1923,21 @@ class CAHInitResLod : public IActionHandler // first indicates the preset-able cfg-variable // second indicates if its a double variable (else it's an int) CfgPresetList.clear(); - CfgPresetList.push_back(pair("LandscapeTileNear", true)); - CfgPresetList.push_back(pair("LandscapeThreshold", true)); - CfgPresetList.push_back(pair("Vision", true)); - CfgPresetList.push_back(pair("MicroVeget", false)); - CfgPresetList.push_back(pair("MicroVegetDensity", true)); + CfgPresetList.push_back(pair("LandscapeTileNear", true)); + CfgPresetList.push_back(pair("LandscapeThreshold", true)); + CfgPresetList.push_back(pair("Vision", true)); + CfgPresetList.push_back(pair("MicroVeget", false)); + CfgPresetList.push_back(pair("MicroVegetDensity", true)); CfgPresetList.push_back(pair("FxNbMaxPoly", false)); - CfgPresetList.push_back(pair("Cloud", false)); + CfgPresetList.push_back(pair("Cloud", false)); CfgPresetList.push_back(pair("CloudQuality", true)); CfgPresetList.push_back(pair("CloudUpdate", false)); CfgPresetList.push_back(pair("Shadows", false)); - CfgPresetList.push_back(pair("SkinNbMaxPoly", false)); + CfgPresetList.push_back(pair("SkinNbMaxPoly", false)); CfgPresetList.push_back(pair("NbMaxSkeletonNotCLod", false)); CfgPresetList.push_back(pair("CharacterFarClip", true)); - CfgPresetList.push_back(pair("Bloom", false)); + CfgPresetList.push_back(pair("Bloom", false)); CfgPresetList.push_back(pair("SquareBloom", false)); CfgPresetList.push_back(pair("DensityBloom", true)); diff --git a/code/ryzom/client/src/main_loop.cpp b/code/ryzom/client/src/main_loop.cpp index 81a63ae60..15a525f58 100644 --- a/code/ryzom/client/src/main_loop.cpp +++ b/code/ryzom/client/src/main_loop.cpp @@ -2889,6 +2889,7 @@ bool mainLoop() // This code must remain at the very end of the main loop. if(LoginSM.getCurrentState() == CLoginStateMachine::st_enter_far_tp_main_loop) { + CInterfaceManager::getInstance()->executeLuaScript("game:onFarTpStart()"); // Will loop the network until the end of the relogging process FarTP.farTPmainLoop(); @@ -2965,6 +2966,8 @@ bool mainLoop() // Get the Connection State (must be done after any Far TP to prevent the uiDisconnected box to be displayed) lastConnectionState = CNetworkConnection::Connected; connectionState = NetMngr.getConnectionState(); + + CInterfaceManager::getInstance()->executeLuaScript("game:onFarTpEnd()"); } } // end of main loop diff --git a/code/ryzom/client/src/net_manager.cpp b/code/ryzom/client/src/net_manager.cpp index 568081c81..55df0585d 100644 --- a/code/ryzom/client/src/net_manager.cpp +++ b/code/ryzom/client/src/net_manager.cpp @@ -664,10 +664,12 @@ void CInterfaceChatDisplayer::displayChat(TDataSetIndex compressedSenderIndex, c } // select DB + sint32 dbIndex = ChatMngr.getDynamicChannelDbIndexFromId(dynChatId); + clamp(dbIndex,0 , CChatGroup::MaxDynChanPerPlayer); string entry="UI:SAVE:CHAT:COLORS:"; switch(mode) { - case CChatGroup::dyn_chat: entry+="DYN"; break; + case CChatGroup::dyn_chat: entry+="DYN:" + NLMISC::toString(dbIndex); break; case CChatGroup::say: entry+="SAY"; break; case CChatGroup::shout: entry+="SHOUT"; break; case CChatGroup::team: entry+="GROUP"; break; @@ -697,6 +699,11 @@ void CInterfaceChatDisplayer::displayChat(TDataSetIndex compressedSenderIndex, c } } + if (stringCategory == "emt") + { + bubbleWanted = false; + } + if (mode != CChatGroup::system) { // find the sender/text separator to put color tags @@ -3206,7 +3213,7 @@ void impulseOutpostDeclareWarAck(NLMISC::CBitMemStream &impulse) node->setValue32(timeStartAttack); } -extern void addWebIGParams (string &url); +extern void addWebIGParams(string &url, bool trustedDomain); //----------------------------------------------- //----------------------------------------------- @@ -3290,7 +3297,6 @@ private: string group = titleStr.toString(); // group = group.substr(9, group.size()-10); - nlinfo("group = %s", group.c_str()); groupHtml = dynamic_cast(pIM->getElementFromId("ui:interface:"+group+":content:html")); if (!groupHtml) { @@ -3312,7 +3318,7 @@ private: if (group == "webig") pGC->setActive(true); string url = contentStr.toString(); - addWebIGParams(url); + addWebIGParams(url, true); groupHtml->browse(url.c_str()); pIM->setTopWindow(pGC); } diff --git a/code/ryzom/client/src/player_cl.cpp b/code/ryzom/client/src/player_cl.cpp index 9dfe6e12f..d74cf6ae1 100644 --- a/code/ryzom/client/src/player_cl.cpp +++ b/code/ryzom/client/src/player_cl.cpp @@ -164,7 +164,7 @@ bool CPlayerCL::isNeutral() const //----------------------------------------------- bool CPlayerCL::isFriend () const { - return isNeutral() || isNeutralPVP() || isAlly(); + return isNeutral() || isAlly(); } @@ -174,12 +174,44 @@ bool CPlayerCL::isFriend () const //----------------------------------------------- bool CPlayerCL::isEnemy () const { + // Challenge i.e. SOLO FULL PVP + if( getPvpMode()&PVP_MODE::PvpChallenge || + UserEntity->getPvpMode()&PVP_MODE::PvpChallenge ) + { + return true; + } + + // if one of 2 players is not in pvp they can't be enemies if( UserEntity->getPvpMode() == PVP_MODE::None || getPvpMode() == PVP_MODE::None ) { return false; } + + // if one of 2 players is safe they can't be enemies + if( UserEntity->getPvpMode()&PVP_MODE::PvpSafe || + getPvpMode()&PVP_MODE::PvpSafe ) + { + return false; + } + + // if one of 2 players are in safe zone and not flagged they can't be enemies + if ((UserEntity->getPvpMode()&PVP_MODE::PvpZoneSafe && + ((UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged) == 0)) + || + (getPvpMode()&PVP_MODE::PvpZoneSafe && + ((getPvpMode()&PVP_MODE::PvpFactionFlagged) == 0))) + { + return false; + } + + // Duel + if( getPvpMode()&PVP_MODE::PvpDuel && + UserEntity->getPvpMode()&PVP_MODE::PvpDuel ) + { + return true; // TODO + } // Outpost if ( isAnOutpostEnemy() ) @@ -187,19 +219,12 @@ bool CPlayerCL::isEnemy () const return true; } - // Challenge - if( getPvpMode()&PVP_MODE::PvpChallenge && - UserEntity->getPvpMode()&PVP_MODE::PvpChallenge ) - { - if( !isInTeam() ) - return true; - } - // Zone Free if( getPvpMode()&PVP_MODE::PvpZoneFree && UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree ) { - if( !isInTeam() && !isInGuild() ) + // If not in same Team and not in same League => ennemy + if( !isInTeam() && !isInSameLeague() ) return true; } @@ -207,7 +232,11 @@ bool CPlayerCL::isEnemy () const if( getPvpMode()&PVP_MODE::PvpZoneGuild && UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild ) { - if( !isInTeam() && !isInGuild() ) + // If in same Guild but different Leagues => ennemy + if ( isInSameGuild() && oneInLeague() && !isInSameLeague() ) + return true; + + if( !isInTeam() && !isInSameLeague() ) return true; } @@ -219,29 +248,18 @@ bool CPlayerCL::isEnemy () const return true; } - // Duel - if( getPvpMode()&PVP_MODE::PvpDuel && - UserEntity->getPvpMode()&PVP_MODE::PvpDuel ) - { - return true; // TODO - } - - // Faction + // Free PVP : Ennemis are not in team AND not in league if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) && (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged)) { - // Check if is not ally - if (!isInTeam() && !isInGuild()) - { - // Check for each Clan if is in opposition - for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) - { - if ((isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) || (isPvpAlly(i) && UserEntity->isPvpEnnemy(i))) - return true; - } - } - + // If in same Guild but different Leagues => ennemy + if ( isInSameGuild() && oneInLeague() && !isInSameLeague() ) + return true; + + if (!isInTeam() && !isInSameLeague()) + return true; } + return false; } // isEnemy // @@ -253,32 +271,38 @@ bool CPlayerCL::isEnemy () const //----------------------------------------------- bool CPlayerCL::isAlly() const { - // if one of 2 players is not in pvp they can't be enemies + + // Challenge i.e. SOLO FULL PVP + if( getPvpMode()&PVP_MODE::PvpChallenge || + UserEntity->getPvpMode()&PVP_MODE::PvpChallenge ) + { + return false; + } + + // if one of 2 players is not in pvp they can't be allies if( UserEntity->getPvpMode() == PVP_MODE::None || getPvpMode() == PVP_MODE::None ) { return false; } + // if one of 2 players is in safe zone and not other they can't be allies + if ((UserEntity->getPvpMode()&PVP_MODE::PvpSafe) != (getPvpMode()&PVP_MODE::PvpSafe)) + { + return false; + } + // Outpost if ( isAnOutpostAlly() ) { return true; } - // Challenge - if( getPvpMode()&PVP_MODE::PvpChallenge && - UserEntity->getPvpMode()&PVP_MODE::PvpChallenge ) - { - if( isInTeam() ) - return true; - } - // Zone Free if( getPvpMode()&PVP_MODE::PvpZoneFree && UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree ) { - if( isInTeam() || isInGuild() ) + if( isInTeam() || isInSameLeague() ) return true; } @@ -286,8 +310,12 @@ bool CPlayerCL::isAlly() const if( getPvpMode()&PVP_MODE::PvpZoneGuild && UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild ) { - if( isInTeam() || isInGuild() ) + if( isInTeam() || isInSameLeague() ) return true; + + if ( isInSameGuild() && !oneInLeague() ) + return true; + } // Zone Faction @@ -298,26 +326,15 @@ bool CPlayerCL::isAlly() const return true; } - // Faction + // Free PVP : Allies are in team OR in league if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) && (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged)) { - if (isInTeam() && isInGuild()) + if (isInTeam() || isInSameLeague()) return true; - - // Check for each Clan if is in opposition - for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) - { - if ((isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) || (isPvpAlly(i) && UserEntity->isPvpEnnemy(i))) - return false; - } - // Check for each Clan if is in same clan - for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) - { - if ((isPvpEnnemy(i) && UserEntity->isPvpEnnemy(i)) || (isPvpAlly(i) && UserEntity->isPvpAlly(i))) - return true; - } + if ( isInSameGuild() && !oneInLeague() ) + return true; } return false; @@ -336,66 +353,12 @@ bool CPlayerCL::isNeutralPVP() const return false; } - // Outpost - if ( getOutpostId() != 0 ) + if( UserEntity->getPvpMode() == PVP_MODE::None ) { - if( UserEntity->getOutpostId() != getOutpostId() ) - { - return true; - } + return false; } - // Challenge - if( getPvpMode()&PVP_MODE::PvpChallenge && - !(UserEntity->getPvpMode()&PVP_MODE::PvpChallenge) ) - { - return true; - } - - // Zone Free - if( getPvpMode()&PVP_MODE::PvpZoneFree && - !(UserEntity->getPvpMode()&PVP_MODE::PvpZoneFree) ) - { - return true; - } - - // Zone Guild - if( getPvpMode()&PVP_MODE::PvpZoneGuild && - !(UserEntity->getPvpMode()&PVP_MODE::PvpZoneGuild) ) - { - return true; - } - - // Zone Faction - if( getPvpMode()&PVP_MODE::PvpZoneFaction && - !(UserEntity->getPvpMode()&PVP_MODE::PvpZoneFaction) ) - { - return true; - } - - // Duel - if( getPvpMode()&PVP_MODE::PvpDuel && - !(UserEntity->getPvpMode()&PVP_MODE::PvpDuel) ) - { - return true; - } - - if ((getPvpMode()&PVP_MODE::PvpFaction || getPvpMode()&PVP_MODE::PvpFactionFlagged) && - (UserEntity->getPvpMode()&PVP_MODE::PvpFaction || UserEntity->getPvpMode()&PVP_MODE::PvpFactionFlagged)) - { - // Check for each Clan if is in opposition or same - for (uint8 i = 0; i < PVP_CLAN::NbClans; i++) - { - if ((isPvpEnnemy(i) && UserEntity->isPvpAlly(i)) || - (isPvpAlly(i) && UserEntity->isPvpEnnemy(i)) || - (isPvpEnnemy(i) && UserEntity->isPvpEnnemy(i)) || - (isPvpAlly(i) && UserEntity->isPvpAlly(i))) - return false; - } - return true; - } - - return false; + return (!isEnemy() && !isAlly()); } diff --git a/code/ryzom/client/src/string_manager_client.cpp b/code/ryzom/client/src/string_manager_client.cpp index 40886d518..dd79da45e 100644 --- a/code/ryzom/client/src/string_manager_client.cpp +++ b/code/ryzom/client/src/string_manager_client.cpp @@ -34,6 +34,8 @@ namespace STRING_MANAGER // *************************************************************************** map CStringManagerClient::_SpecItem_TempMap; + map CStringManagerClient::_DynStrings; + vector CStringManagerClient::_TitleWords; bool CStringManagerClient::_SpecItem_MemoryCompressed = false; char *CStringManagerClient::_SpecItem_Labels = NULL; ucchar *CStringManagerClient::_SpecItem_NameDesc = NULL; @@ -381,7 +383,15 @@ restartLoop4: result = ucstring(tmp) + it->second; } else + { result = it->second; + if (result.size() > 9 && result.substr(0, 9) == ucstring("::iterator itds = _DynStrings.find(result.substr(9, result.size()-10)); + if (itds != _DynStrings.end()) + result = itds->second; + } + } } return true; @@ -1595,9 +1605,29 @@ const ucchar *CStringManagerClient::getSPhraseLocalizedDescription(NLMISC::CShee // *************************************************************************** const ucchar *CStringManagerClient::getTitleLocalizedName(const std::string &titleId, bool women) { - return getSpecialWord(titleId,women); + const ucchar * infos = getSpecialWord(titleId, women); + ucstring infosUC(infos); + + vector listInfos; + splitUCString(infosUC, ucstring("#"), listInfos); + if (listInfos.empty()) + return infos; + + _TitleWords.push_back(listInfos[0]); + return _TitleWords.back().c_str(); } +vector CStringManagerClient::getTitleInfos(const std::string &titleId, bool women) +{ + const ucchar * infos = getSpecialWord(titleId, women); + ucstring infosUC(infos); + + vector listInfos; + splitUCString(infosUC, ucstring("#"), listInfos); + return listInfos; +} + + // *************************************************************************** const ucchar *CStringManagerClient::getClassificationTypeLocalizedName(EGSPD::CClassificationType::TClassificationType type) { @@ -1641,7 +1671,14 @@ const ucchar *CStringManagerClient::getSquadLocalizedDescription(NLMISC::CSheetI } // *************************************************************************** -void CStringManagerClient::replaceSBrickName(NLMISC::CSheetId id, const ucstring &name, const ucstring &desc, const ucstring &desc2) +void CStringManagerClient::replaceDynString(const ucstring &name, const ucstring &text) +{ + _DynStrings[name] = text; +} + + +// *************************************************************************** +void CStringManagerClient::replaceSBrickName(NLMISC::CSheetId id, const ucstring &name, const ucstring &desc, const ucstring &desc2) { std::string label= id.toString(); if (label.empty()) diff --git a/code/ryzom/client/src/string_manager_client.h b/code/ryzom/client/src/string_manager_client.h index 511986407..e5842a9f6 100644 --- a/code/ryzom/client/src/string_manager_client.h +++ b/code/ryzom/client/src/string_manager_client.h @@ -77,6 +77,7 @@ public: static void specialWordsMemoryCompress(); // Yoyo: Replace the Brick Name with Filled stats (CSBrickManager work). No-Op if not found static void replaceSBrickName(NLMISC::CSheetId id, const ucstring &name, const ucstring &desc, const ucstring &desc2); + static void replaceDynString(const ucstring &name, const ucstring &text); // Get the Localized Name of the Places. static const ucchar *getPlaceLocalizedName(const std::string &placeNameID); @@ -106,6 +107,7 @@ public: // Get the Localized Title name static const ucchar *getTitleLocalizedName(const std::string &titleId, bool women); + static std::vector CStringManagerClient::getTitleInfos(const std::string &titleId, bool women); // Get the Localized name of a classification type static const ucchar *getClassificationTypeLocalizedName(EGSPD::CClassificationType::TClassificationType type); @@ -215,8 +217,6 @@ private: // Callback for dyn string value from the server TStringCallbacksContainer _DynStringsCallbacks; - - // Return value for waiting string.. static ucstring _WaitString; @@ -273,6 +273,9 @@ private: static bool _SpecItem_MemoryCompressed; static std::map _SpecItem_TempMap; + static std::vector _TitleWords; + static std::map _DynStrings; + static char *_SpecItem_Labels; static ucchar *_SpecItem_NameDesc; diff --git a/code/ryzom/client/src/user_entity.cpp b/code/ryzom/client/src/user_entity.cpp index 64a493d2e..f1e4aeff2 100644 --- a/code/ryzom/client/src/user_entity.cpp +++ b/code/ryzom/client/src/user_entity.cpp @@ -507,7 +507,7 @@ void CUserEntity::updateVisualPropertyName(const NLMISC::TGameCycle &gameCycle, CPlayerCL::updateVisualPropertyName(gameCycle, prop); // Name changed ? - if (oldNameId != _NameId) +/* if (oldNameId != _NameId) { CInterfaceManager *pIM = CInterfaceManager::getInstance(); CInterfaceElement *element = pIM->getElementFromId("ui:interface:mailbox:content:html"); @@ -518,7 +518,7 @@ void CUserEntity::updateVisualPropertyName(const NLMISC::TGameCycle &gameCycle, html->browse("home"); } } - +*/ }// updateVisualPropertyName // //----------------------------------------------- @@ -2657,36 +2657,39 @@ void CUserEntity::selection(const CLFECOMMON::TCLEntityId &slot) // virtual playerGiftNeeded->setValue32(0); } } +/* TODO ULU : Add RP tags */ // update pvp tags - CViewBase * tagView = dynamic_cast(pIM->getElementFromId("ui:interface:target:pvp_tags")); - CViewBase * contentView = dynamic_cast(pIM->getElementFromId("ui:interface:target:content")); - if ((tgtSlot!=CLFECOMMON::INVALID_SLOT) && entity) { CPlayerCL *pPlayer = dynamic_cast(entity); if (pPlayer) { - for (uint8 i = 0; i < 7; i++) + /*// Pvp Mode + CViewBitmap * tagMode = dynamic_cast(pIM->getElementFromId("ui:interface:target:pvp_tags:mode")); + if (tagMode) { - CViewBitmap * tag = dynamic_cast(pIM->getElementFromId("ui:interface:target:pvp_tags:tag_"+toString(i))); - if (tag) - { - if ((pPlayer->getPvpMode()&PVP_MODE::PvpFaction || pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged) && pPlayer->isPvpAlly(i)) - { - tag->setTexture("pvp_ally_"+toString(i)+".tga"); - } - else if ((pPlayer->getPvpMode()&PVP_MODE::PvpFaction || pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged) && pPlayer->isPvpEnnemy(i)) - { - tag->setTexture("pvp_enemy_"+toString(i)+".tga"); - } - else - { - tag->setTexture("alpha_10.tga"); - } - } + if (pPlayer->getPvpMode()&PVP_MODE::PvpFaction) + tagMode->setTexture("pvp_orange.tga"); + else if (pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged) + tagMode->setTexture("pvp_red.tga"); + else + tagMode->setTexture("alpha_10.tga"); } +*/ + /*// Pvp available actions (attack, heal, both) + CViewBitmap * tagMode = dynamic_cast(pIM->getElementFromId("ui:interface:target:pvp_tags:actions")); + if (tagMode) + { + if (pPlayer->getPvpMode()&PVP_MODE::PvpFaction) + tag->setTexture("pvp_orange.tga"); + else if (pPlayer->getPvpMode()&PVP_MODE::PvpFactionFlagged) + tag->setTexture("pvp_red.tga"); + else + tag->setTexture("alpha_10.tga"); + }*/ + } } diff --git a/code/ryzom/client/src/user_entity.h b/code/ryzom/client/src/user_entity.h index 0d39f2967..30c9fed41 100644 --- a/code/ryzom/client/src/user_entity.h +++ b/code/ryzom/client/src/user_entity.h @@ -477,6 +477,14 @@ public: /// true if current behaviour allows to change front bool canChangeFront(); + ucstring getLoginName() + { + if (_LoginName == ucstring("")) + _LoginName = getDisplayName(); + + return _LoginName; + } + protected: class CSpeedFactor : public ICDBNode::IPropertyObserver { @@ -741,6 +749,8 @@ private: /// previous items in hand before they have been changed by an auto-equip due to an action (ex: forage) CItemSnapshot _PreviousRightHandItem; CItemSnapshot _PreviousLeftHandItem; + + ucstring _LoginName; }; /// Out game received position From 6c43baaa1c7a36974189138681dc4e48188c1aa1 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 27 Feb 2012 22:42:30 +0100 Subject: [PATCH 182/215] Changed: #1433 Merge changes from patch 1.13 --- code/ryzom/client/src/cdb_branch.cpp | 2 +- code/ryzom/client/src/cdb_branch.h | 2 +- code/ryzom/client/src/character_cl.cpp | 1 - code/ryzom/client/src/client_chat_manager.cpp | 4 ++-- code/ryzom/client/src/entity_cl.cpp | 8 ++++---- .../client/src/interface_v3/chat_text_manager.cpp | 2 +- code/ryzom/client/src/interface_v3/chat_window.cpp | 2 +- code/ryzom/client/src/interface_v3/group_compas.cpp | 10 ++++++++++ .../client/src/interface_v3/group_html_webig.cpp | 2 +- code/ryzom/client/src/interface_v3/lua_ihm.cpp | 12 ++++++------ code/ryzom/client/src/interface_v3/people_list.cpp | 6 +++--- code/ryzom/client/src/string_manager_client.h | 2 +- 12 files changed, 31 insertions(+), 22 deletions(-) diff --git a/code/ryzom/client/src/cdb_branch.cpp b/code/ryzom/client/src/cdb_branch.cpp index 60c6b63f4..351203493 100644 --- a/code/ryzom/client/src/cdb_branch.cpp +++ b/code/ryzom/client/src/cdb_branch.cpp @@ -628,7 +628,7 @@ void CCDBNodeBranch::display (const std::string &prefix) } } -void CCDBNodeBranch::removeNode (CTextId& id) +void CCDBNodeBranch::removeNode (const CTextId& id) { // Look for the node CCDBNodeBranch *pNode = dynamic_cast(getNode(id,false)); diff --git a/code/ryzom/client/src/cdb_branch.h b/code/ryzom/client/src/cdb_branch.h index 405c7c479..74e085ca4 100644 --- a/code/ryzom/client/src/cdb_branch.h +++ b/code/ryzom/client/src/cdb_branch.h @@ -165,7 +165,7 @@ public: virtual void display (const std::string &prefix); - void removeNode (CTextId& id); + void removeNode (const CTextId& id); /** * add an observer to a property diff --git a/code/ryzom/client/src/character_cl.cpp b/code/ryzom/client/src/character_cl.cpp index 694ed9b16..c0e6d1104 100644 --- a/code/ryzom/client/src/character_cl.cpp +++ b/code/ryzom/client/src/character_cl.cpp @@ -1877,7 +1877,6 @@ void CCharacterCL::updateVisualPropertyPvpMode(const NLMISC::TGameCycle &/* game void CCharacterCL::updateVisualPropertyPvpClan(const NLMISC::TGameCycle &/* gameCycle */, const sint64 &prop) { _LeagueId = uint32(prop); - buildInSceneInterface(); if (isUser()) { diff --git a/code/ryzom/client/src/client_chat_manager.cpp b/code/ryzom/client/src/client_chat_manager.cpp index e7ef54d02..59b72cedb 100644 --- a/code/ryzom/client/src/client_chat_manager.cpp +++ b/code/ryzom/client/src/client_chat_manager.cpp @@ -1174,7 +1174,7 @@ class CHandlerTell : public IActionHandler ucstring finalMsg; CChatWindow::encodeColorTag(prop.getRGBA(), finalMsg, false); - ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + ucstring csr(CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""); finalMsg += csr + CI18N::get("youTell") + ": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); CChatWindow::encodeColorTag(prop.getRGBA(), finalMsg, true); @@ -1182,7 +1182,7 @@ class CHandlerTell : public IActionHandler // display msg with good color // TDataSetIndex dsi; // not used .... PeopleInterraction.ChatInput.Tell.displayTellMessage(/*dsi, */finalMsg, receiver, prop.getRGBA()); - + ucstring s = CI18N::get("youTellPlayer"); strFindReplace(s, "%name", receiver); strFindReplace(finalMsg, CI18N::get("youTell"), s); diff --git a/code/ryzom/client/src/entity_cl.cpp b/code/ryzom/client/src/entity_cl.cpp index 421faad47..7cefa69d7 100644 --- a/code/ryzom/client/src/entity_cl.cpp +++ b/code/ryzom/client/src/entity_cl.cpp @@ -2539,7 +2539,7 @@ NLMISC::CRGBA CEntityCL::getColor () const if (isEnemy()) { if (getPvpMode()&PVP_MODE::PvpFaction) - return CRGBA::CRGBA(min(255, _PvpEnemyColor.R+150), min(255, _PvpEnemyColor.G+150), min(255, _PvpEnemyColor.B+150),_PvpEnemyColor.A); + return CRGBA(min(255, _PvpEnemyColor.R+150), min(255, _PvpEnemyColor.G+150), min(255, _PvpEnemyColor.B+150),_PvpEnemyColor.A); else return _PvpEnemyColor; } @@ -2551,8 +2551,8 @@ NLMISC::CRGBA CEntityCL::getColor () const if (getPvpMode() & PVP_MODE::PvpFactionFlagged) { if(isInSameLeague()) - return CRGBA::CRGBA(max(0, _PvpAllyColor.R-100), max(0, _PvpAllyColor.G-100), max(0, _PvpAllyColor.B-100),_PvpAllyColor.A); - return CRGBA::CRGBA(max(0, _PvpAllyInTeamColor.R-100), max(0, _PvpAllyInTeamColor.G-100), max(0, _PvpAllyInTeamColor.B-100),_PvpAllyInTeamColor.A); + return CRGBA(max(0, _PvpAllyColor.R-100), max(0, _PvpAllyColor.G-100), max(0, _PvpAllyColor.B-100),_PvpAllyColor.A); + return CRGBA(max(0, _PvpAllyInTeamColor.R-100), max(0, _PvpAllyInTeamColor.G-100), max(0, _PvpAllyInTeamColor.B-100),_PvpAllyInTeamColor.A); } else { @@ -2572,7 +2572,7 @@ NLMISC::CRGBA CEntityCL::getColor () const // neutral if (isInSameLeague()) - return CRGBA::CRGBA(min(255, _GroupColor.R+50), min(255, _GroupColor.G+50), min(255, _GroupColor.B+50),_GroupColor.A); + return CRGBA(min(255, _GroupColor.R+50), min(255, _GroupColor.G+50), min(255, _GroupColor.B+50),_GroupColor.A); if (isInSameGuild()) return _GuildColor; diff --git a/code/ryzom/client/src/interface_v3/chat_text_manager.cpp b/code/ryzom/client/src/interface_v3/chat_text_manager.cpp index d1488b4dd..9b5113a55 100644 --- a/code/ryzom/client/src/interface_v3/chat_text_manager.cpp +++ b/code/ryzom/client/src/interface_v3/chat_text_manager.cpp @@ -148,7 +148,7 @@ CViewBase *CChatTextManager::createMsgText(const ucstring &cstMsg, NLMISC::CRGBA vt->setMultiLineSpace(getTextMultiLineSpace()); vt->setModulateGlobalColor(false); - ucstring cur_time = ""; + ucstring cur_time; static CCDBNodeLeaf* node = CInterfaceManager::getInstance()->getDbProp("UI:SAVE:CHAT:SHOW_TIMES_IN_CHAT_CB", false); if (node) { diff --git a/code/ryzom/client/src/interface_v3/chat_window.cpp b/code/ryzom/client/src/interface_v3/chat_window.cpp index 773992fc3..18be552e9 100644 --- a/code/ryzom/client/src/interface_v3/chat_window.cpp +++ b/code/ryzom/client/src/interface_v3/chat_window.cpp @@ -473,7 +473,7 @@ void CChatWindow::displayLocalPlayerTell(const ucstring &receiver, const ucstrin prop.readRGBA("UI:SAVE:CHAT:COLORS:SPEAKER"," "); encodeColorTag(prop.getRGBA(), finalMsg, false); - ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + ucstring csr(CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""); finalMsg += csr + CI18N::get("youTell") + ": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); encodeColorTag(prop.getRGBA(), finalMsg, true); diff --git a/code/ryzom/client/src/interface_v3/group_compas.cpp b/code/ryzom/client/src/interface_v3/group_compas.cpp index 0fa2b30e7..fa7655591 100644 --- a/code/ryzom/client/src/interface_v3/group_compas.cpp +++ b/code/ryzom/client/src/interface_v3/group_compas.cpp @@ -587,6 +587,12 @@ bool CGroupCompasMenu::parse(xmlNodePtr cur, CInterfaceGroup *parent /*=NULL*/) return true; } +// Helper for sorting landmarks +static inline bool UserLandMarksSortPredicate(const CUserLandMark& lm1, const CUserLandMark& lm2) +{ + return toLower(lm1.Title) < toLower(lm2.Title); +} + // *************************************************************************** // Called when we activate the compass menu void CGroupCompasMenu::setActive (bool state) @@ -803,6 +809,10 @@ void CGroupCompasMenu::setActive (bool state) } // User landmarks uint nbUserLandMarks = std::min( uint(currCont->UserLandMarks.size()), CContinent::getMaxNbUserLandMarks() ); + + // Sort the landmarks + std::sort(currCont->UserLandMarks.begin(), currCont->UserLandMarks.end(), UserLandMarksSortPredicate); + for(k = 0; k < nbUserLandMarks; ++k) { if (currCont->UserLandMarks[k].Type < CUserLandMark::UserLandMarkTypeCount) diff --git a/code/ryzom/client/src/interface_v3/group_html_webig.cpp b/code/ryzom/client/src/interface_v3/group_html_webig.cpp index e4a55ab48..0e17c47d3 100644 --- a/code/ryzom/client/src/interface_v3/group_html_webig.cpp +++ b/code/ryzom/client/src/interface_v3/group_html_webig.cpp @@ -74,6 +74,7 @@ void addWebIGParams (string &url, bool trustedDomain) string("shardid=") + toString(CharacterHomeSessionId) + string("&name=") + UserEntity->getLoginName().toUtf8() + string("&lang=") + CI18N::getCurrentLanguageCode() + + string("&datasetid=") + toString(UserEntity->dataSetId()) + string("&ig=1"); if (trustedDomain) { @@ -82,7 +83,6 @@ void addWebIGParams (string &url, bool trustedDomain) if (url.find('$') != string::npos) { - strFindReplace(url, "$datasetid$", toString(UserEntity->dataSetId())); strFindReplace(url, "$gender$", GSGENDER::toString(UserEntity->getGender())); strFindReplace(url, "$displayName$", UserEntity->getDisplayName().toString()); strFindReplace(url, "$posx$", toString(UserEntity->pos().x)); diff --git a/code/ryzom/client/src/interface_v3/lua_ihm.cpp b/code/ryzom/client/src/interface_v3/lua_ihm.cpp index 44e9a3ba7..08b128b66 100644 --- a/code/ryzom/client/src/interface_v3/lua_ihm.cpp +++ b/code/ryzom/client/src/interface_v3/lua_ihm.cpp @@ -1944,12 +1944,12 @@ static sint32 getTargetSlotNr() const char *dbPath = "UI:VARIABLES:TARGET:SLOT"; CInterfaceManager *im = CInterfaceManager::getInstance(); CCDBNodeLeaf *node = im->getDbProp(dbPath, false); - if (!node) return NULL; + if (!node) return 0; if ((uint8) node->getValue32() == (uint8) CLFECOMMON::INVALID_SLOT) { - return NULL; + return 0; } - return node->getValue32(); + return node->getValue32(); } // *************************************************************************** @@ -1963,13 +1963,13 @@ static CEntityCL *getTargetEntity() { return NULL; } - return EntitiesMngr.entity((uint) node->getValue32()); + return EntitiesMngr.entity((uint) node->getValue32()); } // *************************************************************************** static CEntityCL *getSlotEntity(uint slot) { - return EntitiesMngr.entity(slot); + return EntitiesMngr.entity(slot); } // *************************************************************************** @@ -2032,7 +2032,7 @@ sint32 CLuaIHM::getTargetLevel() ucstring CLuaIHM::getTargetSheet() { CEntityCL *target = getTargetEntity(); - if (!target) return ""; + if (!target) return ucstring(); return target->sheetId().toString(); } diff --git a/code/ryzom/client/src/interface_v3/people_list.cpp b/code/ryzom/client/src/interface_v3/people_list.cpp index c9b95a14a..395935aab 100644 --- a/code/ryzom/client/src/interface_v3/people_list.cpp +++ b/code/ryzom/client/src/interface_v3/people_list.cpp @@ -468,12 +468,12 @@ void CPeopleList::displayLocalPlayerTell(const ucstring &receiver, uint index, c return; } - ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + ucstring csr(CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""); ucstring finalMsg = csr + CI18N::get("youTell") + ": " + msg; // display msg with good color CInterfaceProperty prop; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); - + ucstring s = CI18N::get("youTellPlayer"); strFindReplace(s, "%name", receiver); strFindReplace(finalMsg, CI18N::get("youTell"), s); @@ -935,7 +935,7 @@ class CHandlerContactEntry : public IActionHandler ucstring final; CChatWindow::encodeColorTag(prop.getRGBA(), final, false); - ucstring csr = CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""; + ucstring csr(CHARACTER_TITLE::isCsrTitle(UserEntity->getTitleRaw()) ? "(CSR) " : ""); final += csr + CI18N::get("youTell")+": "; prop.readRGBA("UI:SAVE:CHAT:COLORS:TELL"," "); CChatWindow::encodeColorTag(prop.getRGBA(), final, true); diff --git a/code/ryzom/client/src/string_manager_client.h b/code/ryzom/client/src/string_manager_client.h index e5842a9f6..13f8188bb 100644 --- a/code/ryzom/client/src/string_manager_client.h +++ b/code/ryzom/client/src/string_manager_client.h @@ -107,7 +107,7 @@ public: // Get the Localized Title name static const ucchar *getTitleLocalizedName(const std::string &titleId, bool women); - static std::vector CStringManagerClient::getTitleInfos(const std::string &titleId, bool women); + static std::vector getTitleInfos(const std::string &titleId, bool women); // Get the Localized name of a classification type static const ucchar *getClassificationTypeLocalizedName(EGSPD::CClassificationType::TClassificationType type); From a09943f1f030ec6c62a70ccbea1c6c7f993d8805 Mon Sep 17 00:00:00 2001 From: kervala Date: Mon, 27 Feb 2012 23:48:36 +0100 Subject: [PATCH 183/215] Changed: #1433 Merge changes from patch 1.13 --- code/CMakeModules/nel.cmake | 6 +- code/nel/src/misc/events.cpp | 4 +- code/ryzom/client/client_default.cfg | 20 +- code/ryzom/client/client_default.cfg.in | 23 +- .../new_texture_interfaces_dxtc.tga | Bin 4194322 -> 4194322 bytes .../new_texture_interfaces_dxtc.txt | 1282 +++++++++-------- .../data/gamedev/interfaces_v3/commands.xml | 11 +- .../data/gamedev/interfaces_v3/config.xml | 65 +- .../gamedev/interfaces_v3/encyclopedia.xml | 2 +- .../gamedev/interfaces_v3/game_config.xml | 178 ++- .../data/gamedev/interfaces_v3/guild.xml | 53 +- .../gamedev/interfaces_v3/info_player.lua | 134 +- .../gamedev/interfaces_v3/info_player.xml | 278 +++- .../gamedev/interfaces_v3/interaction.xml | 28 +- .../data/gamedev/interfaces_v3/interface.txt | 1 + .../gamedev/interfaces_v3/out_v2_select.xml | 16 +- .../gamedev/interfaces_v3/out_v2_widgets.xml | 2 +- .../data/gamedev/interfaces_v3/player.lua | 41 +- .../data/gamedev/interfaces_v3/player.xml | 14 +- .../gamedev/interfaces_v3/player_trade.xml | 58 +- .../data/gamedev/interfaces_v3/reset.xml | 6 + .../interfaces_v3/texture_interfaces_v3.tga | Bin 2117952 -> 4194322 bytes .../data/gamedev/interfaces_v3/widgets.xml | 129 +- 23 files changed, 1478 insertions(+), 873 deletions(-) diff --git a/code/CMakeModules/nel.cmake b/code/CMakeModules/nel.cmake index 9314a7a85..7dd43e8b4 100644 --- a/code/CMakeModules/nel.cmake +++ b/code/CMakeModules/nel.cmake @@ -405,7 +405,7 @@ MACRO(NL_SETUP_BUILD) SET(NL_RELEASE_CFLAGS "/MD /D NDEBUG ${SPEED_OPTIMIZATIONS}") SET(NL_DEBUG_LINKFLAGS "/NODEFAULTLIB:msvcrt /INCREMENTAL:YES") SET(NL_RELEASE_LINKFLAGS "/OPT:REF /OPT:ICF /INCREMENTAL:NO") - ELSE(WIN32) + ELSE(MSVC) IF(HOST_CPU STREQUAL "x86_64" AND TARGET_CPU STREQUAL "x86") SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -m32 -march=i686") ENDIF(HOST_CPU STREQUAL "x86_64" AND TARGET_CPU STREQUAL "x86") @@ -414,7 +414,7 @@ MACRO(NL_SETUP_BUILD) SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -m64") ENDIF(HOST_CPU STREQUAL "x86" AND TARGET_CPU STREQUAL "x86_64") - SET(PLATFORM_CFLAGS "{PLATFORM_CFLAGS} -g -D_REENTRANT -pipe -ftemplate-depth-48 -Wall -ansi -W -Wpointer-arith -Wsign-compare -Wno-deprecated-declarations -Wno-multichar -Wno-unused -fno-strict-aliasing") + SET(PLATFORM_CFLAGS "${PLATFORM_CFLAGS} -g -D_REENTRANT -pipe -ftemplate-depth-48 -Wall -ansi -W -Wpointer-arith -Wsign-compare -Wno-deprecated-declarations -Wno-multichar -Wno-unused -fno-strict-aliasing") IF(WITH_COVERAGE) SET(PLATFORM_CFLAGS "-fprofile-arcs -ftest-coverage ${PLATFORM_CFLAGS}") @@ -437,7 +437,7 @@ MACRO(NL_SETUP_BUILD) SET(NL_DEBUG_CFLAGS "-DNL_DEBUG -D_DEBUG") SET(NL_RELEASE_CFLAGS "-DNL_RELEASE -DNDEBUG -O6") - ENDIF(WIN32) + ENDIF(MSVC) ENDMACRO(NL_SETUP_BUILD) MACRO(NL_SETUP_BUILD_FLAGS) diff --git a/code/nel/src/misc/events.cpp b/code/nel/src/misc/events.cpp index 4d0f996d5..7cef02f44 100644 --- a/code/nel/src/misc/events.cpp +++ b/code/nel/src/misc/events.cpp @@ -178,8 +178,8 @@ static const CStringConversion::CPair stringTable [] = { "KeyZOOM", KeyZOOM }, { "KeyNONAME", KeyNONAME }, { "KeyPA1", KeyPA1 }, - { "KeyOEM_CLEAR", KeyOEM_CLEAR }, -} + { "KeyOEM_CLEAR", KeyOEM_CLEAR } +}; static CStringConversion KeyConversion(stringTable, sizeof(stringTable) / sizeof(stringTable[0]), KeyCount); diff --git a/code/ryzom/client/client_default.cfg b/code/ryzom/client/client_default.cfg index 14885fa01..997699ca8 100644 --- a/code/ryzom/client/client_default.cfg +++ b/code/ryzom/client/client_default.cfg @@ -354,8 +354,8 @@ SystemInfoColors = // NEW System Info Categories "SYS", "255 255 255 255 normal", // Default system messages -"BC", "255 255 255 255 center", // Broadcast messages -"TAGBC", "255 255 255 255 center", // Taged broadcast messages : color should remain white as some word are tagged +"BC", "255 255 255 255 centeraround", // Broadcast messages +"TAGBC", "255 255 255 255 centeraround", // Taged broadcast messages : color should remain white as some word are tagged "XP", "255 255 64 255 over", // XP Gain "SP", "255 255 64 255 over", // SP Gain "TTL", "255 255 64 255 over", // Title @@ -443,23 +443,24 @@ R2EDReloadFiles = { XMLInterfaceFiles = { "config.xml", "widgets.xml", + "webig_widgets.xml", "player.xml", "inventory.xml", "interaction.xml", - "phrase.xml", + "phrase.xml", "harvest.xml", "macros.xml", "info_player.xml", "outpost.xml", "guild.xml", "taskbar.xml", - "game_config.xml", + "game_config.xml", "game_context_menu.xml", "player_trade.xml", "bot_chat_v4.xml", "compass.xml", "map.xml", - "hierarchy.xml", + "hierarchy.xml", "reset.xml", "actions.xml", "help.xml", @@ -559,6 +560,15 @@ HelpPages = "de=http://forums.ryzom.com/forum/showthread.php?t=29131" }; +WebIgMainDomain = "atys.ryzom.com"; + +WebIgTrustedDomains = +{ + "atys.ryzom.com" +}; + +PatchletUrl = "http://atys.ryzom.com/start/app_patchlet.php?patch=preload"; + SelectedSlot = 0; BuildName = "RELEASE_HEAD"; diff --git a/code/ryzom/client/client_default.cfg.in b/code/ryzom/client/client_default.cfg.in index 8ae0117a5..41c3dd1de 100644 --- a/code/ryzom/client/client_default.cfg.in +++ b/code/ryzom/client/client_default.cfg.in @@ -80,6 +80,9 @@ XMLOutGameInterfaceFiles = { "out_v2_keys.xml", }; +TexturesInterface = "texture_interfaces_v3"; +TexturesInterfaceDXTC = "texture_interfaces_dxtc"; + // The ligo primitive class file LigoPrimitiveClass = "world_editor_classes.xml"; @@ -352,8 +355,8 @@ SystemInfoColors = // NEW System Info Categories "SYS", "255 255 255 255 normal", // Default system messages -"BC", "255 255 255 255 center", // Broadcast messages -"TAGBC", "255 255 255 255 center", // Taged broadcast messages : color should remain white as some word are tagged +"BC", "255 255 255 255 centeraround", // Broadcast messages +"TAGBC", "255 255 255 255 centeraround", // Taged broadcast messages : color should remain white as some word are tagged "XP", "255 255 64 255 over", // XP Gain "SP", "255 255 64 255 over", // SP Gain "TTL", "255 255 64 255 over", // Title @@ -441,23 +444,24 @@ R2EDReloadFiles = { XMLInterfaceFiles = { "config.xml", "widgets.xml", + "webig_widgets.xml", "player.xml", "inventory.xml", "interaction.xml", - "phrase.xml", + "phrase.xml", "harvest.xml", "macros.xml", "info_player.xml", "outpost.xml", "guild.xml", "taskbar.xml", - "game_config.xml", + "game_config.xml", "game_context_menu.xml", "player_trade.xml", "bot_chat_v4.xml", "compass.xml", "map.xml", - "hierarchy.xml", + "hierarchy.xml", "reset.xml", "actions.xml", "help.xml", @@ -557,6 +561,15 @@ HelpPages = "de=http://forums.ryzom.com/forum/showthread.php?t=29131" }; +WebIgMainDomain = "atys.ryzom.com"; + +WebIgTrustedDomains = +{ + "atys.ryzom.com" +}; + +PatchletUrl = "http://atys.ryzom.com/start/app_patchlet.php?patch=preload"; + SelectedSlot = 0; BuildName = "RELEASE_HEAD"; diff --git a/code/ryzom/client/data/gamedev/adds/interfaces/new_texture_interfaces_dxtc.tga b/code/ryzom/client/data/gamedev/adds/interfaces/new_texture_interfaces_dxtc.tga index bbeeab27b137f23de2ef17354622069d33f7c77c..91e87e2e7ca33bf8fcad0846b7f2d90a0849095f 100644 GIT binary patch delta 75237 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 wOzxN4!CoHX0h+d&k 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
{Fb24wo2>~lVm~rbn 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 Jr8B}>{6B`FHJ$(f delta 55962 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 diff --git a/code/ryzom/client/data/gamedev/adds/interfaces/new_texture_interfaces_dxtc.txt b/code/ryzom/client/data/gamedev/adds/interfaces/new_texture_interfaces_dxtc.txt index ec1afb449..f35989e33 100644 --- a/code/ryzom/client/data/gamedev/adds/interfaces/new_texture_interfaces_dxtc.txt +++ b/code/ryzom/client/data/gamedev/adds/interfaces/new_texture_interfaces_dxtc.txt @@ -18,305 +18,305 @@ BK_goo.tga 0.156250000000 0.078125000000 0.195312500000 0.117187500000 bk_guild.tga 0.195312500000 0.078125000000 0.234375000000 0.117187500000 bk_horde.tga 0.000000000000 0.117187500000 0.039062500000 0.156250000000 bk_kami.tga 0.039062500000 0.117187500000 0.078125000000 0.156250000000 -bk_karavan.tga 0.078125000000 0.117187500000 0.117187500000 0.156250000000 -BK_matis.tga 0.117187500000 0.117187500000 0.156250000000 0.156250000000 -bk_mission.tga 0.156250000000 0.117187500000 0.195312500000 0.156250000000 -bk_mission2.tga 0.195312500000 0.117187500000 0.234375000000 0.156250000000 -BK_outpost.tga 0.000000000000 0.156250000000 0.039062500000 0.195312500000 -BK_primes.tga 0.039062500000 0.156250000000 0.078125000000 0.195312500000 -bk_service.tga 0.078125000000 0.156250000000 0.117187500000 0.195312500000 -bk_training.tga 0.117187500000 0.156250000000 0.156250000000 0.195312500000 -BK_tryker.tga 0.156250000000 0.156250000000 0.195312500000 0.195312500000 -BK_zorai.tga 0.195312500000 0.156250000000 0.234375000000 0.195312500000 -charge.tga 0.000000000000 0.195312500000 0.039062500000 0.234375000000 -clef.tga 0.039062500000 0.195312500000 0.078125000000 0.234375000000 -conso_branche.tga 0.078125000000 0.195312500000 0.117187500000 0.234375000000 -conso_branche_mask.tga 0.117187500000 0.195312500000 0.156250000000 0.234375000000 -conso_fleur.tga 0.156250000000 0.195312500000 0.195312500000 0.234375000000 -conso_fleur_mask.tga 0.195312500000 0.195312500000 0.234375000000 0.234375000000 -conso_grappe.tga 0.242187500000 0.000000000000 0.281250000000 0.039062500000 -conso_grappe_mask.tga 0.281250000000 0.000000000000 0.320312500000 0.039062500000 -conso_nectar.tga 0.320312500000 0.000000000000 0.359375000000 0.039062500000 -conso_nectar_mask.tga 0.359375000000 0.000000000000 0.398437500000 0.039062500000 -construction.tga 0.398437500000 0.000000000000 0.437500000000 0.039062500000 -cristal_ammo.tga 0.437500000000 0.000000000000 0.476562500000 0.039062500000 -cristal_spell.tga 0.238281250000 0.039062500000 0.277343750000 0.078125000000 -ge_mission_outpost_townhall.tga 0.277343750000 0.039062500000 0.316406250000 0.078125000000 -ico_amande.tga 0.316406250000 0.039062500000 0.355468750000 0.078125000000 -ico_cataliseur_xp.tga 0.355468750000 0.039062500000 0.394531250000 0.078125000000 -ico_consommable_over.tga 0.394531250000 0.039062500000 0.433593750000 0.078125000000 -ico_fleur_carac_1.tga 0.433593750000 0.039062500000 0.472656250000 0.078125000000 -ico_fleur_carac_1_mask.tga 0.234375000000 0.078125000000 0.273437500000 0.117187500000 -ico_fleur_carac_2.tga 0.273437500000 0.078125000000 0.312500000000 0.117187500000 -ico_fleur_carac_2_mask.tga 0.312500000000 0.078125000000 0.351562500000 0.117187500000 -ico_fleur_carac_3.tga 0.351562500000 0.078125000000 0.390625000000 0.117187500000 -ico_fleur_carac_3_mask.tga 0.390625000000 0.078125000000 0.429687500000 0.117187500000 -ico_foreuse.tga 0.429687500000 0.078125000000 0.468750000000 0.117187500000 -ico_haircolor.tga 0.234375000000 0.117187500000 0.273437500000 0.156250000000 -ico_haircut.tga 0.273437500000 0.117187500000 0.312500000000 0.156250000000 -ico_mission_art_fyros.tga 0.312500000000 0.117187500000 0.351562500000 0.156250000000 -ico_mission_art_matis.tga 0.351562500000 0.117187500000 0.390625000000 0.156250000000 -ico_mission_art_tryker.tga 0.390625000000 0.117187500000 0.429687500000 0.156250000000 -ico_mission_art_zorai.tga 0.429687500000 0.117187500000 0.468750000000 0.156250000000 -ico_mission_barrel.tga 0.234375000000 0.156250000000 0.273437500000 0.195312500000 -ico_mission_bottle.tga 0.273437500000 0.156250000000 0.312500000000 0.195312500000 -ico_mission_casket.tga 0.312500000000 0.156250000000 0.351562500000 0.195312500000 -ico_mission_medicine.tga 0.351562500000 0.156250000000 0.390625000000 0.195312500000 -ico_mission_message.tga 0.390625000000 0.156250000000 0.429687500000 0.195312500000 -ico_mission_package.tga 0.429687500000 0.156250000000 0.468750000000 0.195312500000 -ico_mission_pot.tga 0.234375000000 0.195312500000 0.273437500000 0.234375000000 -ico_mission_purse.tga 0.273437500000 0.195312500000 0.312500000000 0.234375000000 -ico_noix.tga 0.312500000000 0.195312500000 0.351562500000 0.234375000000 -ico_racine.tga 0.351562500000 0.195312500000 0.390625000000 0.234375000000 -ico_spores.tga 0.390625000000 0.195312500000 0.429687500000 0.234375000000 -ico_task_craft.tga 0.429687500000 0.195312500000 0.468750000000 0.234375000000 -ico_task_done.tga 0.000000000000 0.234375000000 0.039062500000 0.273437500000 -ico_task_failed.tga 0.039062500000 0.234375000000 0.078125000000 0.273437500000 -ico_task_fight.tga 0.078125000000 0.234375000000 0.117187500000 0.273437500000 -ico_task_forage.tga 0.117187500000 0.234375000000 0.156250000000 0.273437500000 -ico_task_generic.tga 0.156250000000 0.234375000000 0.195312500000 0.273437500000 -ico_task_generic_quart.tga 0.195312500000 0.234375000000 0.234375000000 0.273437500000 -ico_task_guild.tga 0.234375000000 0.234375000000 0.273437500000 0.273437500000 -ico_task_rite.tga 0.273437500000 0.234375000000 0.312500000000 0.273437500000 -ico_task_travel.tga 0.312500000000 0.234375000000 0.351562500000 0.273437500000 -ico_tatoo.tga 0.351562500000 0.234375000000 0.390625000000 0.273437500000 -ico_tourbe.tga 0.390625000000 0.234375000000 0.429687500000 0.273437500000 -improved_tool.tga 0.429687500000 0.234375000000 0.468750000000 0.273437500000 -item_default.tga 0.000000000000 0.273437500000 0.039062500000 0.312500000000 -item_plan_over.tga 0.039062500000 0.273437500000 0.078125000000 0.312500000000 -lucky_flower.tga 0.078125000000 0.273437500000 0.117187500000 0.312500000000 -mektoub_pack.tga 0.117187500000 0.273437500000 0.156250000000 0.312500000000 -mektoub_steed.tga 0.156250000000 0.273437500000 0.195312500000 0.312500000000 -mg_glove.tga 0.195312500000 0.273437500000 0.234375000000 0.312500000000 -mission_icon_0.tga 0.234375000000 0.273437500000 0.273437500000 0.312500000000 -mission_icon_1.tga 0.273437500000 0.273437500000 0.312500000000 0.312500000000 -mission_icon_2.tga 0.312500000000 0.273437500000 0.351562500000 0.312500000000 -mission_icon_3.tga 0.351562500000 0.273437500000 0.390625000000 0.312500000000 -mp_amber.tga 0.390625000000 0.273437500000 0.429687500000 0.312500000000 -mp_bark.tga 0.429687500000 0.273437500000 0.468750000000 0.312500000000 -mp_batiment_brique.tga 0.000000000000 0.312500000000 0.039062500000 0.351562500000 -mp_batiment_colonne.tga 0.039062500000 0.312500000000 0.078125000000 0.351562500000 -mp_batiment_colonne_justice.tga 0.078125000000 0.312500000000 0.117187500000 0.351562500000 -mp_batiment_comble.tga 0.117187500000 0.312500000000 0.156250000000 0.351562500000 -mp_batiment_noyau_maduk.tga 0.156250000000 0.312500000000 0.195312500000 0.351562500000 -mp_batiment_ornement.tga 0.195312500000 0.312500000000 0.234375000000 0.351562500000 -mp_batiment_revetement.tga 0.234375000000 0.312500000000 0.273437500000 0.351562500000 -mp_batiment_socle.tga 0.273437500000 0.312500000000 0.312500000000 0.351562500000 -mp_batiment_statue.tga 0.312500000000 0.312500000000 0.351562500000 0.351562500000 -mp_beak.tga 0.351562500000 0.312500000000 0.390625000000 0.351562500000 -mp_blood.tga 0.390625000000 0.312500000000 0.429687500000 0.351562500000 -mp_bone.tga 0.429687500000 0.312500000000 0.468750000000 0.351562500000 -mp_bud.tga 0.000000000000 0.351562500000 0.039062500000 0.390625000000 -mp_buterfly_blue.tga 0.039062500000 0.351562500000 0.078125000000 0.390625000000 -mp_buterfly_cocoon.tga 0.078125000000 0.351562500000 0.117187500000 0.390625000000 -mp_cereal.tga 0.117187500000 0.351562500000 0.156250000000 0.390625000000 -mp_claw.tga 0.156250000000 0.351562500000 0.195312500000 0.390625000000 -mp_dandelion.tga 0.195312500000 0.351562500000 0.234375000000 0.390625000000 -mp_dust.tga 0.234375000000 0.351562500000 0.273437500000 0.390625000000 -mp_egg.tga 0.273437500000 0.351562500000 0.312500000000 0.390625000000 -mp_eyes.tga 0.312500000000 0.351562500000 0.351562500000 0.390625000000 -mp_fang.tga 0.351562500000 0.351562500000 0.390625000000 0.390625000000 -mp_fiber.tga 0.390625000000 0.351562500000 0.429687500000 0.390625000000 -mp_filament.tga 0.429687500000 0.351562500000 0.468750000000 0.390625000000 -mp_firefly_abdomen.tga 0.000000000000 0.390625000000 0.039062500000 0.429687500000 -mp_fish_scale.tga 0.039062500000 0.390625000000 0.078125000000 0.429687500000 -mp_flowers.tga 0.078125000000 0.390625000000 0.117187500000 0.429687500000 -mp_fresh_loose_soil.tga 0.117187500000 0.390625000000 0.156250000000 0.429687500000 -mp_fruit.tga 0.156250000000 0.390625000000 0.195312500000 0.429687500000 -mp_generic.tga 0.195312500000 0.390625000000 0.234375000000 0.429687500000 -mp_generic_colorize.tga 0.234375000000 0.390625000000 0.273437500000 0.429687500000 -mp_gomme.tga 0.273437500000 0.390625000000 0.312500000000 0.429687500000 -mp_goo_residue.tga 0.312500000000 0.390625000000 0.351562500000 0.429687500000 -mp_hairs.tga 0.351562500000 0.390625000000 0.390625000000 0.429687500000 -mp_hoof.tga 0.390625000000 0.390625000000 0.429687500000 0.429687500000 -mp_horn.tga 0.429687500000 0.390625000000 0.468750000000 0.429687500000 -mp_horney.tga 0.000000000000 0.429687500000 0.039062500000 0.468750000000 -mp_insect_fossil.tga 0.039062500000 0.429687500000 0.078125000000 0.468750000000 -mp_kitinshell.tga 0.078125000000 0.429687500000 0.117187500000 0.468750000000 -mp_kitin_flesh.tga 0.117187500000 0.429687500000 0.156250000000 0.468750000000 -mp_kitin_secretion.tga 0.156250000000 0.429687500000 0.195312500000 0.468750000000 -mp_larva.tga 0.195312500000 0.429687500000 0.234375000000 0.468750000000 -mp_leaf.tga 0.234375000000 0.429687500000 0.273437500000 0.468750000000 -mp_leather.tga 0.273437500000 0.429687500000 0.312500000000 0.468750000000 -mp_liane.tga 0.312500000000 0.429687500000 0.351562500000 0.468750000000 -mp_lichen.tga 0.351562500000 0.429687500000 0.390625000000 0.468750000000 -mp_ligament.tga 0.390625000000 0.429687500000 0.429687500000 0.468750000000 -mp_mandible.tga 0.429687500000 0.429687500000 0.468750000000 0.468750000000 -mp_meat.tga 0.476562500000 0.000000000000 0.515625000000 0.039062500000 -mp_moss.tga 0.515625000000 0.000000000000 0.554687500000 0.039062500000 -mp_mushroom.tga 0.554687500000 0.000000000000 0.593750000000 0.039062500000 -mp_nail.tga 0.593750000000 0.000000000000 0.632812500000 0.039062500000 -mp_oil.tga 0.632812500000 0.000000000000 0.671875000000 0.039062500000 -mp_parasite.tga 0.671875000000 0.000000000000 0.710937500000 0.039062500000 -mp_pearl.tga 0.710937500000 0.000000000000 0.750000000000 0.039062500000 -mp_pelvis.tga 0.750000000000 0.000000000000 0.789062500000 0.039062500000 -mp_pigment.tga 0.789062500000 0.000000000000 0.828125000000 0.039062500000 -mp_pistil.tga 0.828125000000 0.000000000000 0.867187500000 0.039062500000 -mp_plant_fossil.tga 0.867187500000 0.000000000000 0.906250000000 0.039062500000 -mp_pollen.tga 0.906250000000 0.000000000000 0.945312500000 0.039062500000 -mp_resin.tga 0.945312500000 0.000000000000 0.984375000000 0.039062500000 -mp_ronce.tga 0.472656250000 0.039062500000 0.511718750000 0.078125000000 -mp_rostrum.tga 0.511718750000 0.039062500000 0.550781250000 0.078125000000 -mp_sap.tga 0.550781250000 0.039062500000 0.589843750000 0.078125000000 -mp_sawdust.tga 0.589843750000 0.039062500000 0.628906250000 0.078125000000 -mp_seed.tga 0.628906250000 0.039062500000 0.667968750000 0.078125000000 -mp_shell.tga 0.667968750000 0.039062500000 0.707031250000 0.078125000000 -mp_silk_worm.tga 0.707031250000 0.039062500000 0.746093750000 0.078125000000 -mp_skin.tga 0.746093750000 0.039062500000 0.785156250000 0.078125000000 -mp_skull.tga 0.785156250000 0.039062500000 0.824218750000 0.078125000000 -mp_spiders_web.tga 0.824218750000 0.039062500000 0.863281250000 0.078125000000 -mp_spine.tga 0.863281250000 0.039062500000 0.902343750000 0.078125000000 -mp_stem.tga 0.902343750000 0.039062500000 0.941406250000 0.078125000000 -mp_sting.tga 0.941406250000 0.039062500000 0.980468750000 0.078125000000 -mp_straw.tga 0.468750000000 0.078125000000 0.507812500000 0.117187500000 -mp_suc.tga 0.507812500000 0.078125000000 0.546875000000 0.117187500000 -mp_tail.tga 0.546875000000 0.078125000000 0.585937500000 0.117187500000 -mp_tooth.tga 0.585937500000 0.078125000000 0.625000000000 0.117187500000 -mp_trunk.tga 0.625000000000 0.078125000000 0.664062500000 0.117187500000 -mp_whiskers.tga 0.664062500000 0.078125000000 0.703125000000 0.117187500000 -mp_wing.tga 0.703125000000 0.078125000000 0.742187500000 0.117187500000 -mp_wood.tga 0.742187500000 0.078125000000 0.781250000000 0.117187500000 -mp_wood_node.tga 0.781250000000 0.078125000000 0.820312500000 0.117187500000 -MW_2h_axe.tga 0.820312500000 0.078125000000 0.859375000000 0.117187500000 -MW_2h_lance.tga 0.859375000000 0.078125000000 0.898437500000 0.117187500000 -MW_2h_mace.tga 0.898437500000 0.078125000000 0.937500000000 0.117187500000 -MW_2h_sword.tga 0.937500000000 0.078125000000 0.976562500000 0.117187500000 -MW_axe.tga 0.468750000000 0.117187500000 0.507812500000 0.156250000000 -MW_dagger.tga 0.507812500000 0.117187500000 0.546875000000 0.156250000000 -MW_lance.tga 0.546875000000 0.117187500000 0.585937500000 0.156250000000 -MW_mace.tga 0.585937500000 0.117187500000 0.625000000000 0.156250000000 -MW_staff.tga 0.625000000000 0.117187500000 0.664062500000 0.156250000000 -MW_sword.tga 0.664062500000 0.117187500000 0.703125000000 0.156250000000 -PA_anklet.tga 0.703125000000 0.117187500000 0.742187500000 0.156250000000 -PA_bracelet.tga 0.742187500000 0.117187500000 0.781250000000 0.156250000000 -PA_diadem.tga 0.781250000000 0.117187500000 0.820312500000 0.156250000000 -PA_earring.tga 0.820312500000 0.117187500000 0.859375000000 0.156250000000 -PA_pendant.tga 0.859375000000 0.117187500000 0.898437500000 0.156250000000 -PA_ring.tga 0.898437500000 0.117187500000 0.937500000000 0.156250000000 -protect_amber.tga 0.937500000000 0.117187500000 0.976562500000 0.156250000000 -pvp_aura.tga 0.468750000000 0.156250000000 0.507812500000 0.195312500000 -pvp_aura_mask.tga 0.507812500000 0.156250000000 0.546875000000 0.195312500000 -pvp_boost.tga 0.546875000000 0.156250000000 0.585937500000 0.195312500000 -pvp_boost_mask.tga 0.585937500000 0.156250000000 0.625000000000 0.195312500000 -pw_4.tga 0.625000000000 0.156250000000 0.664062500000 0.195312500000 -pw_5.tga 0.664062500000 0.156250000000 0.703125000000 0.195312500000 -pw_6.tga 0.703125000000 0.156250000000 0.742187500000 0.195312500000 -pw_7.tga 0.742187500000 0.156250000000 0.781250000000 0.195312500000 -PW_heavy.tga 0.781250000000 0.156250000000 0.820312500000 0.195312500000 -PW_light.tga 0.820312500000 0.156250000000 0.859375000000 0.195312500000 -PW_medium.tga 0.859375000000 0.156250000000 0.898437500000 0.195312500000 -quest_coeur.tga 0.898437500000 0.156250000000 0.937500000000 0.195312500000 -quest_foie.tga 0.937500000000 0.156250000000 0.976562500000 0.195312500000 -quest_jeton.tga 0.468750000000 0.195312500000 0.507812500000 0.234375000000 -quest_langue.tga 0.507812500000 0.195312500000 0.546875000000 0.234375000000 -quest_louche.tga 0.546875000000 0.195312500000 0.585937500000 0.234375000000 -quest_oreille.tga 0.585937500000 0.195312500000 0.625000000000 0.234375000000 -quest_patte.tga 0.625000000000 0.195312500000 0.664062500000 0.234375000000 -quest_poils.tga 0.664062500000 0.195312500000 0.703125000000 0.234375000000 -quest_queue.tga 0.703125000000 0.195312500000 0.742187500000 0.234375000000 -quest_ticket.tga 0.742187500000 0.195312500000 0.781250000000 0.234375000000 -AM_logo.tga 0.781250000000 0.195312500000 0.820312500000 0.234375000000 -AR_armpad.tga 0.820312500000 0.195312500000 0.859375000000 0.234375000000 -ar_armpad_mask.tga 0.859375000000 0.195312500000 0.898437500000 0.234375000000 -requirement.tga 0.898437500000 0.195312500000 0.937500000000 0.234375000000 -rm_f.tga 0.937500000000 0.195312500000 0.976562500000 0.234375000000 -rm_f_upgrade.tga 0.468750000000 0.234375000000 0.507812500000 0.273437500000 -rm_h.tga 0.507812500000 0.234375000000 0.546875000000 0.273437500000 -rm_h_upgrade.tga 0.546875000000 0.234375000000 0.585937500000 0.273437500000 -rm_m.tga 0.585937500000 0.234375000000 0.625000000000 0.273437500000 -rm_m_upgrade.tga 0.625000000000 0.234375000000 0.664062500000 0.273437500000 -rm_r.tga 0.664062500000 0.234375000000 0.703125000000 0.273437500000 -rm_r_upgrade.tga 0.703125000000 0.234375000000 0.742187500000 0.273437500000 -rpjobitem_200_a.tga 0.742187500000 0.234375000000 0.781250000000 0.273437500000 -rpjobitem_200_b.tga 0.781250000000 0.234375000000 0.820312500000 0.273437500000 -rpjobitem_200_c.tga 0.820312500000 0.234375000000 0.859375000000 0.273437500000 -rpjobitem_201_a.tga 0.859375000000 0.234375000000 0.898437500000 0.273437500000 -rpjobitem_201_b.tga 0.898437500000 0.234375000000 0.937500000000 0.273437500000 -rpjobitem_201_c.tga 0.937500000000 0.234375000000 0.976562500000 0.273437500000 -rpjobitem_202_a.tga 0.468750000000 0.273437500000 0.507812500000 0.312500000000 -rpjobitem_202_b.tga 0.507812500000 0.273437500000 0.546875000000 0.312500000000 -rpjobitem_202_c.tga 0.546875000000 0.273437500000 0.585937500000 0.312500000000 -rpjobitem_203_a.tga 0.585937500000 0.273437500000 0.625000000000 0.312500000000 -rpjobitem_203_b.tga 0.625000000000 0.273437500000 0.664062500000 0.312500000000 -rpjobitem_203_c.tga 0.664062500000 0.273437500000 0.703125000000 0.312500000000 -rpjobitem_204_a.tga 0.703125000000 0.273437500000 0.742187500000 0.312500000000 -rpjobitem_204_b.tga 0.742187500000 0.273437500000 0.781250000000 0.312500000000 -rpjobitem_204_c.tga 0.781250000000 0.273437500000 0.820312500000 0.312500000000 -rpjobitem_205_a.tga 0.820312500000 0.273437500000 0.859375000000 0.312500000000 -rpjobitem_205_b.tga 0.859375000000 0.273437500000 0.898437500000 0.312500000000 -rpjobitem_205_c.tga 0.898437500000 0.273437500000 0.937500000000 0.312500000000 -rpjobitem_206_a.tga 0.937500000000 0.273437500000 0.976562500000 0.312500000000 -rpjobitem_206_b.tga 0.468750000000 0.312500000000 0.507812500000 0.351562500000 -rpjobitem_206_c.tga 0.507812500000 0.312500000000 0.546875000000 0.351562500000 -rpjobitem_207_a.tga 0.546875000000 0.312500000000 0.585937500000 0.351562500000 -rpjobitem_207_b.tga 0.585937500000 0.312500000000 0.625000000000 0.351562500000 -rpjobitem_207_c.tga 0.625000000000 0.312500000000 0.664062500000 0.351562500000 -rpjobitem_certifications.tga 0.664062500000 0.312500000000 0.703125000000 0.351562500000 -rpjob_200.tga 0.703125000000 0.312500000000 0.742187500000 0.351562500000 -rpjob_201.tga 0.742187500000 0.312500000000 0.781250000000 0.351562500000 -rpjob_202.tga 0.781250000000 0.312500000000 0.820312500000 0.351562500000 -rpjob_203.tga 0.820312500000 0.312500000000 0.859375000000 0.351562500000 -rpjob_204.tga 0.859375000000 0.312500000000 0.898437500000 0.351562500000 -rpjob_205.tga 0.898437500000 0.312500000000 0.937500000000 0.351562500000 -rpjob_206.tga 0.937500000000 0.312500000000 0.976562500000 0.351562500000 -rpjob_207.tga 0.468750000000 0.351562500000 0.507812500000 0.390625000000 -rpjob_advanced.tga 0.507812500000 0.351562500000 0.546875000000 0.390625000000 -rpjob_elementary.tga 0.546875000000 0.351562500000 0.585937500000 0.390625000000 -rpjob_roleplay.tga 0.585937500000 0.351562500000 0.625000000000 0.390625000000 -rpjob_task.tga 0.625000000000 0.351562500000 0.664062500000 0.390625000000 -rpjob_task_certificats.tga 0.664062500000 0.351562500000 0.703125000000 0.390625000000 -rpjob_task_convert.tga 0.703125000000 0.351562500000 0.742187500000 0.390625000000 -rpjob_task_elementary.tga 0.742187500000 0.351562500000 0.781250000000 0.390625000000 -rpjob_task_generic.tga 0.781250000000 0.351562500000 0.820312500000 0.390625000000 -rpjob_task_upgrade.tga 0.820312500000 0.351562500000 0.859375000000 0.390625000000 -RW_autolaunch.tga 0.859375000000 0.351562500000 0.898437500000 0.390625000000 -RW_bowgun.tga 0.898437500000 0.351562500000 0.937500000000 0.390625000000 -RW_grenade.tga 0.937500000000 0.351562500000 0.976562500000 0.390625000000 -RW_harpoongun.tga 0.468750000000 0.390625000000 0.507812500000 0.429687500000 -RW_launcher.tga 0.507812500000 0.390625000000 0.546875000000 0.429687500000 -RW_pistol.tga 0.546875000000 0.390625000000 0.585937500000 0.429687500000 -RW_pistolarc.tga 0.585937500000 0.390625000000 0.625000000000 0.429687500000 -RW_rifle.tga 0.625000000000 0.390625000000 0.664062500000 0.429687500000 -SH_buckler.tga 0.664062500000 0.390625000000 0.703125000000 0.429687500000 -SH_large_shield.tga 0.703125000000 0.390625000000 0.742187500000 0.429687500000 -spe_beast.tga 0.742187500000 0.390625000000 0.781250000000 0.429687500000 -spe_com.tga 0.781250000000 0.390625000000 0.820312500000 0.429687500000 -spe_inventory.tga 0.820312500000 0.390625000000 0.859375000000 0.429687500000 -spe_labs.tga 0.859375000000 0.390625000000 0.898437500000 0.429687500000 -spe_memory.tga 0.898437500000 0.390625000000 0.937500000000 0.429687500000 -spe_options.tga 0.937500000000 0.390625000000 0.976562500000 0.429687500000 -spe_status.tga 0.468750000000 0.429687500000 0.507812500000 0.468750000000 -stimulating_water.tga 0.507812500000 0.429687500000 0.546875000000 0.468750000000 -tetekitin.tga 0.546875000000 0.429687500000 0.585937500000 0.468750000000 -to_ammo.tga 0.585937500000 0.429687500000 0.625000000000 0.468750000000 -to_armor.tga 0.625000000000 0.429687500000 0.664062500000 0.468750000000 -to_cooking_pot.tga 0.664062500000 0.429687500000 0.703125000000 0.468750000000 -to_fishing_rod.tga 0.703125000000 0.429687500000 0.742187500000 0.468750000000 -to_forage.tga 0.742187500000 0.429687500000 0.781250000000 0.468750000000 -to_hammer.tga 0.781250000000 0.429687500000 0.820312500000 0.468750000000 -to_jewelry_hammer.tga 0.820312500000 0.429687500000 0.859375000000 0.468750000000 -to_jewels.tga 0.859375000000 0.429687500000 0.898437500000 0.468750000000 -to_leathercutter.tga 0.898437500000 0.429687500000 0.937500000000 0.468750000000 -to_melee.tga 0.937500000000 0.429687500000 0.976562500000 0.468750000000 -to_needle.tga 0.000000000000 0.468750000000 0.039062500000 0.507812500000 -to_pestle.tga 0.039062500000 0.468750000000 0.078125000000 0.507812500000 -to_range.tga 0.078125000000 0.468750000000 0.117187500000 0.507812500000 -to_searake.tga 0.117187500000 0.468750000000 0.156250000000 0.507812500000 -to_spade.tga 0.156250000000 0.468750000000 0.195312500000 0.507812500000 -to_stick.tga 0.195312500000 0.468750000000 0.234375000000 0.507812500000 -to_tunneling_knife.tga 0.234375000000 0.468750000000 0.273437500000 0.507812500000 -to_whip.tga 0.273437500000 0.468750000000 0.312500000000 0.507812500000 -to_wrench.tga 0.312500000000 0.468750000000 0.351562500000 0.507812500000 -TP_caravane.tga 0.351562500000 0.468750000000 0.390625000000 0.507812500000 -TP_kami.tga 0.390625000000 0.468750000000 0.429687500000 0.507812500000 -W_AM_logo.tga 0.429687500000 0.468750000000 0.468750000000 0.507812500000 -w_pa_anklet.tga 0.468750000000 0.468750000000 0.507812500000 0.507812500000 -w_pa_bracelet.tga 0.507812500000 0.468750000000 0.546875000000 0.507812500000 -w_pa_diadem.tga 0.546875000000 0.468750000000 0.585937500000 0.507812500000 -w_pa_earring.tga 0.585937500000 0.468750000000 0.625000000000 0.507812500000 -w_pa_pendant.tga 0.625000000000 0.468750000000 0.664062500000 0.507812500000 -w_pa_ring.tga 0.664062500000 0.468750000000 0.703125000000 0.507812500000 -xp_cat_green.tga 0.703125000000 0.468750000000 0.742187500000 0.507812500000 +BK_matis.tga 0.078125000000 0.117187500000 0.117187500000 0.156250000000 +bk_mission.tga 0.117187500000 0.117187500000 0.156250000000 0.156250000000 +bk_mission2.tga 0.156250000000 0.117187500000 0.195312500000 0.156250000000 +BK_outpost.tga 0.195312500000 0.117187500000 0.234375000000 0.156250000000 +BK_primes.tga 0.000000000000 0.156250000000 0.039062500000 0.195312500000 +bk_service.tga 0.039062500000 0.156250000000 0.078125000000 0.195312500000 +bk_training.tga 0.078125000000 0.156250000000 0.117187500000 0.195312500000 +BK_tryker.tga 0.117187500000 0.156250000000 0.156250000000 0.195312500000 +BK_zorai.tga 0.156250000000 0.156250000000 0.195312500000 0.195312500000 +charge.tga 0.195312500000 0.156250000000 0.234375000000 0.195312500000 +clef.tga 0.000000000000 0.195312500000 0.039062500000 0.234375000000 +conso_branche.tga 0.039062500000 0.195312500000 0.078125000000 0.234375000000 +conso_branche_mask.tga 0.078125000000 0.195312500000 0.117187500000 0.234375000000 +conso_fleur.tga 0.117187500000 0.195312500000 0.156250000000 0.234375000000 +conso_fleur_mask.tga 0.156250000000 0.195312500000 0.195312500000 0.234375000000 +conso_grappe.tga 0.195312500000 0.195312500000 0.234375000000 0.234375000000 +conso_nectar.tga 0.242187500000 0.000000000000 0.281250000000 0.039062500000 +conso_nectar_mask.tga 0.281250000000 0.000000000000 0.320312500000 0.039062500000 +construction.tga 0.320312500000 0.000000000000 0.359375000000 0.039062500000 +cristal_ammo.tga 0.359375000000 0.000000000000 0.398437500000 0.039062500000 +cristal_spell.tga 0.398437500000 0.000000000000 0.437500000000 0.039062500000 +ico_haircolor.tga 0.437500000000 0.000000000000 0.476562500000 0.039062500000 +ico_haircut.tga 0.238281250000 0.039062500000 0.277343750000 0.078125000000 +bk_karavan.tga 0.277343750000 0.039062500000 0.316406250000 0.078125000000 +conso_grappe_mask.tga 0.316406250000 0.039062500000 0.355468750000 0.078125000000 +ico_foreuse.tga 0.355468750000 0.039062500000 0.394531250000 0.078125000000 +ico_noix.tga 0.394531250000 0.039062500000 0.433593750000 0.078125000000 +ico_spores.tga 0.433593750000 0.039062500000 0.472656250000 0.078125000000 +mektoub_pack.tga 0.234375000000 0.078125000000 0.273437500000 0.117187500000 +mp_beak.tga 0.273437500000 0.078125000000 0.312500000000 0.117187500000 +mp_fresh_loose_soil.tga 0.312500000000 0.078125000000 0.351562500000 0.117187500000 +mp_lichen.tga 0.351562500000 0.078125000000 0.390625000000 0.117187500000 +mp_sawdust.tga 0.390625000000 0.078125000000 0.429687500000 0.117187500000 +MW_2h_axe.tga 0.429687500000 0.078125000000 0.468750000000 0.117187500000 +PA_bracelet.tga 0.234375000000 0.117187500000 0.273437500000 0.156250000000 +pvp_aura_mask.tga 0.273437500000 0.117187500000 0.312500000000 0.156250000000 +quest_queue.tga 0.312500000000 0.117187500000 0.351562500000 0.156250000000 +rpjobitem_201_b.tga 0.351562500000 0.117187500000 0.390625000000 0.156250000000 +rpjobitem_207_a.tga 0.390625000000 0.117187500000 0.429687500000 0.156250000000 +rpjob_task_elementary.tga 0.429687500000 0.117187500000 0.468750000000 0.156250000000 +spe_options.tga 0.234375000000 0.156250000000 0.273437500000 0.195312500000 +to_range.tga 0.273437500000 0.156250000000 0.312500000000 0.195312500000 +w_pa_anklet.tga 0.312500000000 0.156250000000 0.351562500000 0.195312500000 +ico_task_craft.tga 0.351562500000 0.156250000000 0.390625000000 0.195312500000 +ico_task_done.tga 0.390625000000 0.156250000000 0.429687500000 0.195312500000 +ico_task_failed.tga 0.429687500000 0.156250000000 0.468750000000 0.195312500000 +ico_task_fight.tga 0.234375000000 0.195312500000 0.273437500000 0.234375000000 +ico_task_forage.tga 0.273437500000 0.195312500000 0.312500000000 0.234375000000 +ico_task_generic.tga 0.312500000000 0.195312500000 0.351562500000 0.234375000000 +ico_task_generic_quart.tga 0.351562500000 0.195312500000 0.390625000000 0.234375000000 +ico_task_guild.tga 0.390625000000 0.195312500000 0.429687500000 0.234375000000 +ico_task_rite.tga 0.429687500000 0.195312500000 0.468750000000 0.234375000000 +ico_task_travel.tga 0.000000000000 0.234375000000 0.039062500000 0.273437500000 +ico_tatoo.tga 0.039062500000 0.234375000000 0.078125000000 0.273437500000 +mektoub_steed.tga 0.078125000000 0.234375000000 0.117187500000 0.273437500000 +mg_glove.tga 0.117187500000 0.234375000000 0.156250000000 0.273437500000 +mission_icon_0.tga 0.156250000000 0.234375000000 0.195312500000 0.273437500000 +mission_icon_1.tga 0.195312500000 0.234375000000 0.234375000000 0.273437500000 +mission_icon_2.tga 0.234375000000 0.234375000000 0.273437500000 0.273437500000 +mission_icon_3.tga 0.273437500000 0.234375000000 0.312500000000 0.273437500000 +mp_amber.tga 0.312500000000 0.234375000000 0.351562500000 0.273437500000 +mp_bark.tga 0.351562500000 0.234375000000 0.390625000000 0.273437500000 +mp_batiment_brique.tga 0.390625000000 0.234375000000 0.429687500000 0.273437500000 +mp_batiment_colonne.tga 0.429687500000 0.234375000000 0.468750000000 0.273437500000 +mp_batiment_colonne_justice.tga 0.000000000000 0.273437500000 0.039062500000 0.312500000000 +mp_batiment_comble.tga 0.039062500000 0.273437500000 0.078125000000 0.312500000000 +mp_batiment_noyau_maduk.tga 0.078125000000 0.273437500000 0.117187500000 0.312500000000 +mp_batiment_ornement.tga 0.117187500000 0.273437500000 0.156250000000 0.312500000000 +mp_batiment_revetement.tga 0.156250000000 0.273437500000 0.195312500000 0.312500000000 +mp_batiment_socle.tga 0.195312500000 0.273437500000 0.234375000000 0.312500000000 +mp_batiment_statue.tga 0.234375000000 0.273437500000 0.273437500000 0.312500000000 +mp_blood.tga 0.273437500000 0.273437500000 0.312500000000 0.312500000000 +mp_bone.tga 0.312500000000 0.273437500000 0.351562500000 0.312500000000 +mp_bud.tga 0.351562500000 0.273437500000 0.390625000000 0.312500000000 +mp_buterfly_blue.tga 0.390625000000 0.273437500000 0.429687500000 0.312500000000 +mp_buterfly_cocoon.tga 0.429687500000 0.273437500000 0.468750000000 0.312500000000 +mp_cereal.tga 0.000000000000 0.312500000000 0.039062500000 0.351562500000 +mp_claw.tga 0.039062500000 0.312500000000 0.078125000000 0.351562500000 +mp_dandelion.tga 0.078125000000 0.312500000000 0.117187500000 0.351562500000 +mp_dust.tga 0.117187500000 0.312500000000 0.156250000000 0.351562500000 +mp_egg.tga 0.156250000000 0.312500000000 0.195312500000 0.351562500000 +mp_eyes.tga 0.195312500000 0.312500000000 0.234375000000 0.351562500000 +mp_fang.tga 0.234375000000 0.312500000000 0.273437500000 0.351562500000 +mp_fiber.tga 0.273437500000 0.312500000000 0.312500000000 0.351562500000 +mp_filament.tga 0.312500000000 0.312500000000 0.351562500000 0.351562500000 +mp_firefly_abdomen.tga 0.351562500000 0.312500000000 0.390625000000 0.351562500000 +mp_fish_scale.tga 0.390625000000 0.312500000000 0.429687500000 0.351562500000 +mp_flowers.tga 0.429687500000 0.312500000000 0.468750000000 0.351562500000 +mp_fruit.tga 0.000000000000 0.351562500000 0.039062500000 0.390625000000 +mp_generic.tga 0.039062500000 0.351562500000 0.078125000000 0.390625000000 +mp_generic_colorize.tga 0.078125000000 0.351562500000 0.117187500000 0.390625000000 +mp_gomme.tga 0.117187500000 0.351562500000 0.156250000000 0.390625000000 +mp_goo_residue.tga 0.156250000000 0.351562500000 0.195312500000 0.390625000000 +mp_hairs.tga 0.195312500000 0.351562500000 0.234375000000 0.390625000000 +mp_hoof.tga 0.234375000000 0.351562500000 0.273437500000 0.390625000000 +mp_horn.tga 0.273437500000 0.351562500000 0.312500000000 0.390625000000 +mp_horney.tga 0.312500000000 0.351562500000 0.351562500000 0.390625000000 +mp_insect_fossil.tga 0.351562500000 0.351562500000 0.390625000000 0.390625000000 +mp_kitinshell.tga 0.390625000000 0.351562500000 0.429687500000 0.390625000000 +mp_kitin_flesh.tga 0.429687500000 0.351562500000 0.468750000000 0.390625000000 +mp_kitin_secretion.tga 0.000000000000 0.390625000000 0.039062500000 0.429687500000 +mp_larva.tga 0.039062500000 0.390625000000 0.078125000000 0.429687500000 +mp_leaf.tga 0.078125000000 0.390625000000 0.117187500000 0.429687500000 +mp_leather.tga 0.117187500000 0.390625000000 0.156250000000 0.429687500000 +mp_liane.tga 0.156250000000 0.390625000000 0.195312500000 0.429687500000 +mp_ligament.tga 0.195312500000 0.390625000000 0.234375000000 0.429687500000 +mp_mandible.tga 0.234375000000 0.390625000000 0.273437500000 0.429687500000 +mp_meat.tga 0.273437500000 0.390625000000 0.312500000000 0.429687500000 +mp_moss.tga 0.312500000000 0.390625000000 0.351562500000 0.429687500000 +mp_mushroom.tga 0.351562500000 0.390625000000 0.390625000000 0.429687500000 +mp_nail.tga 0.390625000000 0.390625000000 0.429687500000 0.429687500000 +mp_oil.tga 0.429687500000 0.390625000000 0.468750000000 0.429687500000 +mp_parasite.tga 0.000000000000 0.429687500000 0.039062500000 0.468750000000 +mp_pearl.tga 0.039062500000 0.429687500000 0.078125000000 0.468750000000 +mp_pelvis.tga 0.078125000000 0.429687500000 0.117187500000 0.468750000000 +mp_pigment.tga 0.117187500000 0.429687500000 0.156250000000 0.468750000000 +mp_pistil.tga 0.156250000000 0.429687500000 0.195312500000 0.468750000000 +mp_plant_fossil.tga 0.195312500000 0.429687500000 0.234375000000 0.468750000000 +mp_pollen.tga 0.234375000000 0.429687500000 0.273437500000 0.468750000000 +mp_resin.tga 0.273437500000 0.429687500000 0.312500000000 0.468750000000 +mp_ronce.tga 0.312500000000 0.429687500000 0.351562500000 0.468750000000 +mp_rostrum.tga 0.351562500000 0.429687500000 0.390625000000 0.468750000000 +mp_sap.tga 0.390625000000 0.429687500000 0.429687500000 0.468750000000 +mp_seed.tga 0.429687500000 0.429687500000 0.468750000000 0.468750000000 +mp_shell.tga 0.476562500000 0.000000000000 0.515625000000 0.039062500000 +mp_silk_worm.tga 0.515625000000 0.000000000000 0.554687500000 0.039062500000 +mp_skin.tga 0.554687500000 0.000000000000 0.593750000000 0.039062500000 +mp_skull.tga 0.593750000000 0.000000000000 0.632812500000 0.039062500000 +mp_spiders_web.tga 0.632812500000 0.000000000000 0.671875000000 0.039062500000 +mp_spine.tga 0.671875000000 0.000000000000 0.710937500000 0.039062500000 +mp_stem.tga 0.710937500000 0.000000000000 0.750000000000 0.039062500000 +mp_sting.tga 0.750000000000 0.000000000000 0.789062500000 0.039062500000 +mp_straw.tga 0.789062500000 0.000000000000 0.828125000000 0.039062500000 +mp_suc.tga 0.828125000000 0.000000000000 0.867187500000 0.039062500000 +mp_tail.tga 0.867187500000 0.000000000000 0.906250000000 0.039062500000 +mp_tooth.tga 0.906250000000 0.000000000000 0.945312500000 0.039062500000 +mp_trunk.tga 0.945312500000 0.000000000000 0.984375000000 0.039062500000 +mp_whiskers.tga 0.472656250000 0.039062500000 0.511718750000 0.078125000000 +mp_wing.tga 0.511718750000 0.039062500000 0.550781250000 0.078125000000 +mp_wood.tga 0.550781250000 0.039062500000 0.589843750000 0.078125000000 +mp_wood_node.tga 0.589843750000 0.039062500000 0.628906250000 0.078125000000 +MW_2h_lance.tga 0.628906250000 0.039062500000 0.667968750000 0.078125000000 +MW_2h_mace.tga 0.667968750000 0.039062500000 0.707031250000 0.078125000000 +MW_2h_sword.tga 0.707031250000 0.039062500000 0.746093750000 0.078125000000 +MW_axe.tga 0.746093750000 0.039062500000 0.785156250000 0.078125000000 +MW_dagger.tga 0.785156250000 0.039062500000 0.824218750000 0.078125000000 +MW_lance.tga 0.824218750000 0.039062500000 0.863281250000 0.078125000000 +MW_mace.tga 0.863281250000 0.039062500000 0.902343750000 0.078125000000 +MW_staff.tga 0.902343750000 0.039062500000 0.941406250000 0.078125000000 +MW_sword.tga 0.941406250000 0.039062500000 0.980468750000 0.078125000000 +PA_anklet.tga 0.468750000000 0.078125000000 0.507812500000 0.117187500000 +pvp_boost.tga 0.507812500000 0.078125000000 0.546875000000 0.117187500000 +pvp_boost_mask.tga 0.546875000000 0.078125000000 0.585937500000 0.117187500000 +pw_4.tga 0.585937500000 0.078125000000 0.625000000000 0.117187500000 +pw_5.tga 0.625000000000 0.078125000000 0.664062500000 0.117187500000 +pw_6.tga 0.664062500000 0.078125000000 0.703125000000 0.117187500000 +pw_7.tga 0.703125000000 0.078125000000 0.742187500000 0.117187500000 +PW_heavy.tga 0.742187500000 0.078125000000 0.781250000000 0.117187500000 +PW_light.tga 0.781250000000 0.078125000000 0.820312500000 0.117187500000 +PW_medium.tga 0.820312500000 0.078125000000 0.859375000000 0.117187500000 +quest_coeur.tga 0.859375000000 0.078125000000 0.898437500000 0.117187500000 +quest_foie.tga 0.898437500000 0.078125000000 0.937500000000 0.117187500000 +quest_jeton.tga 0.937500000000 0.078125000000 0.976562500000 0.117187500000 +quest_langue.tga 0.468750000000 0.117187500000 0.507812500000 0.156250000000 +quest_louche.tga 0.507812500000 0.117187500000 0.546875000000 0.156250000000 +quest_oreille.tga 0.546875000000 0.117187500000 0.585937500000 0.156250000000 +quest_patte.tga 0.585937500000 0.117187500000 0.625000000000 0.156250000000 +quest_poils.tga 0.625000000000 0.117187500000 0.664062500000 0.156250000000 +quest_ticket.tga 0.664062500000 0.117187500000 0.703125000000 0.156250000000 +AM_logo.tga 0.703125000000 0.117187500000 0.742187500000 0.156250000000 +AR_armpad.tga 0.742187500000 0.117187500000 0.781250000000 0.156250000000 +ar_armpad_mask.tga 0.781250000000 0.117187500000 0.820312500000 0.156250000000 +requirement.tga 0.820312500000 0.117187500000 0.859375000000 0.156250000000 +rm_f.tga 0.859375000000 0.117187500000 0.898437500000 0.156250000000 +rm_f_upgrade.tga 0.898437500000 0.117187500000 0.937500000000 0.156250000000 +rm_h.tga 0.937500000000 0.117187500000 0.976562500000 0.156250000000 +rm_h_upgrade.tga 0.468750000000 0.156250000000 0.507812500000 0.195312500000 +rm_m.tga 0.507812500000 0.156250000000 0.546875000000 0.195312500000 +rm_m_upgrade.tga 0.546875000000 0.156250000000 0.585937500000 0.195312500000 +rm_r.tga 0.585937500000 0.156250000000 0.625000000000 0.195312500000 +rm_r_upgrade.tga 0.625000000000 0.156250000000 0.664062500000 0.195312500000 +rpjobitem_200_a.tga 0.664062500000 0.156250000000 0.703125000000 0.195312500000 +rpjobitem_200_b.tga 0.703125000000 0.156250000000 0.742187500000 0.195312500000 +rpjobitem_200_c.tga 0.742187500000 0.156250000000 0.781250000000 0.195312500000 +rpjobitem_201_a.tga 0.781250000000 0.156250000000 0.820312500000 0.195312500000 +rpjobitem_201_c.tga 0.820312500000 0.156250000000 0.859375000000 0.195312500000 +rpjobitem_202_a.tga 0.859375000000 0.156250000000 0.898437500000 0.195312500000 +rpjobitem_202_b.tga 0.898437500000 0.156250000000 0.937500000000 0.195312500000 +rpjobitem_202_c.tga 0.937500000000 0.156250000000 0.976562500000 0.195312500000 +rpjobitem_203_a.tga 0.468750000000 0.195312500000 0.507812500000 0.234375000000 +rpjobitem_203_b.tga 0.507812500000 0.195312500000 0.546875000000 0.234375000000 +rpjobitem_203_c.tga 0.546875000000 0.195312500000 0.585937500000 0.234375000000 +rpjobitem_204_a.tga 0.585937500000 0.195312500000 0.625000000000 0.234375000000 +rpjobitem_204_b.tga 0.625000000000 0.195312500000 0.664062500000 0.234375000000 +rpjobitem_204_c.tga 0.664062500000 0.195312500000 0.703125000000 0.234375000000 +rpjobitem_205_a.tga 0.703125000000 0.195312500000 0.742187500000 0.234375000000 +rpjobitem_205_b.tga 0.742187500000 0.195312500000 0.781250000000 0.234375000000 +rpjobitem_205_c.tga 0.781250000000 0.195312500000 0.820312500000 0.234375000000 +rpjobitem_206_a.tga 0.820312500000 0.195312500000 0.859375000000 0.234375000000 +rpjobitem_206_b.tga 0.859375000000 0.195312500000 0.898437500000 0.234375000000 +rpjobitem_206_c.tga 0.898437500000 0.195312500000 0.937500000000 0.234375000000 +rpjobitem_207_b.tga 0.937500000000 0.195312500000 0.976562500000 0.234375000000 +rpjobitem_207_c.tga 0.468750000000 0.234375000000 0.507812500000 0.273437500000 +rpjobitem_certifications.tga 0.507812500000 0.234375000000 0.546875000000 0.273437500000 +rpjob_200.tga 0.546875000000 0.234375000000 0.585937500000 0.273437500000 +rpjob_201.tga 0.585937500000 0.234375000000 0.625000000000 0.273437500000 +rpjob_202.tga 0.625000000000 0.234375000000 0.664062500000 0.273437500000 +rpjob_203.tga 0.664062500000 0.234375000000 0.703125000000 0.273437500000 +rpjob_204.tga 0.703125000000 0.234375000000 0.742187500000 0.273437500000 +rpjob_205.tga 0.742187500000 0.234375000000 0.781250000000 0.273437500000 +rpjob_206.tga 0.781250000000 0.234375000000 0.820312500000 0.273437500000 +rpjob_207.tga 0.820312500000 0.234375000000 0.859375000000 0.273437500000 +rpjob_advanced.tga 0.859375000000 0.234375000000 0.898437500000 0.273437500000 +rpjob_elementary.tga 0.898437500000 0.234375000000 0.937500000000 0.273437500000 +rpjob_roleplay.tga 0.937500000000 0.234375000000 0.976562500000 0.273437500000 +rpjob_task.tga 0.468750000000 0.273437500000 0.507812500000 0.312500000000 +rpjob_task_certificats.tga 0.507812500000 0.273437500000 0.546875000000 0.312500000000 +rpjob_task_convert.tga 0.546875000000 0.273437500000 0.585937500000 0.312500000000 +rpjob_task_generic.tga 0.585937500000 0.273437500000 0.625000000000 0.312500000000 +rpjob_task_upgrade.tga 0.625000000000 0.273437500000 0.664062500000 0.312500000000 +RW_autolaunch.tga 0.664062500000 0.273437500000 0.703125000000 0.312500000000 +RW_bowgun.tga 0.703125000000 0.273437500000 0.742187500000 0.312500000000 +RW_grenade.tga 0.742187500000 0.273437500000 0.781250000000 0.312500000000 +RW_harpoongun.tga 0.781250000000 0.273437500000 0.820312500000 0.312500000000 +RW_launcher.tga 0.820312500000 0.273437500000 0.859375000000 0.312500000000 +RW_pistol.tga 0.859375000000 0.273437500000 0.898437500000 0.312500000000 +RW_pistolarc.tga 0.898437500000 0.273437500000 0.937500000000 0.312500000000 +RW_rifle.tga 0.937500000000 0.273437500000 0.976562500000 0.312500000000 +SH_buckler.tga 0.468750000000 0.312500000000 0.507812500000 0.351562500000 +SH_large_shield.tga 0.507812500000 0.312500000000 0.546875000000 0.351562500000 +spe_beast.tga 0.546875000000 0.312500000000 0.585937500000 0.351562500000 +spe_com.tga 0.585937500000 0.312500000000 0.625000000000 0.351562500000 +spe_inventory.tga 0.625000000000 0.312500000000 0.664062500000 0.351562500000 +spe_labs.tga 0.664062500000 0.312500000000 0.703125000000 0.351562500000 +spe_memory.tga 0.703125000000 0.312500000000 0.742187500000 0.351562500000 +spe_status.tga 0.742187500000 0.312500000000 0.781250000000 0.351562500000 +stimulating_water.tga 0.781250000000 0.312500000000 0.820312500000 0.351562500000 +ico_cataliseur_xp.tga 0.820312500000 0.312500000000 0.859375000000 0.351562500000 +ico_consommable_over.tga 0.859375000000 0.312500000000 0.898437500000 0.351562500000 +ico_fleur_carac_1.tga 0.898437500000 0.312500000000 0.937500000000 0.351562500000 +ico_fleur_carac_1_mask.tga 0.937500000000 0.312500000000 0.976562500000 0.351562500000 +ico_fleur_carac_2.tga 0.468750000000 0.351562500000 0.507812500000 0.390625000000 +ico_fleur_carac_2_mask.tga 0.507812500000 0.351562500000 0.546875000000 0.390625000000 +ico_fleur_carac_3.tga 0.546875000000 0.351562500000 0.585937500000 0.390625000000 +ico_fleur_carac_3_mask.tga 0.585937500000 0.351562500000 0.625000000000 0.390625000000 +ico_mission_art_fyros.tga 0.625000000000 0.351562500000 0.664062500000 0.390625000000 +ico_mission_art_matis.tga 0.664062500000 0.351562500000 0.703125000000 0.390625000000 +ico_mission_art_tryker.tga 0.703125000000 0.351562500000 0.742187500000 0.390625000000 +ico_mission_art_zorai.tga 0.742187500000 0.351562500000 0.781250000000 0.390625000000 +ico_mission_barrel.tga 0.781250000000 0.351562500000 0.820312500000 0.390625000000 +ico_mission_bottle.tga 0.820312500000 0.351562500000 0.859375000000 0.390625000000 +ico_mission_casket.tga 0.859375000000 0.351562500000 0.898437500000 0.390625000000 +ico_mission_medicine.tga 0.898437500000 0.351562500000 0.937500000000 0.390625000000 +ico_mission_message.tga 0.937500000000 0.351562500000 0.976562500000 0.390625000000 +ico_mission_package.tga 0.468750000000 0.390625000000 0.507812500000 0.429687500000 +ico_mission_pot.tga 0.507812500000 0.390625000000 0.546875000000 0.429687500000 +ico_mission_purse.tga 0.546875000000 0.390625000000 0.585937500000 0.429687500000 +ico_racine.tga 0.585937500000 0.390625000000 0.625000000000 0.429687500000 +ge_mission_outpost_townhall.tga 0.625000000000 0.390625000000 0.664062500000 0.429687500000 +ico_amande.tga 0.664062500000 0.390625000000 0.703125000000 0.429687500000 +to_searake.tga 0.703125000000 0.390625000000 0.742187500000 0.429687500000 +to_spade.tga 0.742187500000 0.390625000000 0.781250000000 0.429687500000 +to_stick.tga 0.781250000000 0.390625000000 0.820312500000 0.429687500000 +to_tunneling_knife.tga 0.820312500000 0.390625000000 0.859375000000 0.429687500000 +to_whip.tga 0.859375000000 0.390625000000 0.898437500000 0.429687500000 +to_wrench.tga 0.898437500000 0.390625000000 0.937500000000 0.429687500000 +TP_caravane.tga 0.937500000000 0.390625000000 0.976562500000 0.429687500000 +TP_kami.tga 0.468750000000 0.429687500000 0.507812500000 0.468750000000 +tetekitin.tga 0.507812500000 0.429687500000 0.546875000000 0.468750000000 +to_ammo.tga 0.546875000000 0.429687500000 0.585937500000 0.468750000000 +to_armor.tga 0.585937500000 0.429687500000 0.625000000000 0.468750000000 +to_cooking_pot.tga 0.625000000000 0.429687500000 0.664062500000 0.468750000000 +to_fishing_rod.tga 0.664062500000 0.429687500000 0.703125000000 0.468750000000 +to_forage.tga 0.703125000000 0.429687500000 0.742187500000 0.468750000000 +to_hammer.tga 0.742187500000 0.429687500000 0.781250000000 0.468750000000 +to_jewelry_hammer.tga 0.781250000000 0.429687500000 0.820312500000 0.468750000000 +to_jewels.tga 0.820312500000 0.429687500000 0.859375000000 0.468750000000 +to_leathercutter.tga 0.859375000000 0.429687500000 0.898437500000 0.468750000000 +to_melee.tga 0.898437500000 0.429687500000 0.937500000000 0.468750000000 +to_needle.tga 0.937500000000 0.429687500000 0.976562500000 0.468750000000 +to_pestle.tga 0.000000000000 0.468750000000 0.039062500000 0.507812500000 +ico_tourbe.tga 0.039062500000 0.468750000000 0.078125000000 0.507812500000 +improved_tool.tga 0.078125000000 0.468750000000 0.117187500000 0.507812500000 +item_default.tga 0.117187500000 0.468750000000 0.156250000000 0.507812500000 +item_plan_over.tga 0.156250000000 0.468750000000 0.195312500000 0.507812500000 +lucky_flower.tga 0.195312500000 0.468750000000 0.234375000000 0.507812500000 +W_AM_logo.tga 0.234375000000 0.468750000000 0.273437500000 0.507812500000 +w_pa_bracelet.tga 0.273437500000 0.468750000000 0.312500000000 0.507812500000 +w_pa_diadem.tga 0.312500000000 0.468750000000 0.351562500000 0.507812500000 +w_pa_earring.tga 0.351562500000 0.468750000000 0.390625000000 0.507812500000 +w_pa_pendant.tga 0.390625000000 0.468750000000 0.429687500000 0.507812500000 +w_pa_ring.tga 0.429687500000 0.468750000000 0.468750000000 0.507812500000 +xp_cat_green.tga 0.468750000000 0.468750000000 0.507812500000 0.507812500000 +PA_diadem.tga 0.507812500000 0.468750000000 0.546875000000 0.507812500000 +PA_earring.tga 0.546875000000 0.468750000000 0.585937500000 0.507812500000 +PA_pendant.tga 0.585937500000 0.468750000000 0.625000000000 0.507812500000 +PA_ring.tga 0.625000000000 0.468750000000 0.664062500000 0.507812500000 +protect_amber.tga 0.664062500000 0.468750000000 0.703125000000 0.507812500000 +pvp_aura.tga 0.703125000000 0.468750000000 0.742187500000 0.507812500000 asc_exit.tga 0.742187500000 0.468750000000 0.773437500000 0.500000000000 asc_rolemastercraft.tga 0.773437500000 0.468750000000 0.804687500000 0.500000000000 asc_rolemasterfight.tga 0.804687500000 0.468750000000 0.835937500000 0.500000000000 @@ -324,340 +324,350 @@ asc_rolemasterharvest.tga 0.835937500000 0.468750000000 0.867187500000 0.5000000 asc_rolemastermagic.tga 0.867187500000 0.468750000000 0.898437500000 0.500000000000 asc_unknown.tga 0.898437500000 0.468750000000 0.929687500000 0.500000000000 mail.tga 0.929687500000 0.468750000000 0.960937500000 0.492187500000 -mp_back_curative.tga 0.976562500000 0.078125000000 1.000000000000 0.101562500000 -mp_back_offensive.tga 0.976562500000 0.101562500000 1.000000000000 0.125000000000 -mp_back_selfonly.tga 0.976562500000 0.125000000000 1.000000000000 0.148437500000 -building_state_24x24.tga 0.976562500000 0.148437500000 1.000000000000 0.171875000000 -ico_ammo_bullet.tga 0.976562500000 0.171875000000 1.000000000000 0.195312500000 -ico_ammo_jacket.tga 0.976562500000 0.195312500000 1.000000000000 0.218750000000 -ico_angle.tga 0.976562500000 0.218750000000 1.000000000000 0.242187500000 -ico_anti_magic_shield.tga 0.976562500000 0.242187500000 1.000000000000 0.265625000000 -ico_armor.tga 0.976562500000 0.265625000000 1.000000000000 0.289062500000 -ico_armor_clip.tga 0.976562500000 0.289062500000 1.000000000000 0.312500000000 -ico_armor_heavy.tga 0.976562500000 0.312500000000 1.000000000000 0.335937500000 -ico_armor_kitin.tga 0.976562500000 0.335937500000 1.000000000000 0.359375000000 -ico_armor_light.tga 0.976562500000 0.359375000000 1.000000000000 0.382812500000 -ico_armor_medium.tga 0.976562500000 0.382812500000 1.000000000000 0.406250000000 -ico_armor_penalty.tga 0.976562500000 0.406250000000 1.000000000000 0.429687500000 -ico_armor_shell.tga 0.976562500000 0.429687500000 1.000000000000 0.453125000000 -ico_atys.tga 0.976562500000 0.453125000000 1.000000000000 0.476562500000 -ico_atysian.tga 0.960937500000 0.476562500000 0.984375000000 0.500000000000 -ico_balance_hp.tga 0.929687500000 0.492187500000 0.953125000000 0.515625000000 -ico_barrel.tga 0.742187500000 0.500000000000 0.765625000000 0.523437500000 -ico_bash.tga 0.765625000000 0.500000000000 0.789062500000 0.523437500000 -ico_berserk.tga 0.789062500000 0.500000000000 0.812500000000 0.523437500000 -ico_blade.tga 0.812500000000 0.500000000000 0.835937500000 0.523437500000 -ico_bleeding.tga 0.835937500000 0.500000000000 0.859375000000 0.523437500000 -ico_blind.tga 0.859375000000 0.500000000000 0.882812500000 0.523437500000 -ico_blunt.tga 0.882812500000 0.500000000000 0.906250000000 0.523437500000 -ico_bomb.tga 0.906250000000 0.500000000000 0.929687500000 0.523437500000 -cb_main_nue.tga 0.953125000000 0.500000000000 0.976562500000 0.523437500000 -ico_celestial.tga 0.976562500000 0.500000000000 1.000000000000 0.523437500000 -ico_circular_attack.tga 0.000000000000 0.507812500000 0.023437500000 0.531250000000 -ico_clothes.tga 0.023437500000 0.507812500000 0.046875000000 0.531250000000 -ico_cold.tga 0.046875000000 0.507812500000 0.070312500000 0.531250000000 -ico_concentration.tga 0.070312500000 0.507812500000 0.093750000000 0.531250000000 -BK_matis_brick.tga 0.093750000000 0.507812500000 0.117187500000 0.531250000000 -ico_constitution.tga 0.117187500000 0.507812500000 0.140625000000 0.531250000000 -ico_counterweight.tga 0.140625000000 0.507812500000 0.164062500000 0.531250000000 -ico_craft_buff.tga 0.164062500000 0.507812500000 0.187500000000 0.531250000000 -ico_create_sapload.tga 0.187500000000 0.507812500000 0.210937500000 0.531250000000 -ico_curse.tga 0.210937500000 0.507812500000 0.234375000000 0.531250000000 -ico_debuff.tga 0.234375000000 0.507812500000 0.257812500000 0.531250000000 -ico_debuff_resist.tga 0.257812500000 0.507812500000 0.281250000000 0.531250000000 -ico_debuff_skill.tga 0.281250000000 0.507812500000 0.304687500000 0.531250000000 -ico_desert.tga 0.304687500000 0.507812500000 0.328125000000 0.531250000000 -ico_dexterity.tga 0.328125000000 0.507812500000 0.351562500000 0.531250000000 -ico_disarm.tga 0.351562500000 0.507812500000 0.375000000000 0.531250000000 -ico_dodge.tga 0.375000000000 0.507812500000 0.398437500000 0.531250000000 -ico_dot.tga 0.398437500000 0.507812500000 0.421875000000 0.531250000000 -ico_durability.tga 0.421875000000 0.507812500000 0.445312500000 0.531250000000 -ico_electric.tga 0.445312500000 0.507812500000 0.468750000000 0.531250000000 -ico_explosif.tga 0.468750000000 0.507812500000 0.492187500000 0.531250000000 -ico_extracting.tga 0.492187500000 0.507812500000 0.515625000000 0.531250000000 -ico_fear.tga 0.515625000000 0.507812500000 0.539062500000 0.531250000000 -ico_feint.tga 0.539062500000 0.507812500000 0.562500000000 0.531250000000 -ico_fire.tga 0.562500000000 0.507812500000 0.585937500000 0.531250000000 -ico_firing_pin.tga 0.585937500000 0.507812500000 0.609375000000 0.531250000000 -ch_back.tga 0.609375000000 0.507812500000 0.632812500000 0.531250000000 -BK_generic_brick.tga 0.632812500000 0.507812500000 0.656250000000 0.531250000000 -mp_over_link.tga 0.656250000000 0.507812500000 0.679687500000 0.531250000000 -bk_aura.tga 0.679687500000 0.507812500000 0.703125000000 0.531250000000 -bk_conso.tga 0.703125000000 0.507812500000 0.726562500000 0.531250000000 -bk_outpost_brick.tga 0.929687500000 0.515625000000 0.953125000000 0.539062500000 -bk_power.tga 0.726562500000 0.523437500000 0.750000000000 0.546875000000 -ico_focus.tga 0.750000000000 0.523437500000 0.773437500000 0.546875000000 -ico_forage_buff.tga 0.773437500000 0.523437500000 0.796875000000 0.546875000000 -ico_forbid_item.tga 0.796875000000 0.523437500000 0.820312500000 0.546875000000 -ico_forest.tga 0.820312500000 0.523437500000 0.843750000000 0.546875000000 -2h_over.tga 0.843750000000 0.523437500000 0.867187500000 0.546875000000 -ico_gardening.tga 0.867187500000 0.523437500000 0.890625000000 0.546875000000 -ico_gentle.tga 0.890625000000 0.523437500000 0.914062500000 0.546875000000 -ico_goo.tga 0.953125000000 0.523437500000 0.976562500000 0.546875000000 -ico_gripp.tga 0.976562500000 0.523437500000 1.000000000000 0.546875000000 -1h_over.tga 0.000000000000 0.531250000000 0.023437500000 0.554687500000 -BK_fyros_brick.tga 0.023437500000 0.531250000000 0.046875000000 0.554687500000 -ico_hammer.tga 0.046875000000 0.531250000000 0.070312500000 0.554687500000 -ico_harmful.tga 0.070312500000 0.531250000000 0.093750000000 0.554687500000 -ico_hatred.tga 0.093750000000 0.531250000000 0.117187500000 0.554687500000 -ico_heal.tga 0.117187500000 0.531250000000 0.140625000000 0.554687500000 -ico_hit_rate.tga 0.140625000000 0.531250000000 0.164062500000 0.554687500000 -ico_incapacity.tga 0.164062500000 0.531250000000 0.187500000000 0.554687500000 -ico_intelligence.tga 0.187500000000 0.531250000000 0.210937500000 0.554687500000 -ico_interrupt.tga 0.210937500000 0.531250000000 0.234375000000 0.554687500000 -ico_invulnerability.tga 0.234375000000 0.531250000000 0.257812500000 0.554687500000 -ico_jewel_stone.tga 0.257812500000 0.531250000000 0.281250000000 0.554687500000 -ico_jewel_stone_support.tga 0.281250000000 0.531250000000 0.304687500000 0.554687500000 -ico_jungle.tga 0.304687500000 0.531250000000 0.328125000000 0.554687500000 -ico_lacustre.tga 0.328125000000 0.531250000000 0.351562500000 0.554687500000 -ico_landmark_bonus.tga 0.351562500000 0.531250000000 0.375000000000 0.554687500000 -ico_level.tga 0.375000000000 0.531250000000 0.398437500000 0.554687500000 -ico_lining.tga 0.398437500000 0.531250000000 0.421875000000 0.554687500000 -ico_location.tga 0.421875000000 0.531250000000 0.445312500000 0.554687500000 -ico_madness.tga 0.445312500000 0.531250000000 0.468750000000 0.554687500000 -ico_magic.tga 0.468750000000 0.531250000000 0.492187500000 0.554687500000 -ico_magic_action_buff.tga 0.492187500000 0.531250000000 0.515625000000 0.554687500000 -ico_magic_focus.tga 0.515625000000 0.531250000000 0.539062500000 0.554687500000 -ico_magic_target_buff.tga 0.539062500000 0.531250000000 0.562500000000 0.554687500000 -ico_melee_action_buff.tga 0.562500000000 0.531250000000 0.585937500000 0.554687500000 -ico_melee_target_buff.tga 0.585937500000 0.531250000000 0.609375000000 0.554687500000 -ico_mental.tga 0.609375000000 0.531250000000 0.632812500000 0.554687500000 -no_action.tga 0.632812500000 0.531250000000 0.656250000000 0.554687500000 -op_back.tga 0.656250000000 0.531250000000 0.679687500000 0.554687500000 -op_over_break.tga 0.679687500000 0.531250000000 0.703125000000 0.554687500000 -op_over_less.tga 0.703125000000 0.531250000000 0.726562500000 0.554687500000 -op_over_more.tga 0.914062500000 0.539062500000 0.937500000000 0.562500000000 -ico_metabolism.tga 0.726562500000 0.546875000000 0.750000000000 0.570312500000 -pa_back.tga 0.750000000000 0.546875000000 0.773437500000 0.570312500000 -ico_mezz.tga 0.773437500000 0.546875000000 0.796875000000 0.570312500000 -ico_misfortune.tga 0.796875000000 0.546875000000 0.820312500000 0.570312500000 -BK_magie_noire_brick.tga 0.820312500000 0.546875000000 0.843750000000 0.570312500000 -pa_over_break.tga 0.843750000000 0.546875000000 0.867187500000 0.570312500000 -pa_over_less.tga 0.867187500000 0.546875000000 0.890625000000 0.570312500000 -pa_over_more.tga 0.890625000000 0.546875000000 0.914062500000 0.570312500000 -BK_tryker_brick.tga 0.937500000000 0.546875000000 0.960937500000 0.570312500000 -cp_back.tga 0.960937500000 0.546875000000 0.984375000000 0.570312500000 -cp_over_break.tga 0.000000000000 0.554687500000 0.023437500000 0.578125000000 -pvp_ally_0.tga 0.023437500000 0.554687500000 0.046875000000 0.578125000000 -pvp_ally_1.tga 0.046875000000 0.554687500000 0.070312500000 0.578125000000 -pvp_ally_2.tga 0.070312500000 0.554687500000 0.093750000000 0.578125000000 -pvp_ally_3.tga 0.093750000000 0.554687500000 0.117187500000 0.578125000000 -pvp_ally_4.tga 0.117187500000 0.554687500000 0.140625000000 0.578125000000 -pvp_ally_6.tga 0.140625000000 0.554687500000 0.164062500000 0.578125000000 -pvp_ally_primas.tga 0.164062500000 0.554687500000 0.187500000000 0.578125000000 -pvp_ally_ranger.tga 0.187500000000 0.554687500000 0.210937500000 0.578125000000 -cp_over_less.tga 0.210937500000 0.554687500000 0.234375000000 0.578125000000 -cp_over_more.tga 0.234375000000 0.554687500000 0.257812500000 0.578125000000 -cp_over_opening.tga 0.257812500000 0.554687500000 0.281250000000 0.578125000000 -cp_over_opening_2.tga 0.281250000000 0.554687500000 0.304687500000 0.578125000000 -pvp_enemy_0.tga 0.304687500000 0.554687500000 0.328125000000 0.578125000000 -pvp_enemy_1.tga 0.328125000000 0.554687500000 0.351562500000 0.578125000000 -pvp_enemy_2.tga 0.351562500000 0.554687500000 0.375000000000 0.578125000000 -pvp_enemy_3.tga 0.375000000000 0.554687500000 0.398437500000 0.578125000000 -pvp_enemy_4.tga 0.398437500000 0.554687500000 0.421875000000 0.578125000000 -pvp_enemy_6.tga 0.421875000000 0.554687500000 0.445312500000 0.578125000000 -pvp_enemy_marauder.tga 0.445312500000 0.554687500000 0.468750000000 0.578125000000 -pvp_enemy_trytonist.tga 0.468750000000 0.554687500000 0.492187500000 0.578125000000 -bg_downloader.tga 0.492187500000 0.554687500000 0.515625000000 0.578125000000 -BK_zorai_brick.tga 0.515625000000 0.554687500000 0.539062500000 0.578125000000 -ef_back.tga 0.539062500000 0.554687500000 0.562500000000 0.578125000000 -ef_over_break.tga 0.562500000000 0.554687500000 0.585937500000 0.578125000000 -ico_move.tga 0.585937500000 0.554687500000 0.609375000000 0.578125000000 -ico_multiple_spots.tga 0.609375000000 0.554687500000 0.632812500000 0.578125000000 -ico_multi_fight.tga 0.632812500000 0.554687500000 0.656250000000 0.578125000000 -ef_over_less.tga 0.656250000000 0.554687500000 0.679687500000 0.578125000000 -ico_opening_hit.tga 0.679687500000 0.554687500000 0.703125000000 0.578125000000 -ico_over_autumn.tga 0.703125000000 0.554687500000 0.726562500000 0.578125000000 -ico_over_degenerated.tga 0.914062500000 0.562500000000 0.937500000000 0.585937500000 -ico_over_fauna.tga 0.726562500000 0.570312500000 0.750000000000 0.593750000000 -ico_over_flora.tga 0.750000000000 0.570312500000 0.773437500000 0.593750000000 -ico_over_hit_arms.tga 0.773437500000 0.570312500000 0.796875000000 0.593750000000 -ico_over_hit_chest.tga 0.796875000000 0.570312500000 0.820312500000 0.593750000000 -ico_over_hit_feet.tga 0.820312500000 0.570312500000 0.843750000000 0.593750000000 -ico_over_hit_feet_hands.tga 0.843750000000 0.570312500000 0.867187500000 0.593750000000 -ico_over_hit_feet_head.tga 0.867187500000 0.570312500000 0.890625000000 0.593750000000 -ico_over_hit_feet_x2.tga 0.890625000000 0.570312500000 0.914062500000 0.593750000000 -ico_over_hit_feint_x3.tga 0.937500000000 0.570312500000 0.960937500000 0.593750000000 -ico_over_hit_hands.tga 0.960937500000 0.570312500000 0.984375000000 0.593750000000 -ico_over_hit_hands_chest.tga 0.000000000000 0.578125000000 0.023437500000 0.601562500000 -ico_over_hit_hands_head.tga 0.023437500000 0.578125000000 0.046875000000 0.601562500000 -ico_over_hit_head.tga 0.046875000000 0.578125000000 0.070312500000 0.601562500000 -ico_over_hit_head_x3.tga 0.070312500000 0.578125000000 0.093750000000 0.601562500000 -ico_over_hit_legs.tga 0.093750000000 0.578125000000 0.117187500000 0.601562500000 -ico_over_homin.tga 0.117187500000 0.578125000000 0.140625000000 0.601562500000 -ico_over_kitin.tga 0.140625000000 0.578125000000 0.164062500000 0.601562500000 -ico_over_magic.tga 0.164062500000 0.578125000000 0.187500000000 0.601562500000 -ico_over_melee.tga 0.187500000000 0.578125000000 0.210937500000 0.601562500000 -ico_over_racial.tga 0.210937500000 0.578125000000 0.234375000000 0.601562500000 -ico_over_range.tga 0.234375000000 0.578125000000 0.257812500000 0.601562500000 -ico_over_special.tga 0.257812500000 0.578125000000 0.281250000000 0.601562500000 -ico_over_spring.tga 0.281250000000 0.578125000000 0.304687500000 0.601562500000 -ico_over_summer.tga 0.304687500000 0.578125000000 0.328125000000 0.601562500000 -ico_over_winter.tga 0.328125000000 0.578125000000 0.351562500000 0.601562500000 -ico_parry.tga 0.351562500000 0.578125000000 0.375000000000 0.601562500000 -ico_piercing.tga 0.375000000000 0.578125000000 0.398437500000 0.601562500000 -ico_pointe.tga 0.398437500000 0.578125000000 0.421875000000 0.601562500000 -ico_poison.tga 0.421875000000 0.578125000000 0.445312500000 0.601562500000 -ico_power.tga 0.445312500000 0.578125000000 0.468750000000 0.601562500000 -ico_preservation.tga 0.468750000000 0.578125000000 0.492187500000 0.601562500000 -ico_primal.tga 0.492187500000 0.578125000000 0.515625000000 0.601562500000 -ico_prime_roots.tga 0.515625000000 0.578125000000 0.539062500000 0.601562500000 -ico_private.tga 0.539062500000 0.578125000000 0.562500000000 0.601562500000 -ico_prospecting.tga 0.562500000000 0.578125000000 0.585937500000 0.601562500000 -ico_quality.tga 0.585937500000 0.578125000000 0.609375000000 0.601562500000 -ef_over_more.tga 0.609375000000 0.578125000000 0.632812500000 0.601562500000 -ico_range.tga 0.632812500000 0.578125000000 0.656250000000 0.601562500000 -ico_range_action_buff.tga 0.656250000000 0.578125000000 0.679687500000 0.601562500000 -ico_range_target_buff.tga 0.679687500000 0.578125000000 0.703125000000 0.601562500000 -ico_ricochet.tga 0.703125000000 0.578125000000 0.726562500000 0.601562500000 -ico_root.tga 0.914062500000 0.585937500000 0.937500000000 0.609375000000 -ico_rot.tga 0.726562500000 0.593750000000 0.750000000000 0.617187500000 -ico_safe.tga 0.750000000000 0.593750000000 0.773437500000 0.617187500000 -ico_sap.tga 0.773437500000 0.593750000000 0.796875000000 0.617187500000 -ico_self_damage.tga 0.796875000000 0.593750000000 0.820312500000 0.617187500000 -ico_shaft.tga 0.820312500000 0.593750000000 0.843750000000 0.617187500000 -ico_shielding.tga 0.843750000000 0.593750000000 0.867187500000 0.617187500000 -ico_shield_buff.tga 0.867187500000 0.593750000000 0.890625000000 0.617187500000 -ico_shield_up.tga 0.890625000000 0.593750000000 0.914062500000 0.617187500000 -ico_shockwave.tga 0.937500000000 0.593750000000 0.960937500000 0.617187500000 -ico_sickness.tga 0.960937500000 0.593750000000 0.984375000000 0.617187500000 -ico_slashing.tga 0.000000000000 0.601562500000 0.023437500000 0.625000000000 -ico_slow.tga 0.023437500000 0.601562500000 0.046875000000 0.625000000000 -ico_soft_spot.tga 0.046875000000 0.601562500000 0.070312500000 0.625000000000 -ico_source_time.tga 0.070312500000 0.601562500000 0.093750000000 0.625000000000 -ico_speed.tga 0.093750000000 0.601562500000 0.117187500000 0.625000000000 -ico_speeding_up.tga 0.117187500000 0.601562500000 0.140625000000 0.625000000000 -ico_spell_break.tga 0.140625000000 0.601562500000 0.164062500000 0.625000000000 -fo_back.tga 0.164062500000 0.601562500000 0.187500000000 0.625000000000 -ico_spray.tga 0.187500000000 0.601562500000 0.210937500000 0.625000000000 -ico_spying.tga 0.210937500000 0.601562500000 0.234375000000 0.625000000000 -ico_stamina.tga 0.234375000000 0.601562500000 0.257812500000 0.625000000000 -ico_strength.tga 0.257812500000 0.601562500000 0.281250000000 0.625000000000 -ico_stuffing.tga 0.281250000000 0.601562500000 0.304687500000 0.625000000000 -ico_stunn.tga 0.304687500000 0.601562500000 0.328125000000 0.625000000000 -fo_over.tga 0.328125000000 0.601562500000 0.351562500000 0.625000000000 -fp_ammo.tga 0.351562500000 0.601562500000 0.375000000000 0.625000000000 -fp_armor.tga 0.375000000000 0.601562500000 0.398437500000 0.625000000000 -fp_building.tga 0.398437500000 0.601562500000 0.421875000000 0.625000000000 -fp_jewel.tga 0.421875000000 0.601562500000 0.445312500000 0.625000000000 -fp_melee.tga 0.445312500000 0.601562500000 0.468750000000 0.625000000000 -fp_over.tga 0.468750000000 0.601562500000 0.492187500000 0.625000000000 -fp_range.tga 0.492187500000 0.601562500000 0.515625000000 0.625000000000 -fp_shield.tga 0.515625000000 0.601562500000 0.539062500000 0.625000000000 -fp_tools.tga 0.539062500000 0.601562500000 0.562500000000 0.625000000000 -brick_default.tga 0.562500000000 0.601562500000 0.585937500000 0.625000000000 -ico_taunt.tga 0.585937500000 0.601562500000 0.609375000000 0.625000000000 -tb_action_attack.tga 0.609375000000 0.601562500000 0.632812500000 0.625000000000 -tb_action_config.tga 0.632812500000 0.601562500000 0.656250000000 0.625000000000 -tb_action_disband.tga 0.656250000000 0.601562500000 0.679687500000 0.625000000000 -tb_action_disengage.tga 0.679687500000 0.601562500000 0.703125000000 0.625000000000 -tb_action_extract.tga 0.703125000000 0.601562500000 0.726562500000 0.625000000000 -tb_action_invite.tga 0.914062500000 0.609375000000 0.937500000000 0.632812500000 -tb_action_kick.tga 0.726562500000 0.617187500000 0.750000000000 0.640625000000 -tb_action_move.tga 0.750000000000 0.617187500000 0.773437500000 0.640625000000 -tb_action_run.tga 0.773437500000 0.617187500000 0.796875000000 0.640625000000 -tb_action_sit.tga 0.796875000000 0.617187500000 0.820312500000 0.640625000000 -tb_action_stand.tga 0.820312500000 0.617187500000 0.843750000000 0.640625000000 -tb_action_stop.tga 0.843750000000 0.617187500000 0.867187500000 0.640625000000 -tb_action_talk.tga 0.867187500000 0.617187500000 0.890625000000 0.640625000000 -tb_action_walk.tga 0.890625000000 0.617187500000 0.914062500000 0.640625000000 -tb_animals.tga 0.937500000000 0.617187500000 0.960937500000 0.640625000000 -tb_config.tga 0.960937500000 0.617187500000 0.984375000000 0.640625000000 -tb_connection.tga 0.000000000000 0.625000000000 0.023437500000 0.648437500000 -tb_contacts.tga 0.023437500000 0.625000000000 0.046875000000 0.648437500000 -tb_desk_1.tga 0.046875000000 0.625000000000 0.070312500000 0.648437500000 -tb_desk_2.tga 0.070312500000 0.625000000000 0.093750000000 0.648437500000 -tb_desk_3.tga 0.093750000000 0.625000000000 0.117187500000 0.648437500000 -tb_desk_4.tga 0.117187500000 0.625000000000 0.140625000000 0.648437500000 -tb_faction.tga 0.140625000000 0.625000000000 0.164062500000 0.648437500000 -tb_forum.tga 0.164062500000 0.625000000000 0.187500000000 0.648437500000 -tb_guild.tga 0.187500000000 0.625000000000 0.210937500000 0.648437500000 -TB_help2.tga 0.210937500000 0.625000000000 0.234375000000 0.648437500000 -tb_keys.tga 0.234375000000 0.625000000000 0.257812500000 0.648437500000 -tb_macros.tga 0.257812500000 0.625000000000 0.281250000000 0.648437500000 -tb_mail.tga 0.281250000000 0.625000000000 0.304687500000 0.648437500000 -tb_mode_dodge.tga 0.304687500000 0.625000000000 0.328125000000 0.648437500000 -tb_mode_parry.tga 0.328125000000 0.625000000000 0.351562500000 0.648437500000 -tb_over.tga 0.351562500000 0.625000000000 0.375000000000 0.648437500000 -tb_support.tga 0.375000000000 0.625000000000 0.398437500000 0.648437500000 -tb_team.tga 0.398437500000 0.625000000000 0.421875000000 0.648437500000 -tb_windows.tga 0.421875000000 0.625000000000 0.445312500000 0.648437500000 -ico_time.tga 0.445312500000 0.625000000000 0.468750000000 0.648437500000 -ico_time_bonus.tga 0.468750000000 0.625000000000 0.492187500000 0.648437500000 -ico_absorb_damage.tga 0.492187500000 0.625000000000 0.515625000000 0.648437500000 -ico_trigger.tga 0.515625000000 0.625000000000 0.539062500000 0.648437500000 -ico_umbrella.tga 0.539062500000 0.625000000000 0.562500000000 0.648437500000 -ico_use_enchantement.tga 0.562500000000 0.625000000000 0.585937500000 0.648437500000 -ico_vampire.tga 0.585937500000 0.625000000000 0.609375000000 0.648437500000 -ico_visibility.tga 0.609375000000 0.625000000000 0.632812500000 0.648437500000 -ico_war_cry.tga 0.632812500000 0.625000000000 0.656250000000 0.648437500000 -ico_weight.tga 0.656250000000 0.625000000000 0.679687500000 0.648437500000 -ico_wellbalanced.tga 0.679687500000 0.625000000000 0.703125000000 0.648437500000 -ico_will.tga 0.703125000000 0.625000000000 0.726562500000 0.648437500000 -ico_windding.tga 0.914062500000 0.632812500000 0.937500000000 0.656250000000 -ico_wisdom.tga 0.726562500000 0.640625000000 0.750000000000 0.664062500000 -ico_accurate.tga 0.750000000000 0.640625000000 0.773437500000 0.664062500000 -ico_acid.tga 0.773437500000 0.640625000000 0.796875000000 0.664062500000 -ico_aim.tga 0.796875000000 0.640625000000 0.820312500000 0.664062500000 -ico_aim_bird_wings.tga 0.820312500000 0.640625000000 0.843750000000 0.664062500000 -ico_aim_flying_kitin_abdomen.tga 0.843750000000 0.640625000000 0.867187500000 0.664062500000 -ico_aim_homin_arms.tga 0.867187500000 0.640625000000 0.890625000000 0.664062500000 -ico_aim_homin_chest.tga 0.890625000000 0.640625000000 0.914062500000 0.664062500000 -mf_back.tga 0.937500000000 0.640625000000 0.960937500000 0.664062500000 -us_back_0.tga 0.960937500000 0.640625000000 0.984375000000 0.664062500000 -us_back_1.tga 0.000000000000 0.648437500000 0.023437500000 0.671875000000 -us_back_2.tga 0.023437500000 0.648437500000 0.046875000000 0.671875000000 -us_back_3.tga 0.046875000000 0.648437500000 0.070312500000 0.671875000000 -us_back_4.tga 0.070312500000 0.648437500000 0.093750000000 0.671875000000 -us_back_5.tga 0.093750000000 0.648437500000 0.117187500000 0.671875000000 -us_back_6.tga 0.117187500000 0.648437500000 0.140625000000 0.671875000000 -us_back_7.tga 0.140625000000 0.648437500000 0.164062500000 0.671875000000 -us_back_8.tga 0.164062500000 0.648437500000 0.187500000000 0.671875000000 -us_back_9.tga 0.187500000000 0.648437500000 0.210937500000 0.671875000000 -us_ico_0.tga 0.210937500000 0.648437500000 0.234375000000 0.671875000000 -us_ico_1.tga 0.234375000000 0.648437500000 0.257812500000 0.671875000000 -us_ico_2.tga 0.257812500000 0.648437500000 0.281250000000 0.671875000000 -us_ico_3.tga 0.281250000000 0.648437500000 0.304687500000 0.671875000000 -us_ico_4.tga 0.304687500000 0.648437500000 0.328125000000 0.671875000000 -us_ico_5.tga 0.328125000000 0.648437500000 0.351562500000 0.671875000000 -us_ico_6.tga 0.351562500000 0.648437500000 0.375000000000 0.671875000000 -us_ico_7.tga 0.375000000000 0.648437500000 0.398437500000 0.671875000000 -us_ico_8.tga 0.398437500000 0.648437500000 0.421875000000 0.671875000000 -us_ico_9.tga 0.421875000000 0.648437500000 0.445312500000 0.671875000000 -us_over_0.tga 0.445312500000 0.648437500000 0.468750000000 0.671875000000 -us_over_1.tga 0.468750000000 0.648437500000 0.492187500000 0.671875000000 -us_over_2.tga 0.492187500000 0.648437500000 0.515625000000 0.671875000000 -us_over_3.tga 0.515625000000 0.648437500000 0.539062500000 0.671875000000 -us_over_4.tga 0.539062500000 0.648437500000 0.562500000000 0.671875000000 -mf_over.tga 0.562500000000 0.648437500000 0.585937500000 0.671875000000 -ico_aim_homin_feet.tga 0.585937500000 0.648437500000 0.609375000000 0.671875000000 -ico_aim_homin_feint.tga 0.609375000000 0.648437500000 0.632812500000 0.671875000000 -ico_aim_homin_hands.tga 0.632812500000 0.648437500000 0.656250000000 0.671875000000 -ico_aim_homin_head.tga 0.656250000000 0.648437500000 0.679687500000 0.671875000000 -ico_aim_homin_legs.tga 0.679687500000 0.648437500000 0.703125000000 0.671875000000 -mp3.tga 0.703125000000 0.648437500000 0.726562500000 0.671875000000 -W_slot_shortcut_id0.tga 0.914062500000 0.656250000000 0.937500000000 0.679687500000 -W_slot_shortcut_id1.tga 0.726562500000 0.664062500000 0.750000000000 0.687500000000 -W_slot_shortcut_id2.tga 0.750000000000 0.664062500000 0.773437500000 0.687500000000 -W_slot_shortcut_id3.tga 0.773437500000 0.664062500000 0.796875000000 0.687500000000 -W_slot_shortcut_id4.tga 0.796875000000 0.664062500000 0.820312500000 0.687500000000 -W_slot_shortcut_id5.tga 0.820312500000 0.664062500000 0.843750000000 0.687500000000 -W_slot_shortcut_id6.tga 0.843750000000 0.664062500000 0.867187500000 0.687500000000 -W_slot_shortcut_id7.tga 0.867187500000 0.664062500000 0.890625000000 0.687500000000 -W_slot_shortcut_id8.tga 0.890625000000 0.664062500000 0.914062500000 0.687500000000 -W_slot_shortcut_id9.tga 0.937500000000 0.664062500000 0.960937500000 0.687500000000 -w_slot_shortcut_shift_id0.tga 0.960937500000 0.664062500000 0.984375000000 0.687500000000 -w_slot_shortcut_shift_id1.tga 0.000000000000 0.671875000000 0.023437500000 0.695312500000 -w_slot_shortcut_shift_id2.tga 0.023437500000 0.671875000000 0.046875000000 0.695312500000 -w_slot_shortcut_shift_id3.tga 0.046875000000 0.671875000000 0.070312500000 0.695312500000 -w_slot_shortcut_shift_id4.tga 0.070312500000 0.671875000000 0.093750000000 0.695312500000 -w_slot_shortcut_shift_id5.tga 0.093750000000 0.671875000000 0.117187500000 0.695312500000 -w_slot_shortcut_shift_id6.tga 0.117187500000 0.671875000000 0.140625000000 0.695312500000 -w_slot_shortcut_shift_id7.tga 0.140625000000 0.671875000000 0.164062500000 0.695312500000 -w_slot_shortcut_shift_id8.tga 0.164062500000 0.671875000000 0.187500000000 0.695312500000 -w_slot_shortcut_shift_id9.tga 0.187500000000 0.671875000000 0.210937500000 0.695312500000 -ico_aim_kitin_head.tga 0.210937500000 0.671875000000 0.234375000000 0.695312500000 -ico_source_knowledge.tga 0.234375000000 0.671875000000 0.255859375000 0.695312500000 +ico_taunt.tga 0.976562500000 0.078125000000 1.000000000000 0.101562500000 +BK_generic_brick.tga 0.976562500000 0.101562500000 1.000000000000 0.125000000000 +bk_aura.tga 0.976562500000 0.125000000000 1.000000000000 0.148437500000 +bk_outpost_brick.tga 0.976562500000 0.148437500000 1.000000000000 0.171875000000 +bk_power.tga 0.976562500000 0.171875000000 1.000000000000 0.195312500000 +no_action.tga 0.976562500000 0.195312500000 1.000000000000 0.218750000000 +no_pvp.tga 0.976562500000 0.218750000000 1.000000000000 0.242187500000 +op_back.tga 0.976562500000 0.242187500000 1.000000000000 0.265625000000 +op_over_break.tga 0.976562500000 0.265625000000 1.000000000000 0.289062500000 +op_over_less.tga 0.976562500000 0.289062500000 1.000000000000 0.312500000000 +op_over_more.tga 0.976562500000 0.312500000000 1.000000000000 0.335937500000 +bk_conso.tga 0.976562500000 0.335937500000 1.000000000000 0.359375000000 +pa_back.tga 0.976562500000 0.359375000000 1.000000000000 0.382812500000 +2h_over.tga 0.976562500000 0.382812500000 1.000000000000 0.406250000000 +1h_over.tga 0.976562500000 0.406250000000 1.000000000000 0.429687500000 +pvp_duel.tga 0.976562500000 0.429687500000 1.000000000000 0.453125000000 +pvp_enemy_0.tga 0.976562500000 0.453125000000 1.000000000000 0.476562500000 +pvp_enemy_1.tga 0.960937500000 0.476562500000 0.984375000000 0.500000000000 +pvp_enemy_2.tga 0.929687500000 0.492187500000 0.953125000000 0.515625000000 +pvp_enemy_3.tga 0.742187500000 0.500000000000 0.765625000000 0.523437500000 +pvp_enemy_4.tga 0.765625000000 0.500000000000 0.789062500000 0.523437500000 +pvp_enemy_6.tga 0.789062500000 0.500000000000 0.812500000000 0.523437500000 +pvp_enemy_flag.tga 0.812500000000 0.500000000000 0.835937500000 0.523437500000 +pvp_enemy_marauder.tga 0.835937500000 0.500000000000 0.859375000000 0.523437500000 +pvp_enemy_tag.tga 0.859375000000 0.500000000000 0.882812500000 0.523437500000 +tb_action_attack.tga 0.882812500000 0.500000000000 0.906250000000 0.523437500000 +tb_action_config.tga 0.906250000000 0.500000000000 0.929687500000 0.523437500000 +tb_action_disband.tga 0.953125000000 0.500000000000 0.976562500000 0.523437500000 +tb_action_disengage.tga 0.976562500000 0.500000000000 1.000000000000 0.523437500000 +tb_action_extract.tga 0.000000000000 0.507812500000 0.023437500000 0.531250000000 +tb_action_invite.tga 0.023437500000 0.507812500000 0.046875000000 0.531250000000 +tb_action_kick.tga 0.046875000000 0.507812500000 0.070312500000 0.531250000000 +tb_action_move.tga 0.070312500000 0.507812500000 0.093750000000 0.531250000000 +tb_action_run.tga 0.093750000000 0.507812500000 0.117187500000 0.531250000000 +tb_action_sit.tga 0.117187500000 0.507812500000 0.140625000000 0.531250000000 +tb_action_stand.tga 0.140625000000 0.507812500000 0.164062500000 0.531250000000 +tb_action_stop.tga 0.164062500000 0.507812500000 0.187500000000 0.531250000000 +tb_action_talk.tga 0.187500000000 0.507812500000 0.210937500000 0.531250000000 +tb_action_walk.tga 0.210937500000 0.507812500000 0.234375000000 0.531250000000 +ico_armor_penalty.tga 0.234375000000 0.507812500000 0.257812500000 0.531250000000 +ico_armor_shell.tga 0.257812500000 0.507812500000 0.281250000000 0.531250000000 +ico_atys.tga 0.281250000000 0.507812500000 0.304687500000 0.531250000000 +ico_atysian.tga 0.304687500000 0.507812500000 0.328125000000 0.531250000000 +ico_balance_hp.tga 0.328125000000 0.507812500000 0.351562500000 0.531250000000 +ico_barrel.tga 0.351562500000 0.507812500000 0.375000000000 0.531250000000 +ico_bash.tga 0.375000000000 0.507812500000 0.398437500000 0.531250000000 +ico_berserk.tga 0.398437500000 0.507812500000 0.421875000000 0.531250000000 +ico_blade.tga 0.421875000000 0.507812500000 0.445312500000 0.531250000000 +ico_bleeding.tga 0.445312500000 0.507812500000 0.468750000000 0.531250000000 +ico_blind.tga 0.468750000000 0.507812500000 0.492187500000 0.531250000000 +ico_blunt.tga 0.492187500000 0.507812500000 0.515625000000 0.531250000000 +ico_bomb.tga 0.515625000000 0.507812500000 0.539062500000 0.531250000000 +pvp_enemy_trytonist.tga 0.539062500000 0.507812500000 0.562500000000 0.531250000000 +ico_celestial.tga 0.562500000000 0.507812500000 0.585937500000 0.531250000000 +ico_circular_attack.tga 0.585937500000 0.507812500000 0.609375000000 0.531250000000 +ico_cold.tga 0.609375000000 0.507812500000 0.632812500000 0.531250000000 +ico_concentration.tga 0.632812500000 0.507812500000 0.656250000000 0.531250000000 +pvp_flag.tga 0.656250000000 0.507812500000 0.679687500000 0.531250000000 +ico_constitution.tga 0.679687500000 0.507812500000 0.703125000000 0.531250000000 +ico_counterweight.tga 0.703125000000 0.507812500000 0.726562500000 0.531250000000 +ico_craft_buff.tga 0.929687500000 0.515625000000 0.953125000000 0.539062500000 +ico_create_sapload.tga 0.726562500000 0.523437500000 0.750000000000 0.546875000000 +ico_curse.tga 0.750000000000 0.523437500000 0.773437500000 0.546875000000 +ico_debuff.tga 0.773437500000 0.523437500000 0.796875000000 0.546875000000 +ico_debuff_resist.tga 0.796875000000 0.523437500000 0.820312500000 0.546875000000 +ico_debuff_skill.tga 0.820312500000 0.523437500000 0.843750000000 0.546875000000 +ico_desert.tga 0.843750000000 0.523437500000 0.867187500000 0.546875000000 +ico_dexterity.tga 0.867187500000 0.523437500000 0.890625000000 0.546875000000 +ico_disarm.tga 0.890625000000 0.523437500000 0.914062500000 0.546875000000 +ico_dodge.tga 0.953125000000 0.523437500000 0.976562500000 0.546875000000 +ico_dot.tga 0.976562500000 0.523437500000 1.000000000000 0.546875000000 +ico_electric.tga 0.000000000000 0.531250000000 0.023437500000 0.554687500000 +ico_explosif.tga 0.023437500000 0.531250000000 0.046875000000 0.554687500000 +ico_extracting.tga 0.046875000000 0.531250000000 0.070312500000 0.554687500000 +ico_fear.tga 0.070312500000 0.531250000000 0.093750000000 0.554687500000 +ico_feint.tga 0.093750000000 0.531250000000 0.117187500000 0.554687500000 +ico_fire.tga 0.117187500000 0.531250000000 0.140625000000 0.554687500000 +ico_firing_pin.tga 0.140625000000 0.531250000000 0.164062500000 0.554687500000 +pvp_neutral.tga 0.164062500000 0.531250000000 0.187500000000 0.554687500000 +pvp_tag.tga 0.187500000000 0.531250000000 0.210937500000 0.554687500000 +BK_magie_noire_brick.tga 0.210937500000 0.531250000000 0.234375000000 0.554687500000 +cp_back.tga 0.234375000000 0.531250000000 0.257812500000 0.554687500000 +cp_over_break.tga 0.257812500000 0.531250000000 0.281250000000 0.554687500000 +cp_over_less.tga 0.281250000000 0.531250000000 0.304687500000 0.554687500000 +ico_focus.tga 0.304687500000 0.531250000000 0.328125000000 0.554687500000 +ico_forage_buff.tga 0.328125000000 0.531250000000 0.351562500000 0.554687500000 +ico_forbid_item.tga 0.351562500000 0.531250000000 0.375000000000 0.554687500000 +ico_forest.tga 0.375000000000 0.531250000000 0.398437500000 0.554687500000 +ico_jungle.tga 0.398437500000 0.531250000000 0.421875000000 0.554687500000 +ico_lacustre.tga 0.421875000000 0.531250000000 0.445312500000 0.554687500000 +ico_landmark_bonus.tga 0.445312500000 0.531250000000 0.468750000000 0.554687500000 +ico_level.tga 0.468750000000 0.531250000000 0.492187500000 0.554687500000 +ico_lining.tga 0.492187500000 0.531250000000 0.515625000000 0.554687500000 +ico_location.tga 0.515625000000 0.531250000000 0.539062500000 0.554687500000 +ico_madness.tga 0.539062500000 0.531250000000 0.562500000000 0.554687500000 +ico_magic.tga 0.562500000000 0.531250000000 0.585937500000 0.554687500000 +ico_magic_action_buff.tga 0.585937500000 0.531250000000 0.609375000000 0.554687500000 +ico_magic_focus.tga 0.609375000000 0.531250000000 0.632812500000 0.554687500000 +ico_magic_target_buff.tga 0.632812500000 0.531250000000 0.656250000000 0.554687500000 +ico_melee_action_buff.tga 0.656250000000 0.531250000000 0.679687500000 0.554687500000 +ico_melee_target_buff.tga 0.679687500000 0.531250000000 0.703125000000 0.554687500000 +ico_mental.tga 0.703125000000 0.531250000000 0.726562500000 0.554687500000 +ico_metabolism.tga 0.914062500000 0.539062500000 0.937500000000 0.562500000000 +ico_mezz.tga 0.726562500000 0.546875000000 0.750000000000 0.570312500000 +cp_over_more.tga 0.750000000000 0.546875000000 0.773437500000 0.570312500000 +cp_over_opening.tga 0.773437500000 0.546875000000 0.796875000000 0.570312500000 +tb_animals.tga 0.796875000000 0.546875000000 0.820312500000 0.570312500000 +cp_over_opening_2.tga 0.820312500000 0.546875000000 0.843750000000 0.570312500000 +us_back_9.tga 0.843750000000 0.546875000000 0.867187500000 0.570312500000 +BK_tryker_brick.tga 0.867187500000 0.546875000000 0.890625000000 0.570312500000 +ico_spray.tga 0.890625000000 0.546875000000 0.914062500000 0.570312500000 +ico_spying.tga 0.937500000000 0.546875000000 0.960937500000 0.570312500000 +ico_stamina.tga 0.960937500000 0.546875000000 0.984375000000 0.570312500000 +ico_strength.tga 0.000000000000 0.554687500000 0.023437500000 0.578125000000 +ico_stuffing.tga 0.023437500000 0.554687500000 0.046875000000 0.578125000000 +ico_stunn.tga 0.046875000000 0.554687500000 0.070312500000 0.578125000000 +ico_move.tga 0.070312500000 0.554687500000 0.093750000000 0.578125000000 +ico_multiple_spots.tga 0.093750000000 0.554687500000 0.117187500000 0.578125000000 +ico_multi_fight.tga 0.117187500000 0.554687500000 0.140625000000 0.578125000000 +ico_opening_hit.tga 0.140625000000 0.554687500000 0.164062500000 0.578125000000 +ico_over_autumn.tga 0.164062500000 0.554687500000 0.187500000000 0.578125000000 +ico_over_degenerated.tga 0.187500000000 0.554687500000 0.210937500000 0.578125000000 +ico_over_fauna.tga 0.210937500000 0.554687500000 0.234375000000 0.578125000000 +ico_over_flora.tga 0.234375000000 0.554687500000 0.257812500000 0.578125000000 +ico_over_hit_arms.tga 0.257812500000 0.554687500000 0.281250000000 0.578125000000 +ico_over_hit_chest.tga 0.281250000000 0.554687500000 0.304687500000 0.578125000000 +ico_over_hit_feet.tga 0.304687500000 0.554687500000 0.328125000000 0.578125000000 +ico_over_hit_feet_hands.tga 0.328125000000 0.554687500000 0.351562500000 0.578125000000 +ico_over_hit_feet_head.tga 0.351562500000 0.554687500000 0.375000000000 0.578125000000 +ico_over_hit_feet_x2.tga 0.375000000000 0.554687500000 0.398437500000 0.578125000000 +ico_over_hit_feint_x3.tga 0.398437500000 0.554687500000 0.421875000000 0.578125000000 +ico_over_hit_hands.tga 0.421875000000 0.554687500000 0.445312500000 0.578125000000 +ico_over_hit_hands_chest.tga 0.445312500000 0.554687500000 0.468750000000 0.578125000000 +ico_over_hit_hands_head.tga 0.468750000000 0.554687500000 0.492187500000 0.578125000000 +ico_over_hit_head.tga 0.492187500000 0.554687500000 0.515625000000 0.578125000000 +ico_over_hit_legs.tga 0.515625000000 0.554687500000 0.539062500000 0.578125000000 +ico_over_homin.tga 0.539062500000 0.554687500000 0.562500000000 0.578125000000 +ico_over_kitin.tga 0.562500000000 0.554687500000 0.585937500000 0.578125000000 +ico_over_magic.tga 0.585937500000 0.554687500000 0.609375000000 0.578125000000 +ico_over_melee.tga 0.609375000000 0.554687500000 0.632812500000 0.578125000000 +ico_over_racial.tga 0.632812500000 0.554687500000 0.656250000000 0.578125000000 +ico_over_range.tga 0.656250000000 0.554687500000 0.679687500000 0.578125000000 +ico_over_special.tga 0.679687500000 0.554687500000 0.703125000000 0.578125000000 +ico_over_spring.tga 0.703125000000 0.554687500000 0.726562500000 0.578125000000 +ico_over_summer.tga 0.914062500000 0.562500000000 0.937500000000 0.585937500000 +ico_over_winter.tga 0.726562500000 0.570312500000 0.750000000000 0.593750000000 +ico_parry.tga 0.750000000000 0.570312500000 0.773437500000 0.593750000000 +ico_piercing.tga 0.773437500000 0.570312500000 0.796875000000 0.593750000000 +ico_pointe.tga 0.796875000000 0.570312500000 0.820312500000 0.593750000000 +ico_poison.tga 0.820312500000 0.570312500000 0.843750000000 0.593750000000 +ico_power.tga 0.843750000000 0.570312500000 0.867187500000 0.593750000000 +ico_preservation.tga 0.867187500000 0.570312500000 0.890625000000 0.593750000000 +ico_prime_roots.tga 0.890625000000 0.570312500000 0.914062500000 0.593750000000 +ico_private.tga 0.937500000000 0.570312500000 0.960937500000 0.593750000000 +ico_prospecting.tga 0.960937500000 0.570312500000 0.984375000000 0.593750000000 +ico_quality.tga 0.000000000000 0.578125000000 0.023437500000 0.601562500000 +BK_fyros_brick.tga 0.023437500000 0.578125000000 0.046875000000 0.601562500000 +ico_range.tga 0.046875000000 0.578125000000 0.070312500000 0.601562500000 +ico_range_action_buff.tga 0.070312500000 0.578125000000 0.093750000000 0.601562500000 +ico_range_target_buff.tga 0.093750000000 0.578125000000 0.117187500000 0.601562500000 +ico_ricochet.tga 0.117187500000 0.578125000000 0.140625000000 0.601562500000 +ico_root.tga 0.140625000000 0.578125000000 0.164062500000 0.601562500000 +ico_rot.tga 0.164062500000 0.578125000000 0.187500000000 0.601562500000 +ico_safe.tga 0.187500000000 0.578125000000 0.210937500000 0.601562500000 +ico_sap.tga 0.210937500000 0.578125000000 0.234375000000 0.601562500000 +ico_self_damage.tga 0.234375000000 0.578125000000 0.257812500000 0.601562500000 +ico_shaft.tga 0.257812500000 0.578125000000 0.281250000000 0.601562500000 +ico_shielding.tga 0.281250000000 0.578125000000 0.304687500000 0.601562500000 +ico_shield_buff.tga 0.304687500000 0.578125000000 0.328125000000 0.601562500000 +ico_shield_up.tga 0.328125000000 0.578125000000 0.351562500000 0.601562500000 +ico_shockwave.tga 0.351562500000 0.578125000000 0.375000000000 0.601562500000 +ico_sickness.tga 0.375000000000 0.578125000000 0.398437500000 0.601562500000 +ico_slashing.tga 0.398437500000 0.578125000000 0.421875000000 0.601562500000 +ico_slow.tga 0.421875000000 0.578125000000 0.445312500000 0.601562500000 +ico_soft_spot.tga 0.445312500000 0.578125000000 0.468750000000 0.601562500000 +ico_source_time.tga 0.468750000000 0.578125000000 0.492187500000 0.601562500000 +ico_speed.tga 0.492187500000 0.578125000000 0.515625000000 0.601562500000 +ico_speeding_up.tga 0.515625000000 0.578125000000 0.539062500000 0.601562500000 +ico_spell_break.tga 0.539062500000 0.578125000000 0.562500000000 0.601562500000 +fp_armor.tga 0.562500000000 0.578125000000 0.585937500000 0.601562500000 +fp_building.tga 0.585937500000 0.578125000000 0.609375000000 0.601562500000 +fp_jewel.tga 0.609375000000 0.578125000000 0.632812500000 0.601562500000 +fp_melee.tga 0.632812500000 0.578125000000 0.656250000000 0.601562500000 +fp_over.tga 0.656250000000 0.578125000000 0.679687500000 0.601562500000 +fp_range.tga 0.679687500000 0.578125000000 0.703125000000 0.601562500000 +fp_shield.tga 0.703125000000 0.578125000000 0.726562500000 0.601562500000 +fp_tools.tga 0.914062500000 0.585937500000 0.937500000000 0.609375000000 +ef_back.tga 0.726562500000 0.593750000000 0.750000000000 0.617187500000 +ico_absorb_damage.tga 0.750000000000 0.593750000000 0.773437500000 0.617187500000 +ico_accurate.tga 0.773437500000 0.593750000000 0.796875000000 0.617187500000 +ico_acid.tga 0.796875000000 0.593750000000 0.820312500000 0.617187500000 +ico_aim.tga 0.820312500000 0.593750000000 0.843750000000 0.617187500000 +ico_aim_bird_wings.tga 0.843750000000 0.593750000000 0.867187500000 0.617187500000 +ico_aim_flying_kitin_abdomen.tga 0.867187500000 0.593750000000 0.890625000000 0.617187500000 +ico_aim_homin_arms.tga 0.890625000000 0.593750000000 0.914062500000 0.617187500000 +ico_aim_homin_chest.tga 0.937500000000 0.593750000000 0.960937500000 0.617187500000 +ico_aim_homin_feet.tga 0.960937500000 0.593750000000 0.984375000000 0.617187500000 +ico_aim_homin_feint.tga 0.000000000000 0.601562500000 0.023437500000 0.625000000000 +ico_aim_homin_hands.tga 0.023437500000 0.601562500000 0.046875000000 0.625000000000 +ico_aim_homin_head.tga 0.046875000000 0.601562500000 0.070312500000 0.625000000000 +ico_aim_homin_legs.tga 0.070312500000 0.601562500000 0.093750000000 0.625000000000 +ico_aim_kitin_head.tga 0.093750000000 0.601562500000 0.117187500000 0.625000000000 +ef_over_break.tga 0.117187500000 0.601562500000 0.140625000000 0.625000000000 +ico_ammo_bullet.tga 0.140625000000 0.601562500000 0.164062500000 0.625000000000 +ico_ammo_jacket.tga 0.164062500000 0.601562500000 0.187500000000 0.625000000000 +ico_angle.tga 0.187500000000 0.601562500000 0.210937500000 0.625000000000 +ico_anti_magic_shield.tga 0.210937500000 0.601562500000 0.234375000000 0.625000000000 +ico_armor.tga 0.234375000000 0.601562500000 0.257812500000 0.625000000000 +ico_armor_clip.tga 0.257812500000 0.601562500000 0.281250000000 0.625000000000 +ico_armor_heavy.tga 0.281250000000 0.601562500000 0.304687500000 0.625000000000 +ico_armor_kitin.tga 0.304687500000 0.601562500000 0.328125000000 0.625000000000 +ico_armor_light.tga 0.328125000000 0.601562500000 0.351562500000 0.625000000000 +ef_over_less.tga 0.351562500000 0.601562500000 0.375000000000 0.625000000000 +ef_over_more.tga 0.375000000000 0.601562500000 0.398437500000 0.625000000000 +fo_back.tga 0.398437500000 0.601562500000 0.421875000000 0.625000000000 +fo_over.tga 0.421875000000 0.601562500000 0.445312500000 0.625000000000 +ico_gardening.tga 0.445312500000 0.601562500000 0.468750000000 0.625000000000 +ico_gentle.tga 0.468750000000 0.601562500000 0.492187500000 0.625000000000 +mp_over_link.tga 0.492187500000 0.601562500000 0.515625000000 0.625000000000 +ico_goo.tga 0.515625000000 0.601562500000 0.539062500000 0.625000000000 +us_back_0.tga 0.539062500000 0.601562500000 0.562500000000 0.625000000000 +us_back_1.tga 0.562500000000 0.601562500000 0.585937500000 0.625000000000 +us_back_2.tga 0.585937500000 0.601562500000 0.609375000000 0.625000000000 +us_back_3.tga 0.609375000000 0.601562500000 0.632812500000 0.625000000000 +us_back_4.tga 0.632812500000 0.601562500000 0.656250000000 0.625000000000 +us_back_5.tga 0.656250000000 0.601562500000 0.679687500000 0.625000000000 +us_back_6.tga 0.679687500000 0.601562500000 0.703125000000 0.625000000000 +us_back_7.tga 0.703125000000 0.601562500000 0.726562500000 0.625000000000 +us_back_8.tga 0.914062500000 0.609375000000 0.937500000000 0.632812500000 +tb_config.tga 0.726562500000 0.617187500000 0.750000000000 0.640625000000 +tb_connection.tga 0.750000000000 0.617187500000 0.773437500000 0.640625000000 +tb_contacts.tga 0.773437500000 0.617187500000 0.796875000000 0.640625000000 +tb_desk_1.tga 0.796875000000 0.617187500000 0.820312500000 0.640625000000 +tb_desk_2.tga 0.820312500000 0.617187500000 0.843750000000 0.640625000000 +tb_desk_3.tga 0.843750000000 0.617187500000 0.867187500000 0.640625000000 +tb_desk_4.tga 0.867187500000 0.617187500000 0.890625000000 0.640625000000 +tb_faction.tga 0.890625000000 0.617187500000 0.914062500000 0.640625000000 +tb_forum.tga 0.937500000000 0.617187500000 0.960937500000 0.640625000000 +tb_guild.tga 0.960937500000 0.617187500000 0.984375000000 0.640625000000 +TB_help2.tga 0.000000000000 0.625000000000 0.023437500000 0.648437500000 +tb_keys.tga 0.023437500000 0.625000000000 0.046875000000 0.648437500000 +tb_macros.tga 0.046875000000 0.625000000000 0.070312500000 0.648437500000 +tb_mail.tga 0.070312500000 0.625000000000 0.093750000000 0.648437500000 +tb_mode_dodge.tga 0.093750000000 0.625000000000 0.117187500000 0.648437500000 +tb_mode_parry.tga 0.117187500000 0.625000000000 0.140625000000 0.648437500000 +tb_over.tga 0.140625000000 0.625000000000 0.164062500000 0.648437500000 +tb_support.tga 0.164062500000 0.625000000000 0.187500000000 0.648437500000 +tb_team.tga 0.187500000000 0.625000000000 0.210937500000 0.648437500000 +tb_windows.tga 0.210937500000 0.625000000000 0.234375000000 0.648437500000 +ico_gripp.tga 0.234375000000 0.625000000000 0.257812500000 0.648437500000 +BK_zorai_brick.tga 0.257812500000 0.625000000000 0.281250000000 0.648437500000 +mf_back.tga 0.281250000000 0.625000000000 0.304687500000 0.648437500000 +mf_over.tga 0.304687500000 0.625000000000 0.328125000000 0.648437500000 +brick_default.tga 0.328125000000 0.625000000000 0.351562500000 0.648437500000 +ico_hammer.tga 0.351562500000 0.625000000000 0.375000000000 0.648437500000 +ico_harmful.tga 0.375000000000 0.625000000000 0.398437500000 0.648437500000 +ico_hatred.tga 0.398437500000 0.625000000000 0.421875000000 0.648437500000 +ico_heal.tga 0.421875000000 0.625000000000 0.445312500000 0.648437500000 +mp3.tga 0.445312500000 0.625000000000 0.468750000000 0.648437500000 +ico_hit_rate.tga 0.468750000000 0.625000000000 0.492187500000 0.648437500000 +mp_back_curative.tga 0.492187500000 0.625000000000 0.515625000000 0.648437500000 +mp_back_offensive.tga 0.515625000000 0.625000000000 0.539062500000 0.648437500000 +ico_time.tga 0.539062500000 0.625000000000 0.562500000000 0.648437500000 +ico_time_bonus.tga 0.562500000000 0.625000000000 0.585937500000 0.648437500000 +mp_back_selfonly.tga 0.585937500000 0.625000000000 0.609375000000 0.648437500000 +ico_trigger.tga 0.609375000000 0.625000000000 0.632812500000 0.648437500000 +ico_umbrella.tga 0.632812500000 0.625000000000 0.656250000000 0.648437500000 +ico_use_enchantement.tga 0.656250000000 0.625000000000 0.679687500000 0.648437500000 +ico_vampire.tga 0.679687500000 0.625000000000 0.703125000000 0.648437500000 +ico_visibility.tga 0.703125000000 0.625000000000 0.726562500000 0.648437500000 +ico_war_cry.tga 0.914062500000 0.632812500000 0.937500000000 0.656250000000 +ico_weight.tga 0.726562500000 0.640625000000 0.750000000000 0.664062500000 +ico_wellbalanced.tga 0.750000000000 0.640625000000 0.773437500000 0.664062500000 +ico_will.tga 0.773437500000 0.640625000000 0.796875000000 0.664062500000 +ico_windding.tga 0.796875000000 0.640625000000 0.820312500000 0.664062500000 +ico_wisdom.tga 0.820312500000 0.640625000000 0.843750000000 0.664062500000 +ico_incapacity.tga 0.843750000000 0.640625000000 0.867187500000 0.664062500000 +ico_intelligence.tga 0.867187500000 0.640625000000 0.890625000000 0.664062500000 +ico_interrupt.tga 0.890625000000 0.640625000000 0.914062500000 0.664062500000 +ico_invulnerability.tga 0.937500000000 0.640625000000 0.960937500000 0.664062500000 +ico_jewel_stone.tga 0.960937500000 0.640625000000 0.984375000000 0.664062500000 +us_ico_0.tga 0.000000000000 0.648437500000 0.023437500000 0.671875000000 +us_ico_1.tga 0.023437500000 0.648437500000 0.046875000000 0.671875000000 +us_ico_2.tga 0.046875000000 0.648437500000 0.070312500000 0.671875000000 +us_ico_3.tga 0.070312500000 0.648437500000 0.093750000000 0.671875000000 +us_ico_4.tga 0.093750000000 0.648437500000 0.117187500000 0.671875000000 +us_ico_5.tga 0.117187500000 0.648437500000 0.140625000000 0.671875000000 +us_ico_6.tga 0.140625000000 0.648437500000 0.164062500000 0.671875000000 +us_ico_7.tga 0.164062500000 0.648437500000 0.187500000000 0.671875000000 +us_ico_8.tga 0.187500000000 0.648437500000 0.210937500000 0.671875000000 +us_ico_9.tga 0.210937500000 0.648437500000 0.234375000000 0.671875000000 +us_over_0.tga 0.234375000000 0.648437500000 0.257812500000 0.671875000000 +us_over_1.tga 0.257812500000 0.648437500000 0.281250000000 0.671875000000 +us_over_2.tga 0.281250000000 0.648437500000 0.304687500000 0.671875000000 +us_over_3.tga 0.304687500000 0.648437500000 0.328125000000 0.671875000000 +us_over_4.tga 0.328125000000 0.648437500000 0.351562500000 0.671875000000 +building_state_24x24.tga 0.351562500000 0.648437500000 0.375000000000 0.671875000000 +cb_main_nue.tga 0.375000000000 0.648437500000 0.398437500000 0.671875000000 +fp_ammo.tga 0.398437500000 0.648437500000 0.421875000000 0.671875000000 +ico_armor_medium.tga 0.421875000000 0.648437500000 0.445312500000 0.671875000000 +ico_clothes.tga 0.445312500000 0.648437500000 0.468750000000 0.671875000000 +ico_durability.tga 0.468750000000 0.648437500000 0.492187500000 0.671875000000 +W_slot_shortcut_id0.tga 0.492187500000 0.648437500000 0.515625000000 0.671875000000 +W_slot_shortcut_id1.tga 0.515625000000 0.648437500000 0.539062500000 0.671875000000 +W_slot_shortcut_id2.tga 0.539062500000 0.648437500000 0.562500000000 0.671875000000 +W_slot_shortcut_id3.tga 0.562500000000 0.648437500000 0.585937500000 0.671875000000 +W_slot_shortcut_id4.tga 0.585937500000 0.648437500000 0.609375000000 0.671875000000 +W_slot_shortcut_id5.tga 0.609375000000 0.648437500000 0.632812500000 0.671875000000 +W_slot_shortcut_id6.tga 0.632812500000 0.648437500000 0.656250000000 0.671875000000 +W_slot_shortcut_id7.tga 0.656250000000 0.648437500000 0.679687500000 0.671875000000 +W_slot_shortcut_id8.tga 0.679687500000 0.648437500000 0.703125000000 0.671875000000 +W_slot_shortcut_id9.tga 0.703125000000 0.648437500000 0.726562500000 0.671875000000 +w_slot_shortcut_shift_id0.tga 0.914062500000 0.656250000000 0.937500000000 0.679687500000 +w_slot_shortcut_shift_id1.tga 0.726562500000 0.664062500000 0.750000000000 0.687500000000 +w_slot_shortcut_shift_id2.tga 0.750000000000 0.664062500000 0.773437500000 0.687500000000 +w_slot_shortcut_shift_id3.tga 0.773437500000 0.664062500000 0.796875000000 0.687500000000 +w_slot_shortcut_shift_id4.tga 0.796875000000 0.664062500000 0.820312500000 0.687500000000 +w_slot_shortcut_shift_id5.tga 0.820312500000 0.664062500000 0.843750000000 0.687500000000 +w_slot_shortcut_shift_id6.tga 0.843750000000 0.664062500000 0.867187500000 0.687500000000 +w_slot_shortcut_shift_id7.tga 0.867187500000 0.664062500000 0.890625000000 0.687500000000 +w_slot_shortcut_shift_id8.tga 0.890625000000 0.664062500000 0.914062500000 0.687500000000 +w_slot_shortcut_shift_id9.tga 0.937500000000 0.664062500000 0.960937500000 0.687500000000 +BK_matis_brick.tga 0.960937500000 0.664062500000 0.984375000000 0.687500000000 +ico_jewel_stone_support.tga 0.000000000000 0.671875000000 0.023437500000 0.695312500000 +ico_misfortune.tga 0.023437500000 0.671875000000 0.046875000000 0.695312500000 +pa_over_break.tga 0.046875000000 0.671875000000 0.070312500000 0.695312500000 +pa_over_less.tga 0.070312500000 0.671875000000 0.093750000000 0.695312500000 +pa_over_more.tga 0.093750000000 0.671875000000 0.117187500000 0.695312500000 +ch_back.tga 0.117187500000 0.671875000000 0.140625000000 0.695312500000 +ico_over_hit_head_x3.tga 0.140625000000 0.671875000000 0.164062500000 0.695312500000 +ico_primal.tga 0.164062500000 0.671875000000 0.187500000000 0.695312500000 +pvp_ally_0.tga 0.187500000000 0.671875000000 0.210937500000 0.695312500000 +pvp_ally_1.tga 0.210937500000 0.671875000000 0.234375000000 0.695312500000 +pvp_ally_2.tga 0.234375000000 0.671875000000 0.257812500000 0.695312500000 +pvp_ally_3.tga 0.257812500000 0.671875000000 0.281250000000 0.695312500000 +pvp_ally_4.tga 0.281250000000 0.671875000000 0.304687500000 0.695312500000 +pvp_ally_6.tga 0.304687500000 0.671875000000 0.328125000000 0.695312500000 +pvp_ally_flag.tga 0.328125000000 0.671875000000 0.351562500000 0.695312500000 +pvp_ally_primas.tga 0.351562500000 0.671875000000 0.375000000000 0.695312500000 +pvp_ally_ranger.tga 0.375000000000 0.671875000000 0.398437500000 0.695312500000 +pvp_ally_tag.tga 0.398437500000 0.671875000000 0.421875000000 0.695312500000 +bg_downloader.tga 0.421875000000 0.671875000000 0.445312500000 0.695312500000 +ico_source_knowledge.tga 0.445312500000 0.671875000000 0.466796875000 0.695312500000 +filter_tp.tga 0.468750000000 0.671875000000 0.492187500000 0.687500000000 small_task_done.tga 0.984375000000 0.000000000000 1.000000000000 0.015625000000 small_task_failed.tga 0.984375000000 0.015625000000 1.000000000000 0.031250000000 small_task_fight.tga 0.984375000000 0.031250000000 1.000000000000 0.046875000000 @@ -669,6 +679,6 @@ small_task_travel.tga 0.914062500000 0.523437500000 0.929687500000 0.53906250000 small_task_craft.tga 0.984375000000 0.546875000000 1.000000000000 0.562500000000 num_slash.tga 0.984375000000 0.562500000000 0.996093750000 0.576171875000 W_leader.tga 0.984375000000 0.578125000000 0.997070312500 0.589843750000 -tb_mode.tga 0.984375000000 0.589843750000 0.996093750000 0.601562500000 -profile.tga 0.984375000000 0.601562500000 0.996093750000 0.613281250000 -w_major.tga 0.984375000000 0.613281250000 0.996093750000 0.625000000000 +w_major.tga 0.984375000000 0.589843750000 0.996093750000 0.601562500000 +tb_mode.tga 0.984375000000 0.601562500000 0.996093750000 0.613281250000 +profile.tga 0.984375000000 0.613281250000 0.996093750000 0.625000000000 diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml b/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml index ca013f242..f0455028a 100644 --- a/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml +++ b/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml @@ -78,9 +78,18 @@ - + + + + + + + + + + diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/config.xml b/code/ryzom/client/data/gamedev/interfaces_v3/config.xml index 1d593ef06..9fb0dd958 100644 --- a/code/ryzom/client/data/gamedev/interfaces_v3/config.xml +++ b/code/ryzom/client/data/gamedev/interfaces_v3/config.xml @@ -258,6 +258,9 @@ + @@ -291,6 +294,9 @@ + @@ -327,6 +333,9 @@ + @@ -2797,6 +2806,9 @@ This MUST follow the Enum MISSION_DESC::TIconId + - + + + + + + + + @@ -3045,6 +3081,9 @@ This MUST follow the Enum MISSION_DESC::TIconId + @@ -3066,6 +3105,9 @@ This MUST follow the Enum MISSION_DESC::TIconId + @@ -3087,6 +3129,9 @@ This MUST follow the Enum MISSION_DESC::TIconId + @@ -3108,6 +3153,9 @@ This MUST follow the Enum MISSION_DESC::TIconId + @@ -3129,6 +3177,9 @@ This MUST follow the Enum MISSION_DESC::TIconId + @@ -3151,6 +3202,9 @@ This MUST follow the Enum MISSION_DESC::TIconId + @@ -3173,6 +3227,9 @@ This MUST follow the Enum MISSION_DESC::TIconId + + + diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/encyclopedia.xml b/code/ryzom/client/data/gamedev/interfaces_v3/encyclopedia.xml index b2db7ab26..30f2c34a0 100644 --- a/code/ryzom/client/data/gamedev/interfaces_v3/encyclopedia.xml +++ b/code/ryzom/client/data/gamedev/interfaces_v3/encyclopedia.xml @@ -11,7 +11,7 @@ -

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