diff options
201 files changed, 47748 insertions, 1094 deletions
@@ -14,7 +14,7 @@ Getting the project to build and run on Linux is easy if you use Ubuntu 13.10 (o * Qt 5.1.1+ Development tools (http://qt-project.org/downloads) ("Qt Online Installer for Linux (64 bit)") * A copy of the MultiMC source (clone it with git) * cmake -* build-essentials +* build-essential * zlib (for example, zlib1g-dev) * java (for example, openjdk-7-jdk) * GL headers (for example, libgl1-mesa-dev) diff --git a/CMakeLists.txt b/CMakeLists.txt index cb618f3b..be0eced8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -97,6 +97,9 @@ include_directories(${LIBSETTINGS_INCLUDE_DIR}) add_subdirectory(depends/groupview) include_directories(${LIBGROUPVIEW_INCLUDE_DIR}) +# Add the updater +add_subdirectory(mmc_updater) + ################################ SET UP BUILD OPTIONS ################################ ######## Check endianness ######## @@ -115,24 +118,54 @@ SET(MultiMC_VERSION_MINOR 0) SET(MultiMC_VERSION_BUILD -1 CACHE STRING "Build number. -1 for no build number.") # Build type -SET(MultiMC_VERSION_BUILD_TYPE "custombuild" CACHE STRING "Build type. Usually corresponds to the buildbot build name. Empty string for no build type.") +SET(MultiMC_VERSION_BUILD_TYPE "custombuild" CACHE STRING "Build type. If this is set, it is appended to the end of the version string with a dash (<version string>-<build type>. It is not used for anything other than indicating in the version string what type of build this is (eg 'lin64').") -SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}") +# Version channel +SET(MultiMC_VERSION_CHANNEL "" CACHE STRING "The current build's channel. Included in the version string.") + +# Channel list URL +SET(MultiMC_CHANLIST_URL "" CACHE STRING "URL for the channel list.") + +# Updater enabled? +SET(MultiMC_UPDATER false CACHE BOOL "Whether or not the update system is enabled. If this is enabled, you must also set MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL in order for it to work properly.") + +# Build a version string to display in the configure logs. +SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_MAJOR}.${MultiMC_VERSION_MINOR}") IF (MultiMC_VERSION_BUILD GREATER -1) SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}.${MultiMC_VERSION_BUILD}") ENDIF () - +IF (NOT MultiMC_VERSION_CHANNEL STREQUAL "") + SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}-${MultiMC_VERSION_CHANNEL}") +ENDIF () IF (NOT MultiMC_VERSION_BUILD_TYPE STREQUAL "") SET(MultiMC_VERSION_STRING "${MultiMC_VERSION_STRING}-${MultiMC_VERSION_BUILD_TYPE}") ENDIF () MESSAGE(STATUS "MultiMC 5 version ${MultiMC_VERSION_STRING}") -# Custom target to just print the version. +# If the update system is enabled, make sure MultiMC_CHANLIST_URL and MultiMC_VERSION_CHANNEL are set. +IF (MultiMC_UPDATER) + IF (MultiMC_VERSION_CHANNEL STREQUAL "") + MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_CHANNEL is not set.\n" + "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.") + ENDIF () + IF (MultiMC_CHANLIST_URL STREQUAL "") + MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_CHANLIST_URL is not set.\n" + "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.") + ENDIF () + IF (MultiMC_VERSION_BUILD LESS 0) + MESSAGE(FATAL_ERROR "Update system is enabled, but MultiMC_VERSION_BUILD is not set.\n" + "Please ensure the CMake variables MultiMC_VERSION_CHANNEL, MultiMC_CHANLIST_URL, and MultiMC_VERSION_BUILD are set.") + ENDIF () + + MESSAGE(STATUS "Updater is enabled. Channel list URL: ${MultiMC_CHANLIST_URL}") +ENDIF () + +#### Custom target to just print the version. ADD_CUSTOM_TARGET(version echo "Version: ${MultiMC_VERSION_STRING}") -# Check the current Git commit +#### Check the current Git commit execute_process(COMMAND git rev-parse HEAD WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE GIT_COMMIT_CHECK_RESULTVAR @@ -140,7 +173,6 @@ execute_process(COMMAND git rev-parse HEAD OUTPUT_STRIP_TRAILING_WHITESPACE ) -# If Git executed successfully IF(GIT_COMMIT_CHECK_RESULTVAR EQUAL 0) SET(MultiMC_GIT_COMMIT "${GIT_COMMIT_CHECK_OUTVAR}") MESSAGE(STATUS "Git commit: ${MultiMC_GIT_COMMIT}") @@ -149,41 +181,8 @@ ELSE() MESSAGE(STATUS "Failed to check Git commit. ${GIT_COMMIT_CHECK_RESULTVAR}") ENDIF() - -######## Set Jenkins info ######## -# Jenkins build tag -IF(DEFINED MultiMC_BUILD_TAG) - MESSAGE(STATUS "Build tag: ${MultiMC_BUILD_TAG}") -ELSE() - MESSAGE(STATUS "No build tag specified.") - SET(MultiMC_BUILD_TAG custom) -ENDIF() - -# Architecture detection -IF(CMAKE_SIZEOF_VOID_P EQUAL 8) - SET(MultiMC_ARCH "x64" CACHE STRING "Architecture we're building for.") -ELSE() - SET(MultiMC_ARCH "x86" CACHE STRING "Architecture we're building for.") -ENDIF() -MESSAGE(STATUS "Architecture is ${MultiMC_ARCH}") - -# Jenkins job name -IF(WIN32) - SET(MultiMC_JOB_NAME "MultiMC5Windows" CACHE STRING "Jenkins job name.") -ELSEIF(UNIX AND APPLE) - SET(MultiMC_JOB_NAME "MultiMC5OSX" CACHE STRING "Jenkins job name.") -ELSE() - SET(MultiMC_JOB_NAME "MultiMC5Linux" CACHE STRING "Jenkins job name.") -ENDIF() - -# Jenkins URL -SET(MultiMC_JOB_URL "http://ci.forkk.net/job/${MultiMC_JOB_NAME}/arch=${MultiMC_ARCH}${MultiMC_Extra_Label}/" - CACHE STRING "URL of the jenkins job to pull updates from.") -MESSAGE(STATUS "Job URL: ${MultiMC_JOB_URL}") - ######## Configure header ######## -configure_file("${PROJECT_SOURCE_DIR}/config.h.in" - "${PROJECT_BINARY_DIR}/include/config.h") +configure_file("${PROJECT_SOURCE_DIR}/config.h.in" "${PROJECT_BINARY_DIR}/include/config.h") ######## Other Stuff ######## @@ -251,6 +250,8 @@ gui/dialogs/AccountListDialog.h gui/dialogs/AccountListDialog.cpp gui/dialogs/AccountSelectDialog.h gui/dialogs/AccountSelectDialog.cpp +gui/dialogs/UpdateDialog.h +gui/dialogs/UpdateDialog.cpp # GUI - widgets gui/widgets/InstanceDelegate.h @@ -284,8 +285,8 @@ logic/InstanceLauncher.cpp # network stuffs logic/net/NetAction.h -logic/net/FileDownload.h -logic/net/FileDownload.cpp +logic/net/MD5EtagDownload.h +logic/net/MD5EtagDownload.cpp logic/net/ByteArrayDownload.h logic/net/ByteArrayDownload.cpp logic/net/CacheDownload.h @@ -300,8 +301,13 @@ logic/net/HttpMetaCache.h logic/net/HttpMetaCache.cpp logic/net/S3ListBucket.h logic/net/S3ListBucket.cpp +logic/net/PasteUpload.h +logic/net/PasteUpload.cpp +logic/net/URLConstants.h # Yggdrasil login stuff +logic/auth/MojangAccountList.h +logic/auth/MojangAccountList.cpp logic/auth/MojangAccount.h logic/auth/MojangAccount.cpp logic/auth/YggdrasilTask.h @@ -312,8 +318,12 @@ logic/auth/flows/RefreshTask.cpp logic/auth/flows/RefreshTask.cpp logic/auth/flows/ValidateTask.h logic/auth/flows/ValidateTask.cpp -logic/auth/flows/InvalidateTask.h -logic/auth/flows/InvalidateTask.cpp + +# Update system +logic/updater/UpdateChecker.h +logic/updater/UpdateChecker.cpp +logic/updater/DownloadUpdateTask.h +logic/updater/DownloadUpdateTask.cpp # legacy instances logic/LegacyInstance.h @@ -325,8 +335,6 @@ logic/LegacyForge.h logic/LegacyForge.cpp # 1.6 instances -logic/OneSixAssets.h -logic/OneSixAssets.cpp logic/OneSixInstance.h logic/OneSixInstance.cpp logic/OneSixInstance_p.h @@ -362,8 +370,6 @@ logic/lists/ForgeVersionList.h logic/lists/ForgeVersionList.cpp logic/lists/JavaVersionList.h logic/lists/JavaVersionList.cpp -logic/lists/MojangAccountList.h -logic/lists/MojangAccountList.cpp # misc model/view logic/EnabledItemFilter.h @@ -383,7 +389,12 @@ logic/NagUtils.h logic/NagUtils.cpp logic/SkinUtils.h logic/SkinUtils.cpp +logic/JavaCheckerJob.h +logic/JavaCheckerJob.cpp +# Assets +logic/assets/AssetsUtils.h +logic/assets/AssetsUtils.cpp ) @@ -410,6 +421,7 @@ gui/dialogs/EditNotesDialog.ui gui/dialogs/AccountListDialog.ui gui/dialogs/AccountSelectDialog.ui gui/dialogs/EditAccountDialog.ui +gui/dialogs/UpdateDialog.ui # Widgets/other gui/widgets/MCModInfoFrame.ui @@ -580,22 +592,11 @@ FILE(WRITE \"\${CMAKE_INSTALL_PREFIX}/${QTCONF_DEST_DIR}/qt.conf\" \"\") COMPONENT Runtime ) - -INSTALL( - CODE " -FILE(GLOB_RECURSE QTPLUGINS \"\${CMAKE_INSTALL_PREFIX}/${PLUGIN_DEST_DIR}/*${CMAKE_SHARED_LIBRARY_SUFFIX}\") -function(gp_resolved_file_type_override resolved_file type_var) - if(resolved_file MATCHES \"^/usr/lib/libQt\") - message(\"resolving \${resolved_file} as other\") - set(\${type_var} other PARENT_SCOPE) - endif() -endfunction() - -include(BundleUtilities) -fixup_bundle(\"${APPS}\" \"\${QTPLUGINS}\" \"${DIRS}\") -" - COMPONENT Runtime -) +CONFIGURE_FILE( + "${CMAKE_CURRENT_SOURCE_DIR}/install_prereqs.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" + @ONLY) +INSTALL(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/install_prereqs.cmake" COMPONENT Runtime) diff --git a/MultiMC.cpp b/MultiMC.cpp index 1792f587..bf0d9d99 100644 --- a/MultiMC.cpp +++ b/MultiMC.cpp @@ -2,14 +2,16 @@ #include "MultiMC.h" #include <iostream> #include <QDir> +#include <QFileInfo> #include <QNetworkAccessManager> #include <QTranslator> #include <QLibraryInfo> #include <QMessageBox> +#include <QStringList> #include "gui/dialogs/VersionSelectDialog.h" #include "logic/lists/InstanceList.h" -#include "logic/lists/MojangAccountList.h" +#include "logic/auth/MojangAccountList.h" #include "logic/lists/IconList.h" #include "logic/lists/LwjglVersionList.h" #include "logic/lists/MinecraftVersionList.h" @@ -20,6 +22,8 @@ #include "logic/JavaUtils.h" +#include "logic/updater/UpdateChecker.h" + #include "pathutils.h" #include "cmdutils.h" #include <inisettingsobject.h> @@ -31,7 +35,7 @@ using namespace Util::Commandline; MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(argc, argv), - m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_BUILD_TYPE} + m_version{VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_CHANNEL, VERSION_BUILD_TYPE} { setOrganizationName("MultiMC"); setApplicationName("MultiMC5"); @@ -73,11 +77,13 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(arg parser.addShortOpt("quietupdate", 'U'); parser.addDocumentation("quietupdate", "doesn't restart MultiMC after installing updates"); + // WARNING: disabled until further notice + /* // --launch parser.addOption("launch"); parser.addShortOpt("launch", 'l'); parser.addDocumentation("launch", "tries to launch the given instance", "<inst>"); - +*/ // parse the arguments try { @@ -139,6 +145,9 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(arg // load settings initGlobalSettings(); + // initialize the updater + m_updateChecker.reset(new UpdateChecker()); + // and instances auto InstDirSetting = m_settings->getSetting("InstanceDir"); m_instances.reset(new InstanceList(InstDirSetting->get().toString(), this)); @@ -206,6 +215,8 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(arg m_qnam.reset(new QNetworkAccessManager(this)); // launch instance, if that's what should be done + // WARNING: disabled until further notice + /* if (!args["launch"].isNull()) { if (InstanceLauncher(args["launch"].toString()).launch()) @@ -214,7 +225,7 @@ MultiMC::MultiMC(int &argc, char **argv, const QString &root) : QApplication(arg m_status = MultiMC::Failed; return; } - +*/ m_status = MultiMC::Initialized; } @@ -352,7 +363,8 @@ void MultiMC::initGlobalSettings() void MultiMC::initHttpMetaCache() { m_metacache.reset(new HttpMetaCache("metacache")); - m_metacache->addBase("assets", QDir("assets").absolutePath()); + m_metacache->addBase("asset_indexes", QDir("assets/indexes").absolutePath()); + m_metacache->addBase("asset_objects", QDir("assets/objects").absolutePath()); m_metacache->addBase("versions", QDir("versions").absolutePath()); m_metacache->addBase("libraries", QDir("libraries").absolutePath()); m_metacache->addBase("minecraftforge", QDir("mods/minecraftforge").absolutePath()); @@ -405,4 +417,64 @@ std::shared_ptr<JavaVersionList> MultiMC::javalist() return m_javalist; } +#ifdef WINDOWS +#define UPDATER_BIN "updater.exe" +#elif LINUX +#define UPDATER_BIN "updater" +#elif OSX +#define UPDATER_BIN "updater" +#else +#error Unsupported operating system. +#endif + +void MultiMC::installUpdates(const QString& updateFilesDir, bool restartOnFinish) +{ + QLOG_INFO() << "Installing updates."; +#if LINUX + // On Linux, the MultiMC executable file is actually in the bin folder inside the installation directory. + // This means that MultiMC's *actual* install path is the parent folder. + // We need to tell the updater to run with this directory as the install path, rather than the bin folder where the executable is. + // On other operating systems, we'll just use the path to the executable. + QString appDir = QFileInfo(MMC->applicationDirPath()).dir().path(); + + // On Linux, we also need to set the finish command to the launch script, rather than the binary. + QString finishCmd = PathCombine(appDir, "MultiMC"); +#else + QString appDir = MMC->applicationDirPath(); + QString finishCmd = MMC->applicationFilePath(); +#endif + + // Build the command we'll use to run the updater. + // Note, the above comment about the app dir path on Linux is irrelevant here because the updater binary is always in the + // same folder as the main binary. + QString updaterBinary = PathCombine(MMC->applicationDirPath(), UPDATER_BIN); + QStringList args; + // ./updater --install-dir $INSTALL_DIR --package-dir $UPDATEFILES_DIR --script $UPDATEFILES_DIR/file_list.xml --wait $PID --mode main + args << "--install-dir" << appDir; + args << "--package-dir" << updateFilesDir; + args << "--script" << PathCombine(updateFilesDir, "file_list.xml"); + args << "--wait" << QString::number(MMC->applicationPid()); + + if (restartOnFinish) + args << "--finish-cmd" << finishCmd; + + QLOG_INFO() << "Running updater with command" << updaterBinary << args.join(" "); + + QProcess::startDetached(updaterBinary, args); + + // Now that we've started the updater, quit MultiMC. + MMC->quit(); +} + +void MultiMC::setUpdateOnExit(const QString& updateFilesDir) +{ + m_updateOnExitPath = updateFilesDir; +} + +QString MultiMC::getExitUpdatePath() const +{ + return m_updateOnExitPath; +} + + #include "MultiMC.moc" @@ -17,6 +17,7 @@ class IconList; class QNetworkAccessManager; class ForgeVersionList; class JavaVersionList; +class UpdateChecker; #if defined(MMC) #undef MMC @@ -84,6 +85,11 @@ public: return m_metacache; } + std::shared_ptr<UpdateChecker> updateChecker() + { + return m_updateChecker; + } + std::shared_ptr<LWJGLVersionList> lwjgllist(); std::shared_ptr<ForgeVersionList> forgelist(); @@ -92,6 +98,22 @@ public: std::shared_ptr<JavaVersionList> javalist(); + /*! + * Installs update from the given update files directory. + */ + void installUpdates(const QString& updateFilesDir, bool restartOnFinish=false); + + /*! + * Sets MultiMC to install updates from the given directory when it exits. + */ + void setUpdateOnExit(const QString& updateFilesDir); + + /*! + * Gets the path to install updates from on exit. + * If this is an empty string, no updates should be installed on exit. + */ + QString getExitUpdatePath() const; + private: void initLogger(); @@ -106,6 +128,7 @@ private: std::shared_ptr<QTranslator> m_mmc_translator; std::shared_ptr<SettingsObject> m_settings; std::shared_ptr<InstanceList> m_instances; + std::shared_ptr<UpdateChecker> m_updateChecker; std::shared_ptr<MojangAccountList> m_accounts; std::shared_ptr<IconList> m_icons; std::shared_ptr<QNetworkAccessManager> m_qnam; @@ -117,6 +140,8 @@ private: QsLogging::DestinationPtr m_fileDestination; QsLogging::DestinationPtr m_debugDestination; + QString m_updateOnExitPath; + Status m_status = MultiMC::Failed; MultiMCVersion m_version; }; diff --git a/MultiMCVersion.h b/MultiMCVersion.h index 863976b2..8978516b 100644 --- a/MultiMCVersion.h +++ b/MultiMCVersion.h @@ -32,8 +32,9 @@ struct MultiMCVersion QString::number(major), QString::number(minor)); - if (build > 0) vstr += QString(".%1").arg(QString::number(build)); - if (!buildType.isEmpty()) vstr += QString("-%1").arg(buildType); + if (build >= 0) vstr += "." + QString::number(build); + if (!channel.isEmpty()) vstr += "-" + channel; + if (!buildType.isEmpty()) vstr += "-" + buildType; return vstr; } @@ -61,9 +62,13 @@ struct MultiMCVersion int build; /*! + * \brief This build's channel. + */ + QString channel; + + /*! * \brief The build type. - * This indicates the type of build that this is. For example, lin64-stable. - * Usually corresponds to this build's buildbot builder name. + * This indicates the type of build that this is. For example, lin64 or custombuild. */ QString buildType; }; diff --git a/config.h.in b/config.h.in index 34841817..b58dc322 100644 --- a/config.h.in +++ b/config.h.in @@ -1,16 +1,18 @@ +// Minor and major version, used to communicate changes to users. #define VERSION_MAJOR @MultiMC_VERSION_MAJOR@ #define VERSION_MINOR @MultiMC_VERSION_MINOR@ + +// Build number, channel, and type -- number and channel are used by the updater, type is purely visual #define VERSION_BUILD @MultiMC_VERSION_BUILD@ +#define VERSION_CHANNEL "@MultiMC_VERSION_CHANNEL@" #define VERSION_BUILD_TYPE "@MultiMC_VERSION_BUILD_TYPE@" -#define GIT_COMMIT "@MultiMC_GIT_COMMIT@" - -#define VERSION_STR "@MultiMC_VERSION_STRING@" - -#define x86 1 -#define x64 2 +// URL for the updater's channel +#define CHANLIST_URL "@MultiMC_CHANLIST_URL@" -#define ARCH @MultiMC_ARCH@ +// The commit hash of this build +#define GIT_COMMIT "@MultiMC_GIT_COMMIT@" -#define USE_HTTPS @MultiMC_USE_HTTPS@ +// This is printed on start to standard output +#define VERSION_STR "@MultiMC_VERSION_STRING@" diff --git a/depends/javacheck/JavaCheck.java b/depends/javacheck/JavaCheck.java index 73688082..11420b86 100644 --- a/depends/javacheck/JavaCheck.java +++ b/depends/javacheck/JavaCheck.java @@ -2,13 +2,23 @@ import java.lang.Integer; public class JavaCheck { - private static final String key = "os.arch"; + private static final String[] keys = {"os.arch", "java.version"}; public static void main (String [] args) { - String property = System.getProperty(key); - System.out.println(key + "=" + property); - if (property != null) - System.exit(0); - System.exit(1); + int ret = 0; + for(String key : keys) + { + String property = System.getProperty(key); + if(property != null) + { + System.out.println(key + "=" + property); + } + else + { + ret = 1; + } + } + + System.exit(ret); } } diff --git a/gui/ConsoleWindow.cpp b/gui/ConsoleWindow.cpp index d0210df6..24afbc0a 100644 --- a/gui/ConsoleWindow.cpp +++ b/gui/ConsoleWindow.cpp @@ -22,6 +22,9 @@ #include <gui/Platform.h> #include <gui/dialogs/CustomMessageBox.h> +#include <gui/dialogs/ProgressDialog.h> + +#include "logic/net/PasteUpload.h" ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent) : QMainWindow(parent), ui(new Ui::ConsoleWindow), proc(mcproc) @@ -44,6 +47,7 @@ ConsoleWindow::ConsoleWindow(MinecraftProcess *mcproc, QWidget *parent) { show(); } + setMayClose(false); } ConsoleWindow::~ConsoleWindow() @@ -161,6 +165,8 @@ void ConsoleWindow::onEnded(BaseInstance *instance, int code, QProcess::ExitStat { ui->btnKillMinecraft->setEnabled(false); + setMayClose(true); + if (instance->settings().get("AutoCloseConsole").toBool()) { if (code == 0 && status != QProcess::CrashExit) @@ -176,6 +182,21 @@ void ConsoleWindow::onEnded(BaseInstance *instance, int code, QProcess::ExitStat void ConsoleWindow::onLaunchFailed(BaseInstance *instance) { ui->btnKillMinecraft->setEnabled(false); + + setMayClose(true); + if(!isVisible()) show(); } + +void ConsoleWindow::on_btnPaste_clicked() +{ + auto text = ui->text->toPlainText(); + ProgressDialog dialog(this); + PasteUpload* paste=new PasteUpload(this, text); + dialog.exec(paste); + if(!paste->successful()) + { + CustomMessageBox::selectable(this, "Upload failed", paste->failReason(), QMessageBox::Critical)->exec(); + } +} diff --git a/gui/ConsoleWindow.h b/gui/ConsoleWindow.h index 2d948484..731c616c 100644 --- a/gui/ConsoleWindow.h +++ b/gui/ConsoleWindow.h @@ -76,6 +76,8 @@ slots: // FIXME: add handlers for the other MinecraftProcess signals (pre/post launch command // failures) + void on_btnPaste_clicked(); + protected: void closeEvent(QCloseEvent *); diff --git a/gui/ConsoleWindow.ui b/gui/ConsoleWindow.ui index ed1b627b..62cc89ac 100644 --- a/gui/ConsoleWindow.ui +++ b/gui/ConsoleWindow.ui @@ -48,6 +48,13 @@ <number>6</number> </property> <item> + <widget class="QPushButton" name="btnPaste"> + <property name="text"> + <string>Upload Log</string> + </property> + </widget> + </item> + <item> <spacer name="horizontalSpacer"> <property name="orientation"> <enum>Qt::Horizontal</enum> diff --git a/gui/MainWindow.cpp b/gui/MainWindow.cpp index 854091c6..f6ef6bad 100644 --- a/gui/MainWindow.cpp +++ b/gui/MainWindow.cpp @@ -59,6 +59,7 @@ #include "gui/dialogs/CopyInstanceDialog.h" #include "gui/dialogs/AccountListDialog.h" #include "gui/dialogs/AccountSelectDialog.h" +#include "gui/dialogs/UpdateDialog.h" #include "gui/dialogs/EditAccountDialog.h" #include "gui/ConsoleWindow.h" @@ -73,10 +74,13 @@ #include "logic/auth/flows/RefreshTask.h" #include "logic/auth/flows/ValidateTask.h" +#include "logic/updater/DownloadUpdateTask.h" + +#include "logic/net/URLConstants.h" + #include "logic/BaseInstance.h" #include "logic/InstanceFactory.h" #include "logic/MinecraftProcess.h" -#include "logic/OneSixAssets.h" #include "logic/OneSixUpdate.h" #include "logic/JavaUtils.h" #include "logic/NagUtils.h" @@ -84,6 +88,9 @@ #include "logic/LegacyInstance.h" +#include "logic/assets/AssetsUtils.h" +#include <logic/updater/UpdateChecker.h> + MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { MultiMCPlatform::fixWM_CLASS(this); @@ -164,7 +171,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi connect(MMC->instances().get(), SIGNAL(dataIsInvalid()), SLOT(selectionBad())); m_statusLeft = new QLabel(tr("Instance type"), this); - m_statusRight = new QLabel(tr("Assets information"), this); + m_statusRight = new QLabel(this); m_statusRight->setAlignment(Qt::AlignRight); statusBar()->addPermanentWidget(m_statusLeft, 1); statusBar()->addPermanentWidget(m_statusRight, 0); @@ -210,9 +217,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi for(AccountProfile profile : account->profiles()) { - auto meta = MMC->metacache()->resolveEntry("skins", profile.name() + ".png"); + auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png"); auto action = CacheDownload::make( - QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name() + ".png"), + QUrl("http://" + URLConstants::SKINS_BASE + profile.name + ".png"), meta); job->addNetAction(action); meta->stale = true; @@ -236,14 +243,12 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi MMC->lwjgllist()->loadList(); } - assets_downloader = new OneSixAssets(); - connect(assets_downloader, SIGNAL(indexStarted()), SLOT(assetsIndexStarted())); - connect(assets_downloader, SIGNAL(filesStarted()), SLOT(assetsFilesStarted())); - connect(assets_downloader, SIGNAL(filesProgress(int, int, int)), - SLOT(assetsFilesProgress(int, int, int))); - connect(assets_downloader, SIGNAL(failed()), SLOT(assetsFailed())); - connect(assets_downloader, SIGNAL(finished()), SLOT(assetsFinished())); - assets_downloader->start(); + // set up the updater object. + auto updater = MMC->updateChecker(); + QObject::connect(updater.get(), &UpdateChecker::updateAvailable, this, &MainWindow::updateAvailable); + // if automatic update checks are allowed, start one. + if(MMC->settings()->get("AutoUpdate").toBool()) + on_actionCheckUpdate_triggered(); } const QString currentInstanceId = MMC->settings()->get("SelectedInstance").toString(); @@ -267,6 +272,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi // removing this looks stupid view->setFocus(); + + AssetsUtils::migrateOldAssets(); } MainWindow::~MainWindow() @@ -274,7 +281,6 @@ MainWindow::~MainWindow() delete ui; delete proxymodel; delete drawer; - delete assets_downloader; } void MainWindow::repopulateAccountsMenu() @@ -310,9 +316,9 @@ void MainWindow::repopulateAccountsMenu() section->setEnabled(false); accountMenu->addAction(section); - for (AccountProfile profile : account->profiles()) + for (auto profile : account->profiles()) { - QAction *action = new QAction(profile.name(), this); + QAction *action = new QAction(profile.name, this); action->setData(account->username()); action->setCheckable(true); if(active_username == account->username()) @@ -320,7 +326,7 @@ void MainWindow::repopulateAccountsMenu() action->setChecked(true); } - action->setIcon(SkinUtils::getFaceFromCache(profile.name())); + action->setIcon(SkinUtils::getFaceFromCache(profile.name)); accountMenu->addAction(action); connect(action, SIGNAL(triggered(bool)), SLOT(changeActiveAccount())); } @@ -378,7 +384,7 @@ void MainWindow::activeAccountChanged() const AccountProfile *profile = account->currentProfile(); if (profile != nullptr) { - accountMenuButton->setIcon(SkinUtils::getFaceFromCache(profile->name())); + accountMenuButton->setIcon(SkinUtils::getFaceFromCache(profile->name)); return; } } @@ -417,6 +423,41 @@ bool MainWindow::eventFilter(QObject *obj, QEvent *ev) return QMainWindow::eventFilter(obj, ev); } +void MainWindow::updateAvailable(QString repo, QString versionName, int versionId) +{ + UpdateDialog dlg; + UpdateAction action = (UpdateAction) dlg.exec(); + switch(action) + { + case UPDATE_LATER: + QLOG_INFO() << "Update will be installed later."; + break; + case UPDATE_NOW: + downloadUpdates(repo, versionId); + break; + case UPDATE_ONEXIT: + downloadUpdates(repo, versionId, true); + break; + } +} + +void MainWindow::downloadUpdates(QString repo, int versionId, bool installOnExit) +{ + QLOG_INFO() << "Downloading updates."; + // TODO: If the user chooses to update on exit, we should download updates in the background. + // Doing so is a bit complicated, because we'd have to make sure it finished downloading before actually exiting MultiMC. + ProgressDialog updateDlg(this); + DownloadUpdateTask updateTask(repo, versionId, &updateDlg); + // If the task succeeds, install the updates. + if (updateDlg.exec(&updateTask)) + { + if (installOnExit) + MMC->setUpdateOnExit(updateTask.updateFilesDir()); + else + MMC->installUpdates(updateTask.updateFilesDir()); + } +} + void MainWindow::onCatToggled(bool state) { setCatBackground(state); @@ -474,7 +515,7 @@ void MainWindow::on_actionAddInstance_triggered() newInstance->setName(newInstDlg.instName()); newInstance->setIconKey(newInstDlg.iconKey()); MMC->instances()->add(InstancePtr(newInstance)); - return; + break; case InstanceFactory::InstExists: { @@ -497,6 +538,19 @@ void MainWindow::on_actionAddInstance_triggered() break; } } + + std::shared_ptr<MojangAccountList> accounts = MMC->accounts(); + MojangAccountPtr account = accounts->activeAccount(); + if(account.get() != nullptr && account->accountStatus() != NotVerified) + { + ProgressDialog loadDialog(this); + auto update = newInstance->doUpdate(false); + connect(update.get(), &Task::failed , [this](QString reason) { + QString error = QString("Instance load failed: %1").arg(reason); + CustomMessageBox::selectable(this, tr("Error"), error, QMessageBox::Warning)->show(); + }); + loadDialog.exec(update.get()); + } } void MainWindow::on_actionCopyInstance_triggered() @@ -604,6 +658,8 @@ void MainWindow::on_actionConfig_Folder_triggered() void MainWindow::on_actionCheckUpdate_triggered() { + auto updater = MMC->updateChecker(); + updater->checkForUpdate(); } void MainWindow::on_actionSettings_triggered() @@ -781,79 +837,56 @@ void MainWindow::doLaunch() accounts->setActiveAccount(account->username()); } - if (account.get() != nullptr) - { - doLaunchInst(m_selectedInstance, account); - } -} - -void MainWindow::doLaunchInst(BaseInstance* instance, MojangAccountPtr account) -{ - // We'll need to validate the access token to make sure the account is still logged in. - ProgressDialog progDialog(this); - RefreshTask refreshtask(account, &progDialog); - progDialog.exec(&refreshtask); + // if no account is selected, we bail + if (!account.get()) + return; - if (refreshtask.successful()) - { - prepareLaunch(m_selectedInstance, account); - } - else + // do the login. if the account has an access token, try to refresh it first. + if(account->accountStatus() != NotVerified) { - YggdrasilTask::Error *error = refreshtask.getError(); + // We'll need to validate the access token to make sure the account is still logged in. + ProgressDialog progDialog(this); + progDialog.setSkipButton(true, tr("Play Offline")); + auto task = account->login(); + progDialog.exec(task.get()); - if (error != nullptr) - { - if (error->getErrorMessage().contains("invalid token", Qt::CaseInsensitive)) - { - // TODO: Allow the user to enter their password and "refresh" their access token. - if (doRefreshToken(account, tr("Your account's access token is invalid. Please enter your password to log in again."))) - doLaunchInst(instance, account); - } - else - { - CustomMessageBox::selectable( - this, tr("Access Token Validation Error"), - tr("There was an error when trying to validate your access token.\n" - "Details: %s").arg(error->getDisplayMessage()), - QMessageBox::Warning, QMessageBox::Ok)->exec(); - } - } - else + auto status = account->accountStatus(); + if(status != NotVerified) { - CustomMessageBox::selectable( - this, tr("Access Token Validation Error"), - tr("There was an unknown error when trying to validate your access token." - "The authentication server might be down, or you might not be connected to " - "the Internet."), - QMessageBox::Warning, QMessageBox::Ok)->exec(); + updateInstance(m_selectedInstance, account); } + // revert from online to verified. + account->downgrade(); + return; } + if (loginWithPassword(account, tr("Your account is currently not logged in. Please enter your password to log in again."))) + updateInstance(m_selectedInstance, account); } -bool MainWindow::doRefreshToken(MojangAccountPtr account, const QString& errorMsg) +bool MainWindow::loginWithPassword(MojangAccountPtr account, const QString& errorMsg) { EditAccountDialog passDialog(errorMsg, this, EditAccountDialog::PasswordField); if (passDialog.exec() == QDialog::Accepted) { // To refresh the token, we just create an authenticate task with the given account and the user's password. ProgressDialog progDialog(this); - AuthenticateTask authTask(account, passDialog.password(), &progDialog); - progDialog.exec(&authTask); - if (authTask.successful()) + auto task = account->login(passDialog.password()); + progDialog.exec(task.get()); + if(task->successful()) return true; else { // If the authentication task failed, recurse with the task's error message. - return doRefreshToken(account, authTask.failReason()); + return loginWithPassword(account, task->failReason()); } } - else return false; + return false; } -void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) +void MainWindow::updateInstance(BaseInstance* instance, MojangAccountPtr account) { - Task *updateTask = instance->doUpdate(true); + bool only_prepare = account->accountStatus() != Online; + auto updateTask = instance->doUpdate(only_prepare); if (!updateTask) { launchInstance(instance, account); @@ -861,10 +894,9 @@ void MainWindow::prepareLaunch(BaseInstance* instance, MojangAccountPtr account) else { ProgressDialog tDialog(this); - connect(updateTask, &Task::succeeded, [this, instance, account] { launchInstance(instance, account); }); - connect(updateTask, SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); - tDialog.exec(updateTask); - delete updateTask; + connect(updateTask.get(), &Task::succeeded, [this, instance, account] { launchInstance(instance, account); }); + connect(updateTask.get(), SIGNAL(failed(QString)), SLOT(onGameUpdateError(QString))); + tDialog.exec(updateTask.get()); } } diff --git a/gui/MainWindow.h b/gui/MainWindow.h index 59cfa3c9..befe93e6 100644 --- a/gui/MainWindow.h +++ b/gui/MainWindow.h @@ -31,7 +31,6 @@ class KCategorizedView; class KCategoryDrawer; class MinecraftProcess; class ConsoleWindow; -class OneSixAssets; namespace Ui { @@ -110,18 +109,13 @@ slots: * If no default account is selected, prompts the user to pick an account. */ void doLaunch(); - - /*! - * Launches the given instance with the given account. - */ - void doLaunchInst(BaseInstance* instance, MojangAccountPtr account); /*! * Opens an input dialog, allowing the user to input their password and refresh its access token. * This function will execute the proper Yggdrasil task to refresh the access token. * Returns true if successful. False if the user cancelled. */ - bool doRefreshToken(MojangAccountPtr account, const QString& errorMsg=""); + bool loginWithPassword(MojangAccountPtr account, const QString& errorMsg=""); /*! * Launches the given instance with the given account. @@ -132,7 +126,7 @@ slots: /*! * Prepares the given instance for launch with the given account. */ - void prepareLaunch(BaseInstance* instance, MojangAccountPtr account); + void updateInstance(BaseInstance* instance, MojangAccountPtr account); void onGameUpdateError(QString error); @@ -161,11 +155,18 @@ slots: void startTask(Task *task); + void updateAvailable(QString repo, QString versionName, int versionId); + void activeAccountChanged(); void changeActiveAccount(); void repopulateAccountsMenu(); + + /*! + * Runs the DownloadUpdateTask and installs updates. + */ + void downloadUpdates(QString repo, int versionId, bool installOnExit=false); protected: bool eventFilter(QObject *obj, QEvent *ev); @@ -178,7 +179,6 @@ private: InstanceProxyModel *proxymodel; MinecraftProcess *proc; ConsoleWindow *console; - OneSixAssets *assets_downloader; LabeledToolButton *renameButton; QToolButton *changeIconButton; diff --git a/gui/MainWindow.ui b/gui/MainWindow.ui index 0537d0e5..82e3b05f 100644 --- a/gui/MainWindow.ui +++ b/gui/MainWindow.ui @@ -14,7 +14,7 @@ <string>MultiMC 5</string> </property> <property name="windowIcon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/multimc/scalable/apps/multimc.svg</normaloff>:/icons/multimc/scalable/apps/multimc.svg</iconset> </property> <widget class="QWidget" name="centralWidget"> @@ -123,7 +123,7 @@ </widget> <action name="actionAddInstance"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/new</normaloff>:/icons/toolbar/new</iconset> </property> <property name="text"> @@ -138,7 +138,7 @@ </action> <action name="actionViewInstanceFolder"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/viewfolder</normaloff>:/icons/toolbar/viewfolder</iconset> </property> <property name="text"> @@ -153,7 +153,7 @@ </action> <action name="actionRefresh"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/refresh</normaloff>:/icons/toolbar/refresh</iconset> </property> <property name="text"> @@ -168,7 +168,7 @@ </action> <action name="actionViewCentralModsFolder"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/centralmods</normaloff>:/icons/toolbar/centralmods</iconset> </property> <property name="text"> @@ -183,7 +183,7 @@ </action> <action name="actionCheckUpdate"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/checkupdate</normaloff>:/icons/toolbar/checkupdate</iconset> </property> <property name="text"> @@ -198,7 +198,7 @@ </action> <action name="actionSettings"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/settings</normaloff>:/icons/toolbar/settings</iconset> </property> <property name="text"> @@ -216,7 +216,7 @@ </action> <action name="actionReportBug"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/bug</normaloff>:/icons/toolbar/bug</iconset> </property> <property name="text"> @@ -231,7 +231,7 @@ </action> <action name="actionNews"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/news</normaloff>:/icons/toolbar/news</iconset> </property> <property name="text"> @@ -246,7 +246,7 @@ </action> <action name="actionAbout"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/about</normaloff>:/icons/toolbar/about</iconset> </property> <property name="text"> @@ -300,7 +300,7 @@ <bool>true</bool> </property> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/instances/infinity</normaloff>:/icons/instances/infinity</iconset> </property> <property name="text"> @@ -440,7 +440,7 @@ <bool>true</bool> </property> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/cat</normaloff>:/icons/toolbar/cat</iconset> </property> <property name="text"> @@ -452,7 +452,7 @@ </action> <action name="actionCopyInstance"> <property name="icon"> - <iconset resource="../multimc.qrc"> + <iconset resource="../graphics.qrc"> <normaloff>:/icons/toolbar/copy</normaloff>:/icons/toolbar/copy</iconset> </property> <property name="text"> @@ -476,7 +476,7 @@ </widget> <layoutdefault spacing="6" margin="11"/> <resources> - <include location="../multimc.qrc"/> + <include location="../graphics.qrc"/> </resources> <connections/> </ui> diff --git a/gui/dialogs/AboutDialog.ui b/gui/dialogs/AboutDialog.ui index f674eb61..7b91ebc8 100644 --- a/gui/dialogs/AboutDialog.ui +++ b/gui/dialogs/AboutDialog.ui @@ -6,7 +6,7 @@ <rect> <x>0</x> <y>0</y> - <width>637</width> + <width>706</width> <height>579</height> </rect> </property> @@ -17,7 +17,7 @@ </size> </property> <property name="windowTitle"> - <string>Dialog</string> + <string>About MultiMC</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> @@ -95,13 +95,16 @@ </item> <item> <widget class="QToolBox" name="toolBox"> + <property name="currentIndex"> + <number>0</number> + </property> <widget class="QWidget" name="aboutPage"> <property name="geometry"> <rect> <x>0</x> <y>0</y> - <width>619</width> - <height>329</height> + <width>688</width> + <height>313</height> </rect> </property> <attribute name="label"> @@ -159,8 +162,8 @@ <rect> <x>0</x> <y>0</y> - <width>619</width> - <height>329</height> + <width>688</width> + <height>313</height> </rect> </property> <attribute name="label"> @@ -176,7 +179,7 @@ <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Andrew Okin &lt;</span><a href="mailto:forkk@forkk.net"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">forkk@forkk.net</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Petr Mrázek &lt;</span><a href="mailto:peterix@gmail.com"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">peterix@gmail.com</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu';">Orochimarufan &lt;</span><a href="mailto:orochimarufan.x3@gmail.com"><span style=" font-family:'Ubuntu'; text-decoration: underline; color:#0000ff;">orochimarufan.x3@gmail.com</span></a><span style=" font-family:'Ubuntu';">&gt;</span></p> @@ -203,8 +206,8 @@ p, li { white-space: pre-wrap; } <rect> <x>0</x> <y>0</y> - <width>619</width> - <height>329</height> + <width>688</width> + <height>313</height> </rect> </property> <attribute name="label"> @@ -219,6 +222,11 @@ p, li { white-space: pre-wrap; } <height>0</height> </size> </property> + <property name="font"> + <font> + <family>DejaVu Sans Mono</family> + </font> + </property> <property name="readOnly"> <bool>true</bool> </property> @@ -226,139 +234,128 @@ p, li { white-space: pre-wrap; } <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Bitstream Vera Sans'; font-size:11pt; font-weight:400; font-style:normal;"> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">MultiMC</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">Copyright 2012 MultiMC Contributors</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">you may not use this file except in compliance with the License.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">You may obtain a copy of the License at</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;"> http://www.apache.org/licenses/LICENSE-2.0</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">Unless required by applicable law or agreed to in writing, software</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">See the License for the specific language governing permissions and</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Ubuntu'; font-size:10pt;">limitations under the License.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Ubuntu'; font-size:10pt;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">QSLog</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Copyright (c) 2010, Razvan Petru</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">All rights reserved.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Redistribution and use in source and binary forms, with or without modification,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">are permitted provided that the following conditions are met:</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">* Redistributions of source code must retain the above copyright notice, this</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> list of conditions and the following disclaimer.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">* Redistributions in binary form must reproduce the above copyright notice, this</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> list of conditions and the following disclaimer in the documentation and/or other</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> materials provided with the distribution.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">* The name of the contributors may not be used to endorse or promote products</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> derived from this software without specific prior written permission.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">OF THE POSSIBILITY OF SUCH DAMAGE.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">Group View (instance view)</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">/**</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * Copyright (C) 2007 Rafael Fernández López &lt;ereslibre@kde.org&gt;</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * Copyright (C) 2007 John Tapsell &lt;tapsell@kde.org&gt;</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> *</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * This library is free software; you can redistribute it and/or</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * modify it under the terms of the GNU Library General Public</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * License as published by the Free Software Foundation; either</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * version 2 of the License, or (at your option) any later version.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> *</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * This library is distributed in the hope that it will be useful,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * but WITHOUT ANY WARRANTY; without even the implied warranty of</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * Library General Public License for more details.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> *</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * You should have received a copy of the GNU Library General Public License</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * along with this library; see the file COPYING.LIB. If not, write to</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * Boston, MA 02110-1301, USA.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> */</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> +</style></head><body style=" font-family:'DejaVu Sans Mono'; font-size:11pt; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">MultiMC</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Copyright 2012 MultiMC Contributors</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">you may not use this file except in compliance with the License.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">You may obtain a copy of the License at</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">Pack200</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> http://www.apache.org/licenses/LICENSE-2.0</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">The GNU General Public License (GPL)</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Unless required by applicable law or agreed to in writing, software</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">distributed under the License is distributed on an &quot;AS IS&quot; BASIS,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">See the License for the specific language governing permissions and</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">limitations under the License.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Version 2, June 1991</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">+ &quot;CLASSPATH&quot; EXCEPTION TO THE GPL</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Certain source files distributed by Oracle America and/or its affiliates are</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">subject to the following clarification and special exception to the GPL, but</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">only where Oracle has expressly included in the particular source file's header</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">the words &quot;Oracle designates this particular file as subject to the &quot;Classpath&quot;</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">exception as provided by Oracle in the LICENSE file that accompanied this code.&quot;</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> Linking this library statically or dynamically with other modules is making</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> a combined work based on this library. Thus, the terms and conditions of</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> the GNU General Public License cover the whole combination.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> As a special exception, the copyright holders of this library give you</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> permission to link this library with independent modules to produce an</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> executable, regardless of the license terms of these independent modules,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> and to copy and distribute the resulting executable under terms of your</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> choice, provided that you also meet, for each linked independent module,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> the terms and conditions of the license of that module. An independent</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> module is a module which is not derived from or based on this library. If</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> you modify this library, you may extend this exception to your version of</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> the library, but you are not obligated to do so. If you do not wish to do</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> so, delete this exception statement from your version.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">Quazip</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Copyright (C) 2005-2011 Sergey A. Tachenov</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">This program is free software; you can redistribute it and/or modify it</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">under the terms of the GNU Lesser General Public License as published by</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">the Free Software Foundation; either version 2 of the License, or (at</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">your option) any later version.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">This program is distributed in the hope that it will be useful, but</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">WITHOUT ANY WARRANTY; without even the implied warranty of</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">General Public License for more details.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">You should have received a copy of the GNU Lesser General Public License</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">along with this program; if not, write to the Free Software Foundation,</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">See COPYING file for the full LGPL text.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">Original ZIP package is copyrighted by Gilles Vollant, see</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">quazip/(un)zip.h files for details, basically it's zlib license.</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:18pt; font-weight:600;">xz-minidec</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;">/*</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * XZ decompressor</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> *</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * Authors: Lasse Collin &lt;lasse.collin@tukaani.org&gt;</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * Igor Pavlov &lt;http://7-zip.org/&gt;</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> *</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * This file has been put into the public domain.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> * You can do whatever you want with this file.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt;"> */</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:10pt;"><br /></p></body></html></string> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">QSLog</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Copyright (c) 2010, Razvan Petru</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">All rights reserved.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Redistribution and use in source and binary forms, with or without modification,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">are permitted provided that the following conditions are met:</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">* Redistributions of source code must retain the above copyright notice, this</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> list of conditions and the following disclaimer.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">* Redistributions in binary form must reproduce the above copyright notice, this</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> list of conditions and the following disclaimer in the documentation and/or other</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> materials provided with the distribution.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">* The name of the contributors may not be used to endorse or promote products</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> derived from this software without specific prior written permission.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">OF THE POSSIBILITY OF SUCH DAMAGE.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">Group View (instance view)</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> /*</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Copyright (C) 2007 Rafael Fernández López &lt;ereslibre@kde.org&gt;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Copyright (C) 2007 John Tapsell &lt;tapsell@kde.org&gt;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> *</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * This library is free software; you can redistribute it and/or</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * modify it under the terms of the GNU Library General Public</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * License as published by the Free Software Foundation; either</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * version 2 of the License, or (at your option) any later version.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> *</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * This library is distributed in the hope that it will be useful,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * but WITHOUT ANY WARRANTY; without even the implied warranty of</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Library General Public License for more details.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> *</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * You should have received a copy of the GNU Library General Public License</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * along with this library; see the file COPYING.LIB. If not, write to</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Boston, MA 02110-1301, USA.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> */</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">Pack200</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">The GNU General Public License (GPL)</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Version 2, June 1991</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">+ &quot;CLASSPATH&quot; EXCEPTION TO THE GPL</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Certain source files distributed by Oracle America and/or its affiliates are</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">subject to the following clarification and special exception to the GPL, but</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">only where Oracle has expressly included in the particular source file's header</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">the words &quot;Oracle designates this particular file as subject to the &quot;Classpath&quot;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">exception as provided by Oracle in the LICENSE file that accompanied this code.&quot;</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> Linking this library statically or dynamically with other modules is making</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> a combined work based on this library. Thus, the terms and conditions of</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> the GNU General Public License cover the whole combination.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> As a special exception, the copyright holders of this library give you</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> permission to link this library with independent modules to produce an</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> executable, regardless of the license terms of these independent modules,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> and to copy and distribute the resulting executable under terms of your</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> choice, provided that you also meet, for each linked independent module,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> the terms and conditions of the license of that module. An independent</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> module is a module which is not derived from or based on this library. If</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> you modify this library, you may extend this exception to your version of</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> the library, but you are not obligated to do so. If you do not wish to do</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> so, delete this exception statement from your version.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">Quazip</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Copyright (C) 2005-2011 Sergey A. Tachenov</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This program is free software; you can redistribute it and/or modify it</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">under the terms of the GNU Lesser General Public License as published by</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">the Free Software Foundation; either version 2 of the License, or (at</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">your option) any later version.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">This program is distributed in the hope that it will be useful, but</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">WITHOUT ANY WARRANTY; without even the implied warranty of</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">General Public License for more details.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">You should have received a copy of the GNU Lesser General Public License</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">along with this program; if not, write to the Free Software Foundation,</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">See COPYING file for the full LGPL text.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">Original ZIP package is copyrighted by Gilles Vollant, see</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">quazip/(un)zip.h files for details, basically it's zlib license.</span></p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:8pt;"><br /></p> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Bitstream Vera Sans'; font-size:18pt; font-weight:600;">xz-minidec</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;">/*</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * XZ decompressor</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> *</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Authors: Lasse Collin &lt;lasse.collin@tukaani.org&gt;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * Igor Pavlov &lt;http://7-zip.org/&gt;</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> *</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * This file has been put into the public domain.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> * You can do whatever you want with this file.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt;"> */</span></p></body></html></string> </property> </widget> </item> @@ -404,7 +401,6 @@ p, li { white-space: pre-wrap; } </widget> <resources> <include location="../../graphics.qrc"/> - <include location="../../graphics.qrc"/> </resources> <connections/> </ui> diff --git a/gui/dialogs/AccountListDialog.cpp b/gui/dialogs/AccountListDialog.cpp index f5268b61..91b2ac55 100644 --- a/gui/dialogs/AccountListDialog.cpp +++ b/gui/dialogs/AccountListDialog.cpp @@ -20,12 +20,13 @@ #include <logger/QsLog.h> -#include <logic/auth/flows/AuthenticateTask.h> #include <logic/net/NetJob.h> +#include <logic/net/URLConstants.h> #include <gui/dialogs/EditAccountDialog.h> #include <gui/dialogs/ProgressDialog.h> #include <gui/dialogs/AccountSelectDialog.h> +#include <logic/tasks/Task.h> #include <MultiMC.h> @@ -117,24 +118,24 @@ void AccountListDialog::addAccount(const QString& errMsg) QString username(loginDialog.username()); QString password(loginDialog.password()); - MojangAccountPtr account = MojangAccountPtr(new MojangAccount(username)); - + MojangAccountPtr account = MojangAccount::createFromUsername(username); ProgressDialog progDialog(this); - AuthenticateTask authTask(account, password, &progDialog); - if (progDialog.exec(&authTask)) + auto task = account->login(password); + progDialog.exec(task.get()); + if(task->successful()) { - // Add the authenticated account to the accounts list. - MojangAccountPtr account = authTask.getMojangAccount(); m_accounts->addAccount(account); + if (m_accounts->count() == 1) + m_accounts->setActiveAccount(account->username()); // Grab associated player skins auto job = new NetJob("Player skins: " + account->username()); for(AccountProfile profile : account->profiles()) { - auto meta = MMC->metacache()->resolveEntry("skins", profile.name() + ".png"); + auto meta = MMC->metacache()->resolveEntry("skins", profile.name + ".png"); auto action = CacheDownload::make( - QUrl("http://skins.minecraft.net/MinecraftSkins/" + profile.name() + ".png"), + QUrl("http://" + URLConstants::SKINS_BASE + profile.name + ".png"), meta); job->addNetAction(action); meta->stale = true; diff --git a/gui/dialogs/AccountListDialog.h b/gui/dialogs/AccountListDialog.h index 84ff8e0e..fe0c8773 100644 --- a/gui/dialogs/AccountListDialog.h +++ b/gui/dialogs/AccountListDialog.h @@ -19,7 +19,7 @@ #include <memory> -#include "logic/lists/MojangAccountList.h" +#include "logic/auth/MojangAccountList.h" namespace Ui { diff --git a/gui/dialogs/AccountListDialog.ui b/gui/dialogs/AccountListDialog.ui index 571f9be7..72682163 100644 --- a/gui/dialogs/AccountListDialog.ui +++ b/gui/dialogs/AccountListDialog.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Dialog</string> + <string>Manage Accounts</string> </property> <layout class="QVBoxLayout" name="verticalLayout_2"> <item> diff --git a/gui/dialogs/AccountSelectDialog.cpp b/gui/dialogs/AccountSelectDialog.cpp index b8fa9e42..ec2f09be 100644 --- a/gui/dialogs/AccountSelectDialog.cpp +++ b/gui/dialogs/AccountSelectDialog.cpp @@ -20,8 +20,6 @@ #include <logger/QsLog.h> -#include <logic/auth/flows/AuthenticateTask.h> - #include <gui/dialogs/ProgressDialog.h> #include <MultiMC.h> diff --git a/gui/dialogs/AccountSelectDialog.h b/gui/dialogs/AccountSelectDialog.h index a539e7e9..6007253d 100644 --- a/gui/dialogs/AccountSelectDialog.h +++ b/gui/dialogs/AccountSelectDialog.h @@ -19,7 +19,7 @@ #include <memory> -#include "logic/lists/MojangAccountList.h" +#include "logic/auth/MojangAccountList.h" namespace Ui { diff --git a/gui/dialogs/EditAccountDialog.ui b/gui/dialogs/EditAccountDialog.ui index 15d371ee..1a8f9dba 100644 --- a/gui/dialogs/EditAccountDialog.ui +++ b/gui/dialogs/EditAccountDialog.ui @@ -7,11 +7,11 @@ <x>0</x> <y>0</y> <width>400</width> - <height>128</height> + <height>148</height> </rect> </property> <property name="windowTitle"> - <string>Dialog</string> + <string>Edit Account</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> diff --git a/gui/dialogs/InstanceSettings.cpp b/gui/dialogs/InstanceSettings.cpp index bc6b266a..641c7fab 100644 --- a/gui/dialogs/InstanceSettings.cpp +++ b/gui/dialogs/InstanceSettings.cpp @@ -220,7 +220,8 @@ void InstanceSettings::on_javaTestBtn_clicked() checker.reset(new JavaChecker()); connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, SLOT(checkFinished(JavaCheckResult))); - checker->performCheck(ui->javaPathTextBox->text()); + checker->path = ui->javaPathTextBox->text(); + checker->performCheck(); } void InstanceSettings::checkFinished(JavaCheckResult result) diff --git a/gui/dialogs/InstanceSettings.ui b/gui/dialogs/InstanceSettings.ui index 04c32108..c4a7d6ed 100644 --- a/gui/dialogs/InstanceSettings.ui +++ b/gui/dialogs/InstanceSettings.ui @@ -7,11 +7,11 @@ <x>0</x> <y>0</y> <width>526</width> - <height>622</height> + <height>637</height> </rect> </property> <property name="windowTitle"> - <string/> + <string>Instance Settings</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> diff --git a/gui/dialogs/LegacyModEditDialog.ui b/gui/dialogs/LegacyModEditDialog.ui index 47db0079..0662c712 100644 --- a/gui/dialogs/LegacyModEditDialog.ui +++ b/gui/dialogs/LegacyModEditDialog.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Dialog</string> + <string>Edit Mods</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> diff --git a/gui/dialogs/LwjglSelectDialog.ui b/gui/dialogs/LwjglSelectDialog.ui index c715cc07..0287ec8f 100644 --- a/gui/dialogs/LwjglSelectDialog.ui +++ b/gui/dialogs/LwjglSelectDialog.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Dialog</string> + <string>Manage Lwjgl Versions</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> diff --git a/gui/dialogs/OneSixModEditDialog.ui b/gui/dialogs/OneSixModEditDialog.ui index 8f301438..48aa87ee 100644 --- a/gui/dialogs/OneSixModEditDialog.ui +++ b/gui/dialogs/OneSixModEditDialog.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Dialog</string> + <string>Manage Mods</string> </property> <layout class="QGridLayout" name="gridLayout"> <item row="0" column="0"> diff --git a/gui/dialogs/ProgressDialog.cpp b/gui/dialogs/ProgressDialog.cpp index ca433dab..ba14cca2 100644 --- a/gui/dialogs/ProgressDialog.cpp +++ b/gui/dialogs/ProgressDialog.cpp @@ -25,9 +25,23 @@ ProgressDialog::ProgressDialog(QWidget *parent) : QDialog(parent), ui(new Ui::Pr { MultiMCPlatform::fixWM_CLASS(this); ui->setupUi(this); + this->setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); + setSkipButton(false); + changeProgress(0, 100); +} + +void ProgressDialog::setSkipButton(bool present, QString label) +{ + ui->skipButton->setEnabled(present); + ui->skipButton->setVisible(present); + ui->skipButton->setText(label); updateSize(); +} - changeProgress(0, 100); +void ProgressDialog::on_skipButton_clicked(bool checked) +{ + Q_UNUSED(checked); + task->abort(); } ProgressDialog::~ProgressDialog() @@ -51,9 +65,13 @@ int ProgressDialog::exec(ProgressProvider *task) connect(task, SIGNAL(status(QString)), SLOT(changeStatus(const QString &))); connect(task, SIGNAL(progress(qint64, qint64)), SLOT(changeProgress(qint64, qint64))); - // this makes sure that the task is started after the dialog is created - QMetaObject::invokeMethod(task, "start", Qt::QueuedConnection); - return QDialog::exec(); + // if this didn't connect to an already running task, invoke start + if(!task->isRunning()) + task->start(); + if(task->isRunning()) + return QDialog::exec(); + else + return 0; } ProgressProvider *ProgressDialog::getTask() diff --git a/gui/dialogs/ProgressDialog.h b/gui/dialogs/ProgressDialog.h index 0029d3ec..fe63a826 100644 --- a/gui/dialogs/ProgressDialog.h +++ b/gui/dialogs/ProgressDialog.h @@ -35,6 +35,7 @@ public: void updateSize(); int exec(ProgressProvider *task); + void setSkipButton(bool present, QString label = QString()); ProgressProvider *getTask(); @@ -47,7 +48,10 @@ slots: void changeStatus(const QString &status); void changeProgress(qint64 current, qint64 total); -signals: + +private +slots: + void on_skipButton_clicked(bool checked); protected: virtual void keyPressEvent(QKeyEvent *e); diff --git a/gui/dialogs/ProgressDialog.ui b/gui/dialogs/ProgressDialog.ui index a56d2a92..04b8fef3 100644 --- a/gui/dialogs/ProgressDialog.ui +++ b/gui/dialogs/ProgressDialog.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>400</width> - <height>68</height> + <height>100</height> </rect> </property> <property name="minimumSize"> @@ -25,8 +25,8 @@ <property name="windowTitle"> <string>Please wait...</string> </property> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> <widget class="QLabel" name="statusLabel"> <property name="text"> <string>Task Status...</string> @@ -36,7 +36,7 @@ </property> </widget> </item> - <item> + <item row="1" column="0"> <widget class="QProgressBar" name="taskProgressBar"> <property name="value"> <number>24</number> @@ -46,6 +46,19 @@ </property> </widget> </item> + <item row="2" column="0"> + <widget class="QPushButton" name="skipButton"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Skip</string> + </property> + </widget> + </item> </layout> </widget> <resources/> diff --git a/gui/dialogs/SettingsDialog.cpp b/gui/dialogs/SettingsDialog.cpp index 5dcf60f8..e7f537e3 100644 --- a/gui/dialogs/SettingsDialog.cpp +++ b/gui/dialogs/SettingsDialog.cpp @@ -259,7 +259,8 @@ void SettingsDialog::on_javaTestBtn_clicked() checker.reset(new JavaChecker()); connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, SLOT(checkFinished(JavaCheckResult))); - checker->performCheck(ui->javaPathTextBox->text()); + checker->path = ui->javaPathTextBox->text(); + checker->performCheck(); } void SettingsDialog::checkFinished(JavaCheckResult result) @@ -271,7 +272,8 @@ void SettingsDialog::checkFinished(JavaCheckResult result) if (result.is_64bit) text += "Using 64bit java.\n"; text += "\n"; - text += "Platform reported: " + result.realPlatform; + text += "Platform reported: " + result.realPlatform + "\n"; + text += "Java version reported: " + result.javaVersion; QMessageBox::information(this, tr("Java test success"), text); } else diff --git a/gui/dialogs/UpdateDialog.cpp b/gui/dialogs/UpdateDialog.cpp new file mode 100644 index 00000000..c56798b4 --- /dev/null +++ b/gui/dialogs/UpdateDialog.cpp @@ -0,0 +1,28 @@ +#include "UpdateDialog.h" +#include "ui_UpdateDialog.h" +#include "gui/Platform.h" + +UpdateDialog::UpdateDialog(QWidget *parent) : QDialog(parent), ui(new Ui::UpdateDialog) +{ + MultiMCPlatform::fixWM_CLASS(this); + ui->setupUi(this); +} + +UpdateDialog::~UpdateDialog() +{ +} + +void UpdateDialog::on_btnUpdateLater_clicked() +{ + reject(); +} + +void UpdateDialog::on_btnUpdateNow_clicked() +{ + done(UPDATE_NOW); +} + +void UpdateDialog::on_btnUpdateOnExit_clicked() +{ + done(UPDATE_ONEXIT); +} diff --git a/logic/OneSixAssets.h b/gui/dialogs/UpdateDialog.h index 948068b8..c13eb6bf 100644 --- a/logic/OneSixAssets.h +++ b/gui/dialogs/UpdateDialog.h @@ -14,32 +14,33 @@ */ #pragma once -#include "net/NetJob.h" -class Private; -class ThreadedDeleter; +#include <QDialog> -class OneSixAssets : public QObject +namespace Ui { - Q_OBJECT -signals: - void failed(); - void finished(); - void indexStarted(); - void filesStarted(); - void filesProgress(int, int, int); +class UpdateDialog; +} + +enum UpdateAction +{ + UPDATE_LATER = QDialog::Rejected, + UPDATE_NOW = QDialog::Accepted, + UPDATE_ONEXIT = 2 +}; -public -slots: - void S3BucketFinished(); - void downloadFinished(); +class UpdateDialog : public QDialog +{ + Q_OBJECT public: - void start(); + explicit UpdateDialog(QWidget *parent = 0); + ~UpdateDialog(); private: - ThreadedDeleter *deleter; - QStringList nuke_whitelist; - NetJobPtr index_job; - NetJobPtr files_job; + Ui::UpdateDialog *ui; +public slots: + void on_btnUpdateNow_clicked(); + void on_btnUpdateOnExit_clicked(); + void on_btnUpdateLater_clicked(); }; diff --git a/gui/dialogs/UpdateDialog.ui b/gui/dialogs/UpdateDialog.ui new file mode 100644 index 00000000..1fe65e62 --- /dev/null +++ b/gui/dialogs/UpdateDialog.ui @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>UpdateDialog</class> + <widget class="QDialog" name="UpdateDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>350</width> + <height>260</height> + </rect> + </property> + <property name="windowTitle"> + <string>MultiMC Update</string> + </property> + <property name="windowIcon"> + <iconset resource="../../graphics.qrc"> + <normaloff>:/icons/toolbar/checkupdate</normaloff>:/icons/toolbar/checkupdate</iconset> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>A new MultiMC update is available!</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnUpdateNow"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Update now</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnUpdateOnExit"> + <property name="text"> + <string>Update after MultiMC closes</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="btnUpdateLater"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Don't update yet</string> + </property> + </widget> + </item> + </layout> + </widget> + <resources> + <include location="../../graphics.qrc"/> + </resources> + <connections/> +</ui> diff --git a/gui/dialogs/VersionSelectDialog.ui b/gui/dialogs/VersionSelectDialog.ui index 222f29cf..58264f24 100644 --- a/gui/dialogs/VersionSelectDialog.ui +++ b/gui/dialogs/VersionSelectDialog.ui @@ -11,7 +11,7 @@ </rect> </property> <property name="windowTitle"> - <string>Dialog</string> + <string>Choose Version</string> </property> <layout class="QVBoxLayout" name="verticalLayout"> <item> diff --git a/install_prereqs.cmake.in b/install_prereqs.cmake.in new file mode 100644 index 00000000..7565d6cb --- /dev/null +++ b/install_prereqs.cmake.in @@ -0,0 +1,16 @@ +FILE(GLOB_RECURSE QTPLUGINS "${CMAKE_INSTALL_PREFIX}/@PLUGIN_DEST_DIR@/*@CMAKE_SHARED_LIBRARY_SUFFIX@") +function(gp_resolved_file_type_override resolved_file type_var) + if(resolved_file MATCHES "^/usr/lib/libQt") + message("resolving ${resolved_file} as other") + set(${type_var} other PARENT_SCOPE) + endif() +endfunction() + +set(gp_tool "@CMAKE_GP_TOOL@") +set(gp_cmd_paths ${gp_cmd_paths} + "@CMAKE_GP_CMD_PATHS@" +) + +include(BundleUtilities) +fixup_bundle("@APPS@" "${QTPLUGINS}" "@DIRS@") + diff --git a/logger/QsLog.cpp b/logger/QsLog.cpp index 8cf68a53..87d7a412 100644 --- a/logger/QsLog.cpp +++ b/logger/QsLog.cpp @@ -134,4 +134,9 @@ void Logger::write(const QString &message) } } +void Logger::removeDestination(Destination* destination) +{ + d->destList.removeAll(destination); +} + } // end namespace diff --git a/logger/QsLog.h b/logger/QsLog.h index a18c08de..6c96423c 100644 --- a/logger/QsLog.h +++ b/logger/QsLog.h @@ -54,6 +54,8 @@ public: //! Adds a log message destination. Don't add null destinations. void addDestination(Destination *destination); + //! Removes the given destination from the logger. + void removeDestination(Destination* destination); //! Logging at a level < 'newLevel' will be ignored void setLoggingLevel(Level newLevel); //! The default level is INFO @@ -127,4 +129,4 @@ private: QsLogging::Logger::Helper(QsLogging::ErrorLevel).stream() << __FILE__ << '@' << __LINE__ #define QLOG_FATAL() \ QsLogging::Logger::Helper(QsLogging::FatalLevel).stream() << __FILE__ << '@' << __LINE__ -*/
\ No newline at end of file +*/ diff --git a/logger/QsLogDest.cpp b/logger/QsLogDest.cpp index 36297a14..2fd29b23 100644 --- a/logger/QsLogDest.cpp +++ b/logger/QsLogDest.cpp @@ -25,6 +25,7 @@ #include "QsLogDest.h" #include "QsDebugOutput.h" +#include "QsLog.h" #include <QFile> #include <QTextStream> #include <QString> @@ -32,6 +33,12 @@ namespace QsLogging { +Destination::~Destination() +{ + Logger::instance().removeDestination(this); + QsDebugOutput::output("Removed logger destination."); +} + //! file message sink class FileDestination : public Destination { diff --git a/logger/QsLogDest.h b/logger/QsLogDest.h index 32f1a9d0..e7fcc045 100644 --- a/logger/QsLogDest.h +++ b/logger/QsLogDest.h @@ -26,6 +26,7 @@ #pragma once #include <memory> + class QString; namespace QsLogging @@ -34,9 +35,7 @@ namespace QsLogging class Destination { public: - virtual ~Destination() - { - } + virtual ~Destination(); virtual void write(const QString &message) = 0; }; typedef std::shared_ptr<Destination> DestinationPtr; diff --git a/logic/BaseInstance.h b/logic/BaseInstance.h index 2d7537d6..93e57414 100644 --- a/logic/BaseInstance.h +++ b/logic/BaseInstance.h @@ -150,7 +150,7 @@ public: virtual SettingsObject &settings() const; /// returns a valid update task if update is needed, NULL otherwise - virtual Task *doUpdate(bool prepare_for_launch) = 0; + virtual std::shared_ptr<Task> doUpdate(bool only_prepare) = 0; /// returns a valid minecraft process, ready for launch with the given account. virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) = 0; diff --git a/logic/JavaChecker.cpp b/logic/JavaChecker.cpp index daad7281..2b94fbb6 100644 --- a/logic/JavaChecker.cpp +++ b/logic/JavaChecker.cpp @@ -1,6 +1,8 @@ #include "JavaChecker.h" #include <QFile> #include <QProcess> +#include <QMap> +#include <QTemporaryFile> #define CHECKER_FILE "JavaChecker.jar" @@ -8,16 +10,17 @@ JavaChecker::JavaChecker(QObject *parent) : QObject(parent) { } -void JavaChecker::performCheck(QString path) +void JavaChecker::performCheck() { - if(QFile::exists(CHECKER_FILE)) - { - QFile::remove(CHECKER_FILE); - } - // extract the checker - QFile(":/java/checker.jar").copy(CHECKER_FILE); + checkerJar.setFileTemplate("checker_XXXXXX.jar"); + checkerJar.open(); + QFile inner(":/java/checker.jar"); + inner.open(QIODevice::ReadOnly); + checkerJar.write(inner.readAll()); + inner.close(); + checkerJar.close(); - QStringList args = {"-jar", CHECKER_FILE}; + QStringList args = {"-jar", checkerJar.fileName()}; process.reset(new QProcess()); process->setArguments(args); @@ -39,31 +42,55 @@ void JavaChecker::finished(int exitcode, QProcess::ExitStatus status) killTimer.stop(); QProcessPtr _process; _process.swap(process); + checkerJar.remove(); + + JavaCheckResult result; + { + result.path = path; + } if (status == QProcess::CrashExit || exitcode == 1) { - emit checkFinished({}); + emit checkFinished(result); return; } + bool success = true; QString p_stdout = _process->readAllStandardOutput(); - auto parts = p_stdout.split('=', QString::SkipEmptyParts); - if (parts.size() != 2 || parts[0] != "os.arch") + QMap<QString, QString> results; + QStringList lines = p_stdout.split("\n", QString::SkipEmptyParts); + for(QString line : lines) { - emit checkFinished({}); + line = line.trimmed(); + + auto parts = line.split('=', QString::SkipEmptyParts); + if(parts.size() != 2 || parts[0].isEmpty() || parts[1].isEmpty()) + { + success = false; + } + else + { + results.insert(parts[0], parts[1]); + } + } + + if(!results.contains("os.arch") || !results.contains("java.version") || !success) + { + emit checkFinished(result); return; } - auto os_arch = parts[1].remove('\n').remove('\r'); + auto os_arch = results["os.arch"]; + auto java_version = results["java.version"]; bool is_64 = os_arch == "x86_64" || os_arch == "amd64"; - JavaCheckResult result; - { - result.valid = true; - result.is_64bit = is_64; - result.mojangPlatform = is_64 ? "64" : "32"; - result.realPlatform = os_arch; - } + + result.valid = true; + result.is_64bit = is_64; + result.mojangPlatform = is_64 ? "64" : "32"; + result.realPlatform = os_arch; + result.javaVersion = java_version; + emit checkFinished(result); } @@ -72,7 +99,13 @@ void JavaChecker::error(QProcess::ProcessError err) if(err == QProcess::FailedToStart) { killTimer.stop(); - emit checkFinished({}); + + JavaCheckResult result; + { + result.path = path; + } + + emit checkFinished(result); return; } } diff --git a/logic/JavaChecker.h b/logic/JavaChecker.h index 34782383..291bf46c 100644 --- a/logic/JavaChecker.h +++ b/logic/JavaChecker.h @@ -1,29 +1,39 @@ #pragma once #include <QProcess> #include <QTimer> +#include <QTemporaryFile> #include <memory> +class JavaChecker; + + struct JavaCheckResult { + QString path; QString mojangPlatform; QString realPlatform; + QString javaVersion; bool valid = false; bool is_64bit = false; }; -typedef std::shared_ptr<QProcess> QProcessPtr; +typedef std::shared_ptr<QProcess> QProcessPtr; +typedef std::shared_ptr<JavaChecker> JavaCheckerPtr; class JavaChecker : public QObject { Q_OBJECT public: explicit JavaChecker(QObject *parent = 0); - void performCheck(QString path); + void performCheck(); + + QString path; signals: void checkFinished(JavaCheckResult result); private: QProcessPtr process; QTimer killTimer; + QTemporaryFile checkerJar; public slots: void timeout(); diff --git a/logic/JavaCheckerJob.cpp b/logic/JavaCheckerJob.cpp new file mode 100644 index 00000000..36a8a050 --- /dev/null +++ b/logic/JavaCheckerJob.cpp @@ -0,0 +1,49 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "JavaCheckerJob.h" +#include "pathutils.h" +#include "MultiMC.h" + +#include "logger/QsLog.h" + +void JavaCheckerJob::partFinished(JavaCheckResult result) +{ + num_finished++; + QLOG_INFO() << m_job_name.toLocal8Bit() << "progress:" << num_finished << "/" + << javacheckers.size(); + emit progress(num_finished, javacheckers.size()); + + javaresults.append(result); + int result_size = javacheckers.size(); + + emit progress(num_finished, result_size); + + if (num_finished == javacheckers.size()) + { + emit finished(javaresults); + } +} + +void JavaCheckerJob::start() +{ + QLOG_INFO() << m_job_name.toLocal8Bit() << " started."; + m_running = true; + for (auto iter : javacheckers) + { + connect(iter.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); + iter->performCheck(); + } +} diff --git a/logic/JavaCheckerJob.h b/logic/JavaCheckerJob.h new file mode 100644 index 00000000..132a92d4 --- /dev/null +++ b/logic/JavaCheckerJob.h @@ -0,0 +1,100 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <QtNetwork> +#include <QLabel> +#include "JavaChecker.h" +#include "logic/tasks/ProgressProvider.h" + +class JavaCheckerJob; +typedef std::shared_ptr<JavaCheckerJob> JavaCheckerJobPtr; + +class JavaCheckerJob : public ProgressProvider +{ + Q_OBJECT +public: + explicit JavaCheckerJob(QString job_name) : ProgressProvider(), m_job_name(job_name) {}; + + bool addJavaCheckerAction(JavaCheckerPtr base) + { + javacheckers.append(base); + total_progress++; + // if this is already running, the action needs to be started right away! + if (isRunning()) + { + emit progress(current_progress, total_progress); + connect(base.get(), SIGNAL(checkFinished(JavaCheckResult)), SLOT(partFinished(JavaCheckResult))); + + base->performCheck(); + } + return true; + } + + JavaCheckerPtr operator[](int index) + { + return javacheckers[index]; + } + ; + JavaCheckerPtr first() + { + if (javacheckers.size()) + return javacheckers[0]; + return JavaCheckerPtr(); + } + int size() const + { + return javacheckers.size(); + } + virtual void getProgress(qint64 ¤t, qint64 &total) + { + current = current_progress; + total = total_progress; + } + ; + virtual QString getStatus() const + { + return m_job_name; + } + ; + virtual bool isRunning() const + { + return m_running; + } + ; + +signals: + void started(); + void progress(int current, int total); + void finished(QList<JavaCheckResult>); +public +slots: + virtual void start(); + // FIXME: implement + virtual void abort() {}; +private +slots: + void partFinished(JavaCheckResult result); + +private: + QString m_job_name; + QList<JavaCheckerPtr> javacheckers; + QList<JavaCheckResult> javaresults; + qint64 current_progress = 0; + qint64 total_progress = 0; + int num_finished = 0; + bool m_running = false; +}; diff --git a/logic/JavaUtils.cpp b/logic/JavaUtils.cpp index 61d0231a..e1b3bc64 100644 --- a/logic/JavaUtils.cpp +++ b/logic/JavaUtils.cpp @@ -26,11 +26,24 @@ #include "JavaUtils.h" #include "logger/QsLog.h" #include "gui/dialogs/VersionSelectDialog.h" +#include "JavaCheckerJob.h" +#include "lists/JavaVersionList.h" JavaUtils::JavaUtils() { } +JavaVersionPtr JavaUtils::MakeJavaPtr(QString path, QString id, QString arch) +{ + JavaVersionPtr javaVersion(new JavaVersion()); + + javaVersion->id = id; + javaVersion->arch = arch; + javaVersion->path = path; + + return javaVersion; +} + JavaVersionPtr JavaUtils::GetDefaultJava() { JavaVersionPtr javaVersion(new JavaVersion()); @@ -38,7 +51,6 @@ JavaVersionPtr JavaUtils::GetDefaultJava() javaVersion->id = "java"; javaVersion->arch = "unknown"; javaVersion->path = "java"; - javaVersion->recommended = false; return javaVersion; } @@ -112,7 +124,6 @@ QList<JavaVersionPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString javaVersion->arch = archType; javaVersion->path = QDir(PathCombine(value, "bin")).absoluteFilePath("java.exe"); - javaVersion->recommended = (recommended == subKeyName); javas.append(javaVersion); } @@ -128,9 +139,9 @@ QList<JavaVersionPtr> JavaUtils::FindJavaFromRegistryKey(DWORD keyType, QString return javas; } -QList<JavaVersionPtr> JavaUtils::FindJavaPaths() +QList<QString> JavaUtils::FindJavaPaths() { - QList<JavaVersionPtr> javas; + QList<JavaVersionPtr> java_candidates; QList<JavaVersionPtr> JRE64s = this->FindJavaFromRegistryKey( KEY_WOW64_64KEY, "SOFTWARE\\JavaSoft\\Java Runtime Environment"); @@ -141,58 +152,56 @@ QList<JavaVersionPtr> JavaUtils::FindJavaPaths() QList<JavaVersionPtr> JDK32s = this->FindJavaFromRegistryKey( KEY_WOW64_32KEY, "SOFTWARE\\JavaSoft\\Java Development Kit"); - javas.append(JRE64s); - javas.append(JDK64s); - javas.append(JRE32s); - javas.append(JDK32s); - - if (javas.size() <= 0) - { - QLOG_WARN() << "Failed to find Java in the Windows registry - defaulting to \"java\""; - javas.append(this->GetDefaultJava()); - return javas; - } - - QLOG_INFO() << "Found the following Java installations (64 -> 32, JRE -> JDK): "; - - for (auto &java : javas) + java_candidates.append(JRE64s); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre7/bin/java.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files/Java/jre6/bin/java.exe")); + java_candidates.append(JDK64s); + java_candidates.append(JRE32s); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre7/bin/java.exe")); + java_candidates.append(MakeJavaPtr("C:/Program Files (x86)/Java/jre6/bin/java.exe")); + java_candidates.append(JDK32s); + java_candidates.append(MakeJavaPtr(this->GetDefaultJava()->path)); + + QList<QString> candidates; + for(JavaVersionPtr java_candidate : java_candidates) { - QString sRec; - if (java->recommended) - sRec = "(Recommended)"; - QLOG_INFO() << java->id << java->arch << " at " << java->path << sRec; + if(!candidates.contains(java_candidate->path)) + { + candidates.append(java_candidate->path); + } } - return javas; + return candidates; } + #elif OSX -QList<JavaVersionPtr> JavaUtils::FindJavaPaths() +QList<QString> JavaUtils::FindJavaPaths() { QLOG_INFO() << "OS X Java detection incomplete - defaulting to \"java\""; - QList<JavaVersionPtr> javas; - javas.append(this->GetDefaultJava()); + QList<QString> javas; + javas.append(this->GetDefaultJava()->path); return javas; } #elif LINUX -QList<JavaVersionPtr> JavaUtils::FindJavaPaths() +QList<QString> JavaUtils::FindJavaPaths() { QLOG_INFO() << "Linux Java detection incomplete - defaulting to \"java\""; - QList<JavaVersionPtr> javas; - javas.append(this->GetDefaultJava()); + QList<QString> javas; + javas.append(this->GetDefaultJava()->path); return javas; } #else -QList<JavaVersionPtr> JavaUtils::FindJavaPaths() +QList<QString> JavaUtils::FindJavaPaths() { QLOG_INFO() << "Unknown operating system build - defaulting to \"java\""; - QList<JavaVersionPtr> javas; - javas.append(this->GetDefaultJava()); + QList<QString> javas; + javas.append(this->GetDefaultJava()->path); return javas; } diff --git a/logic/JavaUtils.h b/logic/JavaUtils.h index 44f576b4..22a68ef3 100644 --- a/logic/JavaUtils.h +++ b/logic/JavaUtils.h @@ -19,21 +19,23 @@ #include <QWidget> #include <osutils.h> - -#include "logic/lists/JavaVersionList.h" +#include "JavaCheckerJob.h" +#include "JavaChecker.h" +#include "lists/JavaVersionList.h" #if WINDOWS #include <windows.h> #endif -class JavaUtils +class JavaUtils : public QObject { + Q_OBJECT public: JavaUtils(); - QList<JavaVersionPtr> FindJavaPaths(); + JavaVersionPtr MakeJavaPtr(QString path, QString id = "unknown", QString arch = "unknown"); + QList<QString> FindJavaPaths(); JavaVersionPtr GetDefaultJava(); -private: #if WINDOWS QList<JavaVersionPtr> FindJavaFromRegistryKey(DWORD keyType, QString keyName); diff --git a/logic/LegacyInstance.cpp b/logic/LegacyInstance.cpp index 72b6c51a..fef27bcd 100644 --- a/logic/LegacyInstance.cpp +++ b/logic/LegacyInstance.cpp @@ -44,12 +44,12 @@ LegacyInstance::LegacyInstance(const QString &rootDir, SettingsObject *settings, settings->registerSetting(new Setting("IntendedJarVersion", "")); } -Task *LegacyInstance::doUpdate(bool prepare_for_launch) +std::shared_ptr<Task> LegacyInstance::doUpdate(bool only_prepare) { // make sure the jar mods list is initialized by asking for it. auto list = jarModList(); // create an update task - return new LegacyUpdate(this, prepare_for_launch , this); + return std::shared_ptr<Task> (new LegacyUpdate(this, only_prepare , this)); } MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account) @@ -105,7 +105,7 @@ MinecraftProcess *LegacyInstance::prepareForLaunch(MojangAccountPtr account) #endif args << "-jar" << LAUNCHER_FILE; - args << account->currentProfile()->name(); + args << account->currentProfile()->name; args << account->sessionId(); args << windowTitle; args << windowSize; diff --git a/logic/LegacyInstance.h b/logic/LegacyInstance.h index a17ef281..1e7d9eb6 100644 --- a/logic/LegacyInstance.h +++ b/logic/LegacyInstance.h @@ -76,7 +76,7 @@ public: virtual bool shouldUpdate() const override; virtual void setShouldUpdate(bool val) override; - virtual Task *doUpdate(bool prepare_for_launch) override; + virtual std::shared_ptr<Task> doUpdate(bool only_prepare) override; virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; virtual void cleanupAfterRun() override; diff --git a/logic/LegacyUpdate.cpp b/logic/LegacyUpdate.cpp index 3fc17351..e71b270e 100644 --- a/logic/LegacyUpdate.cpp +++ b/logic/LegacyUpdate.cpp @@ -25,15 +25,32 @@ #include <quazipfile.h> #include <JlCompress.h> #include "logger/QsLog.h" +#include "logic/net/URLConstants.h" -LegacyUpdate::LegacyUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent) - : Task(parent), m_inst(inst), m_prepare_for_launch(prepare_for_launch) +LegacyUpdate::LegacyUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) + : Task(parent), m_inst(inst), m_only_prepare(only_prepare) { } void LegacyUpdate::executeTask() { - lwjglStart(); + if(m_only_prepare) + { + // FIXME: think this through some more. + LegacyInstance *inst = (LegacyInstance *)m_inst; + if (!inst->shouldUpdate() || inst->shouldUseCustomBaseJar()) + { + ModTheJar(); + } + else + { + emitSucceeded(); + } + } + else + { + lwjglStart(); + } } void LegacyUpdate::lwjglStart() @@ -245,16 +262,20 @@ void LegacyUpdate::jarStart() // Build a list of URLs that will need to be downloaded. setStatus("Downloading new minecraft.jar"); - QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); - QString intended_version_id = inst->intendedVersionId(); - urlstr += intended_version_id + "/" + intended_version_id + ".jar"; + QString version_id = inst->intendedVersionId(); + QString localPath = version_id + "/" + version_id + ".jar"; + QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath; - auto dljob = new NetJob("Minecraft.jar for version " + intended_version_id); - dljob->addNetAction(FileDownload::make(QUrl(urlstr), inst->defaultBaseJar())); - legacyDownloadJob.reset(dljob); + auto dljob = new NetJob("Minecraft.jar for version " + version_id); + + + auto metacache = MMC->metacache(); + auto entry = metacache->resolveEntry("versions", localPath); + dljob->addNetAction(CacheDownload::make(QUrl(urlstr), entry)); connect(dljob, SIGNAL(succeeded()), SLOT(jarFinished())); connect(dljob, SIGNAL(failed()), SLOT(jarFailed())); connect(dljob, SIGNAL(progress(qint64, qint64)), SIGNAL(progress(qint64, qint64))); + legacyDownloadJob.reset(dljob); legacyDownloadJob->start(); } @@ -466,4 +487,4 @@ void LegacyUpdate::ModTheJar() // inst->UpdateVersion(true); emitSucceeded(); return; -}
\ No newline at end of file +} diff --git a/logic/LegacyUpdate.h b/logic/LegacyUpdate.h index d753197f..0b573ca5 100644 --- a/logic/LegacyUpdate.h +++ b/logic/LegacyUpdate.h @@ -31,7 +31,7 @@ class LegacyUpdate : public Task { Q_OBJECT public: - explicit LegacyUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent = 0); + explicit LegacyUpdate(BaseInstance *inst, bool only_prepare, QObject *parent = 0); virtual void executeTask(); private @@ -72,5 +72,5 @@ private: private: NetJobPtr legacyDownloadJob; BaseInstance *m_inst = nullptr; - bool m_prepare_for_launch = false; + bool m_only_prepare = false; }; diff --git a/logic/MinecraftProcess.cpp b/logic/MinecraftProcess.cpp index 5d99bfae..209929b7 100644 --- a/logic/MinecraftProcess.cpp +++ b/logic/MinecraftProcess.cpp @@ -75,20 +75,22 @@ QString MinecraftProcess::censorPrivateInfo(QString in) { if(!m_account) return in; - else + + QString sessionId = m_account->sessionId(); + QString accessToken = m_account->accessToken(); + QString clientToken = m_account->clientToken(); + in.replace(sessionId, "<SESSION ID>"); + in.replace(accessToken, "<ACCESS TOKEN>"); + in.replace(clientToken, "<CLIENT TOKEN>"); + auto profile = m_account->currentProfile(); + if(profile) { - QString sessionId = m_account->sessionId(); - QString accessToken = m_account->accessToken(); - QString clientToken = m_account->clientToken(); - QString profileId = m_account->currentProfile()->id(); - QString profileName = m_account->currentProfile()->name(); - in.replace(sessionId, "<SESSION ID>"); - in.replace(accessToken, "<ACCESS TOKEN>"); - in.replace(clientToken, "<CLIENT TOKEN>"); + QString profileId = profile->id; + QString profileName = profile->name; in.replace(profileId, "<PROFILE ID>"); in.replace(profileName, "<PROFILE NAME>"); - return in; } + return in; } // console window diff --git a/logic/OneSixAssets.cpp b/logic/OneSixAssets.cpp deleted file mode 100644 index 400aff2c..00000000 --- a/logic/OneSixAssets.cpp +++ /dev/null @@ -1,127 +0,0 @@ -/* Copyright 2013 MultiMC Contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <QString> -#include "logger/QsLog.h" -#include <QtXml/QtXml> -#include "OneSixAssets.h" -#include "net/NetJob.h" -#include "net/HttpMetaCache.h" -#include "net/S3ListBucket.h" -#include "MultiMC.h" - -#define ASSETS_URL "http://resources.download.minecraft.net/" - -class ThreadedDeleter : public QThread -{ - Q_OBJECT -public: - void run() - { - QLOG_INFO() << "Cleaning up assets folder..."; - QDirIterator iter(m_base, QDirIterator::Subdirectories); - int base_length = m_base.length(); - while (iter.hasNext()) - { - QString filename = iter.next(); - QFileInfo current(filename); - // we keep the dirs... whatever - if (current.isDir()) - continue; - QString trimmedf = filename; - trimmedf.remove(0, base_length + 1); - if (m_whitelist.contains(trimmedf)) - { - QLOG_TRACE() << trimmedf << " gets to live"; - } - else - { - // DO NOT TOLERATE JUNK - QLOG_TRACE() << trimmedf << " dies"; - QFile f(filename); - f.remove(); - } - } - } - QString m_base; - QStringList m_whitelist; -}; - -void OneSixAssets::downloadFinished() -{ - deleter = new ThreadedDeleter(); - QDir dir("assets"); - deleter->m_base = dir.absolutePath(); - deleter->m_whitelist = nuke_whitelist; - connect(deleter, SIGNAL(finished()), SIGNAL(finished())); - deleter->start(); -} - -void OneSixAssets::S3BucketFinished() -{ - QString prefix(ASSETS_URL); - nuke_whitelist.clear(); - - emit filesStarted(); - - auto firstJob = index_job->first(); - auto objectList = std::dynamic_pointer_cast<S3ListBucket>(firstJob)->objects; - - NetJob *job = new NetJob("Assets"); - - connect(job, SIGNAL(succeeded()), SLOT(downloadFinished())); - connect(job, SIGNAL(failed()), SIGNAL(failed())); - connect(job, SIGNAL(filesProgress(int, int, int)), SIGNAL(filesProgress(int, int, int))); - - auto metacache = MMC->metacache(); - - for (auto object : objectList) - { - // Filter folder keys (zero size) - if (object.size == 0) - continue; - - nuke_whitelist.append(object.Key); - - auto entry = metacache->resolveEntry("assets", object.Key, object.ETag); - if (entry->stale) - { - job->addNetAction(CacheDownload::make(QUrl(prefix + object.Key), entry)); - } - } - if (job->size()) - { - files_job.reset(job); - files_job->start(); - } - else - { - delete job; - emit finished(); - } -} - -void OneSixAssets::start() -{ - auto job = new NetJob("Assets index"); - job->addNetAction(S3ListBucket::make(QUrl(ASSETS_URL))); - connect(job, SIGNAL(succeeded()), SLOT(S3BucketFinished())); - connect(job, SIGNAL(failed()), SIGNAL(failed())); - emit indexStarted(); - index_job.reset(job); - job->start(); -} - -#include "OneSixAssets.moc" diff --git a/logic/OneSixInstance.cpp b/logic/OneSixInstance.cpp index 08b63bf9..fd41b9e5 100644 --- a/logic/OneSixInstance.cpp +++ b/logic/OneSixInstance.cpp @@ -26,6 +26,7 @@ #include <JlCompress.h> #include "gui/dialogs/OneSixModEditDialog.h" #include "logger/QsLog.h" +#include "logic/assets/AssetsUtils.h" OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_obj, QObject *parent) @@ -37,9 +38,9 @@ OneSixInstance::OneSixInstance(const QString &rootDir, SettingsObject *setting_o reloadFullVersion(); } -Task *OneSixInstance::doUpdate(bool prepare_for_launch) +std::shared_ptr<Task> OneSixInstance::doUpdate(bool only_prepare) { - return new OneSixUpdate(this, prepare_for_launch); + return std::shared_ptr<Task> (new OneSixUpdate(this, only_prepare)); } QString replaceTokensIn(QString text, QMap<QString, QString> with) @@ -66,6 +67,63 @@ QString replaceTokensIn(QString text, QMap<QString, QString> with) return result; } +QDir OneSixInstance::reconstructAssets(std::shared_ptr<OneSixVersion> version) +{ + QDir assetsDir = QDir("assets/"); + QDir indexDir = QDir(PathCombine(assetsDir.path(), "indexes")); + QDir objectDir = QDir(PathCombine(assetsDir.path(), "objects")); + QDir virtualDir = QDir(PathCombine(assetsDir.path(), "virtual")); + + QString indexPath = PathCombine(indexDir.path(), version->assets + ".json"); + QFile indexFile(indexPath); + QDir virtualRoot(PathCombine(virtualDir.path(), version->assets)); + + if(!indexFile.exists()) + { + QLOG_ERROR() << "No assets index file" << indexPath << "; can't reconstruct assets"; + return virtualRoot; + } + + QLOG_DEBUG() << "reconstructAssets" << assetsDir.path() << indexDir.path() << objectDir.path() << virtualDir.path() << virtualRoot.path(); + + AssetsIndex index; + bool loadAssetsIndex = AssetsUtils::loadAssetsIndexJson(indexPath, &index); + + if(loadAssetsIndex) + { + if(index.isVirtual) + { + QLOG_INFO() << "Reconstructing virtual assets folder at" << virtualRoot.path(); + + for(QString map : index.objects.keys()) + { + AssetObject asset_object = index.objects.value(map); + QString target_path = PathCombine(virtualRoot.path(), map); + QFile target(target_path); + + QString tlk = asset_object.hash.left(2); + + QString original_path = PathCombine(PathCombine(objectDir.path(), tlk), asset_object.hash); + QFile original(original_path); + if(!target.exists()) + { + QFileInfo info(target_path); + QDir target_dir = info.dir(); + //QLOG_DEBUG() << target_dir; + if(!target_dir.exists()) QDir("").mkpath(target_dir.path()); + + bool couldCopy = original.copy(target_path); + QLOG_DEBUG() << " Copying" << original_path << "to" << target_path << QString::number(couldCopy);// << original.errorString(); + } + } + + // TODO: Write last used time to virtualRoot/.lastused + } + } + + return virtualRoot; +} + QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) { I_D(OneSixInstance); @@ -77,8 +135,8 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) token_mapping["auth_username"] = account->username(); token_mapping["auth_session"] = account->sessionId(); token_mapping["auth_access_token"] = account->accessToken(); - token_mapping["auth_player_name"] = account->currentProfile()->name(); - token_mapping["auth_uuid"] = account->currentProfile()->id(); + token_mapping["auth_player_name"] = account->currentProfile()->name; + token_mapping["auth_uuid"] = account->currentProfile()->id; // this is for offline?: /* @@ -93,9 +151,22 @@ QStringList OneSixInstance::processMinecraftArgs(MojangAccountPtr account) QString absRootDir = QDir(minecraftRoot()).absolutePath(); token_mapping["game_directory"] = absRootDir; QString absAssetsDir = QDir("assets/").absolutePath(); - token_mapping["game_assets"] = absAssetsDir; - //TODO: this is something new and not even fully implemented in the vanilla launcher. - token_mapping["user_properties"] = "{ }"; + token_mapping["game_assets"] = reconstructAssets(d->version).absolutePath(); + + auto user = account->user(); + QJsonObject userAttrs; + for(auto key: user.properties.keys()) + { + auto array = QJsonArray::fromStringList(user.properties.values(key)); + userAttrs.insert(key, array); + } + QJsonDocument value(userAttrs); + + token_mapping["user_properties"] = value.toJson(QJsonDocument::Compact); + token_mapping["user_type"] = account->currentProfile()->legacy ? "legacy" : "mojang"; + // 1.7.3+ assets tokens + token_mapping["assets_root"] = absAssetsDir; + token_mapping["assets_index_name"] = version->assets; QStringList parts = args_pattern.split(' ', QString::SkipEmptyParts); for (int i = 0; i < parts.length(); i++) diff --git a/logic/OneSixInstance.h b/logic/OneSixInstance.h index 042c104b..f869e345 100644 --- a/logic/OneSixInstance.h +++ b/logic/OneSixInstance.h @@ -16,6 +16,7 @@ #pragma once #include <QStringList> +#include <QDir> #include "BaseInstance.h" @@ -39,7 +40,7 @@ public: QString loaderModsDir() const; virtual QString instanceConfigFolder() const override; - virtual Task *doUpdate(bool prepare_for_launch) override; + virtual std::shared_ptr<Task> doUpdate(bool only_prepare) override; virtual MinecraftProcess *prepareForLaunch(MojangAccountPtr account) override; virtual void cleanupAfterRun() override; @@ -73,4 +74,5 @@ public: private: QStringList processMinecraftArgs(MojangAccountPtr account); + QDir reconstructAssets(std::shared_ptr<OneSixVersion> version); }; diff --git a/logic/OneSixLibrary.cpp b/logic/OneSixLibrary.cpp index ad4aec44..4b6ed9dc 100644 --- a/logic/OneSixLibrary.cpp +++ b/logic/OneSixLibrary.cpp @@ -18,6 +18,7 @@ #include "OneSixLibrary.h" #include "OneSixRule.h" #include "OpSys.h" +#include "logic/net/URLConstants.h" void OneSixLibrary::finalize() { @@ -140,9 +141,9 @@ QJsonObject OneSixLibrary::toJson() libRoot.insert("MMC-absoluteUrl", m_absolute_url); if (m_hint.size()) libRoot.insert("MMC-hint", m_hint); - if (m_base_url != "http://s3.amazonaws.com/Minecraft.Download/libraries/" && - m_base_url != "https://s3.amazonaws.com/Minecraft.Download/libraries/" && - m_base_url != "https://libraries.minecraft.net/") + if (m_base_url != "http://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && + m_base_url != "https://" + URLConstants::AWS_DOWNLOAD_LIBRARIES && + m_base_url != "https://" + URLConstants::LIBRARY_BASE) libRoot.insert("url", m_base_url); if (isNative() && m_native_suffixes.size()) { diff --git a/logic/OneSixLibrary.h b/logic/OneSixLibrary.h index f8dc3aef..5cb867c2 100644 --- a/logic/OneSixLibrary.h +++ b/logic/OneSixLibrary.h @@ -21,6 +21,7 @@ #include <QJsonObject> #include <memory> +#include "logic/net/URLConstants.h" #include "OpSys.h" class Rule; @@ -30,7 +31,7 @@ class OneSixLibrary private: // basic values used internally (so far) QString m_name; - QString m_base_url = "https://libraries.minecraft.net/"; + QString m_base_url = "https://" + URLConstants::LIBRARY_BASE; QList<std::shared_ptr<Rule>> m_rules; // custom values diff --git a/logic/OneSixUpdate.cpp b/logic/OneSixUpdate.cpp index 25e16328..66950fc4 100644 --- a/logic/OneSixUpdate.cpp +++ b/logic/OneSixUpdate.cpp @@ -29,12 +29,14 @@ #include "OneSixLibrary.h" #include "OneSixInstance.h" #include "net/ForgeMirrors.h" +#include "net/URLConstants.h" +#include "assets/AssetsUtils.h" #include "pathutils.h" #include <JlCompress.h> -OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool prepare_for_launch, QObject *parent) - : Task(parent), m_inst(inst), m_prepare_for_launch(prepare_for_launch) +OneSixUpdate::OneSixUpdate(BaseInstance *inst, bool only_prepare, QObject *parent) + : Task(parent), m_inst(inst), m_only_prepare(only_prepare) { } @@ -50,6 +52,24 @@ void OneSixUpdate::executeTask() return; } + if (m_only_prepare) + { + if (m_inst->shouldUpdate()) + { + emitFailed("Unable to update instance in offline mode."); + return; + } + setStatus("Testing the Java installation."); + QString java_path = m_inst->settings().get("JavaPath").toString(); + + checker.reset(new JavaChecker()); + connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, + SLOT(checkFinishedOffline(JavaCheckResult))); + checker->path = java_path; + checker->performCheck(); + return; + } + if (m_inst->shouldUpdate()) { // Get a pointer to the version object that corresponds to the instance's version. @@ -65,35 +85,44 @@ void OneSixUpdate::executeTask() } else { - checkJava(); + checkJavaOnline(); } } -void OneSixUpdate::checkJava() +void OneSixUpdate::checkJavaOnline() { - QLOG_INFO() << m_inst->name() << ": checking java binary"; setStatus("Testing the Java installation."); - // TODO: cache this so we don't have to run an extra java process every time. QString java_path = m_inst->settings().get("JavaPath").toString(); checker.reset(new JavaChecker()); connect(checker.get(), SIGNAL(checkFinished(JavaCheckResult)), this, - SLOT(checkFinished(JavaCheckResult))); - checker->performCheck(java_path); + SLOT(checkFinishedOnline(JavaCheckResult))); + checker->path = java_path; + checker->performCheck(); } -void OneSixUpdate::checkFinished(JavaCheckResult result) +void OneSixUpdate::checkFinishedOnline(JavaCheckResult result) { if (result.valid) { - QLOG_INFO() << m_inst->name() << ": java is " - << (result.is_64bit ? "64 bit" : "32 bit"); java_is_64bit = result.is_64bit; jarlibStart(); } else { - QLOG_INFO() << m_inst->name() << ": java isn't valid"; + emitFailed("The java binary doesn't work. Check the settings and correct the problem"); + } +} + +void OneSixUpdate::checkFinishedOffline(JavaCheckResult result) +{ + if (result.valid) + { + java_is_64bit = result.is_64bit; + prepareForLaunch(); + } + else + { emitFailed("The java binary doesn't work. Check the settings and correct the problem"); } } @@ -103,8 +132,7 @@ void OneSixUpdate::versionFileStart() QLOG_INFO() << m_inst->name() << ": getting version file."; setStatus("Getting the version files from Mojang."); - QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); - urlstr += targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; + QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + targetVersion->descriptor() + "/" + targetVersion->descriptor() + ".json"; auto job = new NetJob("Version index"); job->addNetAction(ByteArrayDownload::make(QUrl(urlstr))); specificVersionDownloadJob.reset(job); @@ -160,7 +188,7 @@ void OneSixUpdate::versionFileFinished() } inst->reloadFullVersion(); - checkJava(); + checkJavaOnline(); } void OneSixUpdate::versionFileFailed() @@ -168,6 +196,90 @@ void OneSixUpdate::versionFileFailed() emitFailed("Failed to download the version description. Try again."); } +void OneSixUpdate::assetIndexStart() +{ + setStatus("Updating asset index."); + OneSixInstance *inst = (OneSixInstance *)m_inst; + std::shared_ptr<OneSixVersion> version = inst->getFullVersion(); + QString assetName = version->assets; + QUrl indexUrl = "http://" + URLConstants::AWS_DOWNLOAD_INDEXES + assetName + ".json"; + QString localPath = assetName + ".json"; + auto job = new NetJob("Asset index for " + inst->name()); + + auto metacache = MMC->metacache(); + auto entry = metacache->resolveEntry("asset_indexes", localPath); + job->addNetAction(CacheDownload::make(indexUrl, entry)); + jarlibDownloadJob.reset(job); + + connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetIndexFinished())); + connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(assetIndexFailed())); + connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + + jarlibDownloadJob->start(); +} + +void OneSixUpdate::assetIndexFinished() +{ + AssetsIndex index; + + OneSixInstance *inst = (OneSixInstance *)m_inst; + std::shared_ptr<OneSixVersion> version = inst->getFullVersion(); + QString assetName = version->assets; + + QString asset_fname = "assets/indexes/" + assetName + ".json"; + if (!AssetsUtils::loadAssetsIndexJson(asset_fname, &index)) + { + emitFailed("Failed to read the assets index!"); + } + + QList<Md5EtagDownloadPtr> dls; + for (auto object : index.objects.values()) + { + QString objectName = object.hash.left(2) + "/" + object.hash; + QFileInfo objectFile("assets/objects/" + objectName); + if ((!objectFile.isFile()) || (objectFile.size() != object.size)) + { + auto objectDL = MD5EtagDownload::make( + QUrl("http://" + URLConstants::RESOURCE_BASE + objectName), + objectFile.filePath()); + dls.append(objectDL); + } + } + if(dls.size()) + { + setStatus("Getting the assets files from Mojang..."); + auto job = new NetJob("Assets for " + inst->name()); + for(auto dl: dls) + job->addNetAction(dl); + jarlibDownloadJob.reset(job); + connect(jarlibDownloadJob.get(), SIGNAL(succeeded()), SLOT(assetsFinished())); + connect(jarlibDownloadJob.get(), SIGNAL(failed()), SLOT(assetsFailed())); + connect(jarlibDownloadJob.get(), SIGNAL(progress(qint64, qint64)), + SIGNAL(progress(qint64, qint64))); + jarlibDownloadJob->start(); + return; + } + assetsFinished(); +} + +void OneSixUpdate::assetIndexFailed() +{ + emitFailed("Failed to download the assets index!"); +} + +void OneSixUpdate::assetsFinished() +{ + prepareForLaunch(); +} + +void OneSixUpdate::assetsFailed() +{ + emitFailed("Failed to download assets!"); +} + + + void OneSixUpdate::jarlibStart() { setStatus("Getting the library files from Mojang."); @@ -181,17 +293,22 @@ void OneSixUpdate::jarlibStart() return; } + // Build a list of URLs that will need to be downloaded. std::shared_ptr<OneSixVersion> version = inst->getFullVersion(); + // minecraft.jar for this version + { + QString version_id = version->id; + QString localPath = version_id + "/" + version_id + ".jar"; + QString urlstr = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + localPath; - // download the right jar, save it in versions/$version/$version.jar - QString urlstr("http://s3.amazonaws.com/Minecraft.Download/versions/"); - urlstr += version->id + "/" + version->id + ".jar"; - QString targetstr("versions/"); - targetstr += version->id + "/" + version->id + ".jar"; + auto job = new NetJob("Libraries for instance " + inst->name()); - auto job = new NetJob("Libraries for instance " + inst->name()); - job->addNetAction(FileDownload::make(QUrl(urlstr), targetstr)); - jarlibDownloadJob.reset(job); + auto metacache = MMC->metacache(); + auto entry = metacache->resolveEntry("versions", localPath); + job->addNetAction(CacheDownload::make(QUrl(urlstr), entry)); + + jarlibDownloadJob.reset(job); + } auto libs = version->getActiveNativeLibs(); libs.append(version->getActiveNormalLibs()); @@ -240,10 +357,7 @@ void OneSixUpdate::jarlibStart() void OneSixUpdate::jarlibFinished() { - if (m_prepare_for_launch) - prepareForLaunch(); - else - emitSucceeded(); + assetIndexStart(); } void OneSixUpdate::jarlibFailed() diff --git a/logic/OneSixUpdate.h b/logic/OneSixUpdate.h index b86c205f..00b769c7 100644 --- a/logic/OneSixUpdate.h +++ b/logic/OneSixUpdate.h @@ -43,11 +43,20 @@ slots: void jarlibFinished(); void jarlibFailed(); - void checkJava(); - void checkFinished(JavaCheckResult result); + void assetIndexStart(); + void assetIndexFinished(); + void assetIndexFailed(); + + void assetsFinished(); + void assetsFailed(); + + void checkJavaOnline(); + void checkFinishedOnline(JavaCheckResult result); + void checkFinishedOffline(JavaCheckResult result); // extract the appropriate libraries void prepareForLaunch(); + private: NetJobPtr specificVersionDownloadJob; NetJobPtr jarlibDownloadJob; @@ -55,7 +64,7 @@ private: // target version, determined during this task std::shared_ptr<MinecraftVersion> targetVersion; BaseInstance *m_inst = nullptr; - bool m_prepare_for_launch = false; + bool m_only_prepare = false; std::shared_ptr<JavaChecker> checker; bool java_is_64bit = false; diff --git a/logic/OneSixVersion.cpp b/logic/OneSixVersion.cpp index cc5b1de1..8ae685f0 100644 --- a/logic/OneSixVersion.cpp +++ b/logic/OneSixVersion.cpp @@ -17,6 +17,8 @@ #include "logic/OneSixLibrary.h" #include "logic/OneSixRule.h" +#include "logger/QsLog.h" + std::shared_ptr<OneSixVersion> fromJsonV4(QJsonObject root, std::shared_ptr<OneSixVersion> fullVersion) { @@ -60,6 +62,18 @@ std::shared_ptr<OneSixVersion> fromJsonV4(QJsonObject root, fullVersion->releaseTime = root.value("releaseTime").toString(); fullVersion->time = root.value("time").toString(); + auto assetsID = root.value("assets"); + if (assetsID.isString()) + { + fullVersion->assets = assetsID.toString(); + } + else + { + fullVersion->assets = "legacy"; + } + + QLOG_DEBUG() << "Assets version:" << fullVersion->assets; + // Iterate through the list, if it's a list. auto librariesValue = root.value("libraries"); if (!librariesValue.isArray()) @@ -151,7 +165,7 @@ std::shared_ptr<OneSixVersion> OneSixVersion::fromJson(QJsonObject root) root.value("minimumLauncherVersion").toDouble(); // ADD MORE HERE :D - if (launcher_ver > 0 && launcher_ver <= 11) + if (launcher_ver > 0 && launcher_ver <= 13) return fromJsonV4(root, readVersion); else { diff --git a/logic/OneSixVersion.h b/logic/OneSixVersion.h index 5718dafe..036f3d53 100644 --- a/logic/OneSixVersion.h +++ b/logic/OneSixVersion.h @@ -56,6 +56,8 @@ public: QString releaseTime; /// Release type - "release" or "snapshot" QString type; + /// Assets type - "legacy" or a version ID + QString assets; /** * DEPRECATED: Old versions of the new vanilla launcher used this * ex: "username_session_version" diff --git a/logic/assets/AssetsUtils.cpp b/logic/assets/AssetsUtils.cpp new file mode 100644 index 00000000..11d928cf --- /dev/null +++ b/logic/assets/AssetsUtils.cpp @@ -0,0 +1,227 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <QDir> +#include <QDirIterator> +#include <QCryptographicHash> +#include <QJsonParseError> +#include <QJsonDocument> +#include <QJsonObject> + +#include "AssetsUtils.h" +#include "MultiMC.h" + +namespace AssetsUtils +{ +void migrateOldAssets() +{ + QDir assets_dir("assets"); + if (!assets_dir.exists()) + return; + assets_dir.setFilter(QDir::AllEntries | QDir::NoDotAndDotDot); + int base_length = assets_dir.path().length(); + + QList<QString> blacklist = {"indexes", "objects", "virtual"}; + + if (!assets_dir.exists("objects")) + assets_dir.mkdir("objects"); + QDir objects_dir("assets/objects"); + + QDirIterator iterator(assets_dir, QDirIterator::Subdirectories); + int successes = 0; + int failures = 0; + while (iterator.hasNext()) + { + QString currentDir = iterator.next(); + currentDir = currentDir.remove(0, base_length + 1); + + bool ignore = false; + for (QString blacklisted : blacklist) + { + if (currentDir.startsWith(blacklisted)) + ignore = true; + } + + if (!iterator.fileInfo().isDir() && !ignore) + { + QString filename = iterator.filePath(); + + QFile input(filename); + input.open(QIODevice::ReadOnly | QIODevice::WriteOnly); + QString sha1sum = + QCryptographicHash::hash(input.readAll(), QCryptographicHash::Sha1) + .toHex() + .constData(); + + QString object_name = filename.remove(0, base_length + 1); + QLOG_DEBUG() << "Processing" << object_name << ":" << sha1sum << input.size(); + + QString object_tlk = sha1sum.left(2); + QString object_tlk_dir = objects_dir.path() + "/" + object_tlk; + + QDir tlk_dir(object_tlk_dir); + if (!tlk_dir.exists()) + objects_dir.mkdir(object_tlk); + + QString new_filename = tlk_dir.path() + "/" + sha1sum; + QFile new_object(new_filename); + if (!new_object.exists()) + { + bool rename_success = input.rename(new_filename); + QLOG_DEBUG() << " Doesn't exist, copying to" << new_filename << ":" + << QString::number(rename_success); + if (rename_success) + successes++; + else + failures++; + } + else + { + input.remove(); + QLOG_DEBUG() << " Already exists, deleting original and not copying."; + } + } + } + + if (successes + failures == 0) + { + QLOG_DEBUG() << "No legacy assets needed importing."; + } + else + { + QLOG_DEBUG() << "Finished copying legacy assets:" << successes << "successes and" + << failures << "failures."; + + QDirIterator cleanup_iterator(assets_dir); + + while (cleanup_iterator.hasNext()) + { + QString currentDir = cleanup_iterator.next(); + currentDir = currentDir.remove(0, base_length + 1); + + bool ignore = false; + for (QString blacklisted : blacklist) + { + if (currentDir.startsWith(blacklisted)) + ignore = true; + } + + if (cleanup_iterator.fileInfo().isDir() && !ignore) + { + QString path = cleanup_iterator.filePath(); + QDir folder(path); + + QLOG_DEBUG() << "Cleaning up legacy assets folder:" << path; + + folder.removeRecursively(); + } + } + } +} + +/* + * Returns true on success, with index populated + * index is undefined otherwise + */ +bool loadAssetsIndexJson(QString path, AssetsIndex *index) +{ + /* + { + "objects": { + "icons/icon_16x16.png": { + "hash": "bdf48ef6b5d0d23bbb02e17d04865216179f510a", + "size": 3665 + }, + ... + } + } + } + */ + + QFile file(path); + + // Try to open the file and fail if we can't. + // TODO: We should probably report this error to the user. + if (!file.open(QIODevice::ReadOnly)) + { + QLOG_ERROR() << "Failed to read assets index file" << path; + return false; + } + + // Read the file and close it. + QByteArray jsonData = file.readAll(); + file.close(); + + QJsonParseError parseError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); + + // Fail if the JSON is invalid. + if (parseError.error != QJsonParseError::NoError) + { + QLOG_ERROR() << "Failed to parse assets index file:" << parseError.errorString() + << "at offset " << QString::number(parseError.offset); + return false; + } + + // Make sure the root is an object. + if (!jsonDoc.isObject()) + { + QLOG_ERROR() << "Invalid assets index JSON: Root should be an array."; + return false; + } + + QJsonObject root = jsonDoc.object(); + + QJsonValue isVirtual = root.value("virtual"); + if (!isVirtual.isUndefined()) + { + index->isVirtual = isVirtual.toBool(false); + } + + QJsonValue objects = root.value("objects"); + QVariantMap map = objects.toVariant().toMap(); + + for (QVariantMap::const_iterator iter = map.begin(); iter != map.end(); ++iter) + { + // QLOG_DEBUG() << iter.key(); + + QVariant variant = iter.value(); + QVariantMap nested_objects = variant.toMap(); + + AssetObject object; + + for (QVariantMap::const_iterator nested_iter = nested_objects.begin(); + nested_iter != nested_objects.end(); ++nested_iter) + { + // QLOG_DEBUG() << nested_iter.key() << nested_iter.value().toString(); + QString key = nested_iter.key(); + QVariant value = nested_iter.value(); + + if (key == "hash") + { + object.hash = value.toString(); + } + else if (key == "size") + { + object.size = value.toDouble(); + } + } + + index->objects.insert(iter.key(), object); + } + + return true; +} +} diff --git a/logic/assets/AssetsUtils.h b/logic/assets/AssetsUtils.h new file mode 100644 index 00000000..5276d5a5 --- /dev/null +++ b/logic/assets/AssetsUtils.h @@ -0,0 +1,39 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <QString> +#include <QMap> + +class AssetObject; + +struct AssetObject +{ + QString hash; + qint64 size; +}; + +struct AssetsIndex +{ + QMap<QString, AssetObject> objects; + bool isVirtual = false; +}; + +namespace AssetsUtils +{ +void migrateOldAssets(); +bool loadAssetsIndexJson(QString file, AssetsIndex* index); +} diff --git a/logic/auth/MojangAccount.cpp b/logic/auth/MojangAccount.cpp index 4a61cf19..bc6af98f 100644 --- a/logic/auth/MojangAccount.cpp +++ b/logic/auth/MojangAccount.cpp @@ -16,113 +16,17 @@ */ #include "MojangAccount.h" +#include "flows/RefreshTask.h" +#include "flows/AuthenticateTask.h" #include <QUuid> #include <QJsonObject> #include <QJsonArray> +#include <QRegExp> +#include <QStringList> #include <logger/QsLog.h> -MojangAccount::MojangAccount(const QString &username, QObject *parent) : QObject(parent) -{ - // Generate a client token. - m_clientToken = QUuid::createUuid().toString(); - - m_username = username; - - m_currentProfile = -1; -} - -MojangAccount::MojangAccount(const QString &username, const QString &clientToken, - const QString &accessToken, QObject *parent) - : QObject(parent) -{ - m_username = username; - m_clientToken = clientToken; - m_accessToken = accessToken; - - m_currentProfile = -1; -} - -MojangAccount::MojangAccount(const MojangAccount &other, QObject *parent) -{ - m_username = other.username(); - m_clientToken = other.clientToken(); - m_accessToken = other.accessToken(); - - m_profiles = other.m_profiles; - m_currentProfile = other.m_currentProfile; -} - -QString MojangAccount::username() const -{ - return m_username; -} - -QString MojangAccount::clientToken() const -{ - return m_clientToken; -} - -void MojangAccount::setClientToken(const QString &clientToken) -{ - m_clientToken = clientToken; -} - -QString MojangAccount::accessToken() const -{ - return m_accessToken; -} - -void MojangAccount::setAccessToken(const QString &accessToken) -{ - m_accessToken = accessToken; -} - -QString MojangAccount::sessionId() const -{ - return "token:" + m_accessToken + ":" + currentProfile()->id(); -} - -const QList<AccountProfile> MojangAccount::profiles() const -{ - return m_profiles; -} - -const AccountProfile *MojangAccount::currentProfile() const -{ - if (m_currentProfile < 0) - { - if (m_profiles.length() > 0) - return &m_profiles.at(0); - else - return nullptr; - } - else - return &m_profiles.at(m_currentProfile); -} - -bool MojangAccount::setProfile(const QString &profileId) -{ - const QList<AccountProfile> &profiles = this->profiles(); - for (int i = 0; i < profiles.length(); i++) - { - if (profiles.at(i).id() == profileId) - { - m_currentProfile = i; - return true; - } - } - return false; -} - -void MojangAccount::loadProfiles(const ProfileList &profiles) -{ - m_profiles.clear(); - for (auto profile : profiles) - m_profiles.append(profile); -} - MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) { // The JSON object must at least have a username for it to be valid. @@ -143,78 +47,160 @@ MojangAccountPtr MojangAccount::loadFromJson(const QJsonObject &object) return nullptr; } - ProfileList profiles; + QList<AccountProfile> profiles; for (QJsonValue profileVal : profileArray) { QJsonObject profileObject = profileVal.toObject(); QString id = profileObject.value("id").toString(""); QString name = profileObject.value("name").toString(""); + bool legacy = profileObject.value("legacy").toBool(false); if (id.isEmpty() || name.isEmpty()) { QLOG_WARN() << "Unable to load a profile because it was missing an ID or a name."; continue; } - profiles.append(AccountProfile(id, name)); + profiles.append({id, name, legacy}); } - MojangAccountPtr account(new MojangAccount(username, clientToken, accessToken)); - account->loadProfiles(profiles); + MojangAccountPtr account(new MojangAccount()); + if(object.value("user").isObject()) + { + User u; + QJsonObject userStructure = object.value("user").toObject(); + u.id = userStructure.value("id").toString(); + /* + QJsonObject propMap = userStructure.value("properties").toObject(); + for(auto key: propMap.keys()) + { + auto values = propMap.operator[](key).toArray(); + for(auto value: values) + u.properties.insert(key, value.toString()); + } + */ + account->m_user = u; + } + account->m_username = username; + account->m_clientToken = clientToken; + account->m_accessToken = accessToken; + account->m_profiles = profiles; // Get the currently selected profile. QString currentProfile = object.value("activeProfile").toString(""); if (!currentProfile.isEmpty()) - account->setProfile(currentProfile); + account->setCurrentProfile(currentProfile); + + return account; +} +MojangAccountPtr MojangAccount::createFromUsername(const QString& username) +{ + MojangAccountPtr account(new MojangAccount()); + account->m_clientToken = QUuid::createUuid().toString().remove(QRegExp("[{}-]")); + account->m_username = username; return account; } -QJsonObject MojangAccount::saveToJson() +QJsonObject MojangAccount::saveToJson() const { QJsonObject json; - json.insert("username", username()); - json.insert("clientToken", clientToken()); - json.insert("accessToken", accessToken()); + json.insert("username", m_username); + json.insert("clientToken", m_clientToken); + json.insert("accessToken", m_accessToken); QJsonArray profileArray; for (AccountProfile profile : m_profiles) { QJsonObject profileObj; - profileObj.insert("id", profile.id()); - profileObj.insert("name", profile.name()); + profileObj.insert("id", profile.id); + profileObj.insert("name", profile.name); + profileObj.insert("legacy", profile.legacy); profileArray.append(profileObj); } json.insert("profiles", profileArray); - if (currentProfile() != nullptr) - json.insert("activeProfile", currentProfile()->id()); + QJsonObject userStructure; + { + userStructure.insert("id", m_user.id); + /* + QJsonObject userAttrs; + for(auto key: m_user.properties.keys()) + { + auto array = QJsonArray::fromStringList(m_user.properties.values(key)); + userAttrs.insert(key, array); + } + userStructure.insert("properties", userAttrs); + */ + } + json.insert("user", userStructure); + + if (m_currentProfile != -1) + json.insert("activeProfile", currentProfile()->id); return json; } - -AccountProfile::AccountProfile(const QString& id, const QString& name) +bool MojangAccount::setCurrentProfile(const QString &profileId) { - m_id = id; - m_name = name; + for (int i = 0; i < m_profiles.length(); i++) + { + if (m_profiles[i].id == profileId) + { + m_currentProfile = i; + return true; + } + } + return false; } -AccountProfile::AccountProfile(const AccountProfile &other) +const AccountProfile* MojangAccount::currentProfile() const { - m_id = other.m_id; - m_name = other.m_name; + if(m_currentProfile == -1) + return nullptr; + return &m_profiles[m_currentProfile]; } -QString AccountProfile::id() const +AccountStatus MojangAccount::accountStatus() const { - return m_id; + if(m_accessToken.isEmpty()) + return NotVerified; + if(!m_online) + return Verified; + return Online; } -QString AccountProfile::name() const +std::shared_ptr<Task> MojangAccount::login(QString password) { - return m_name; + if(m_currentTask) + return m_currentTask; + if(password.isEmpty()) + { + m_currentTask.reset(new RefreshTask(this)); + } + else + { + m_currentTask.reset(new AuthenticateTask(this, password)); + } + connect(m_currentTask.get(), SIGNAL(succeeded()), SLOT(authSucceeded())); + connect(m_currentTask.get(), SIGNAL(failed(QString)), SLOT(authFailed(QString))); + return m_currentTask; } -void MojangAccount::propagateChange() +void MojangAccount::authSucceeded() { + m_online = true; + m_currentTask.reset(); emit changed(); } + +void MojangAccount::authFailed(QString reason) +{ + // This is emitted when the yggdrasil tasks time out or are cancelled. + // -> we treat the error as no-op + if(reason != "Yggdrasil task cancelled.") + { + m_online = false; + m_accessToken = QString(); + emit changed(); + } + m_currentTask.reset(); +} diff --git a/logic/auth/MojangAccount.h b/logic/auth/MojangAccount.h index 25a85790..325aa826 100644 --- a/logic/auth/MojangAccount.h +++ b/logic/auth/MojangAccount.h @@ -20,43 +20,42 @@ #include <QList> #include <QJsonObject> #include <QPair> +#include <QMap> #include <memory> +class Task; +class YggdrasilTask; class MojangAccount; typedef std::shared_ptr<MojangAccount> MojangAccountPtr; Q_DECLARE_METATYPE(MojangAccountPtr) /** - * Class that represents a profile within someone's Mojang account. + * A profile within someone's Mojang account. * * Currently, the profile system has not been implemented by Mojang yet, * but we might as well add some things for it in MultiMC right now so * we don't have to rip the code to pieces to add it later. */ -class AccountProfile +struct AccountProfile { -public: - AccountProfile(const QString &id, const QString &name); - AccountProfile(const AccountProfile &other); - - QString id() const; - QString name() const; - -protected: - QString m_id; - QString m_name; + QString id; + QString name; + bool legacy; }; -typedef QList<AccountProfile> ProfileList; - struct User { QString id; - // pair of key:value - // we don't know if the keys:value mapping is 1:1, so a list is used. - QList<QPair<QString, QString>> properties; + QMultiMap<QString,QString> properties; +}; + +enum AccountStatus +{ + NotVerified, + Verified, + Online }; /** @@ -68,106 +67,121 @@ struct User class MojangAccount : public QObject { Q_OBJECT -public: - /** - * Constructs a new MojangAccount with the given username. - * The client token will be generated automatically and the access token will be blank. - */ - explicit MojangAccount(const QString &username, QObject *parent = 0); +public: /* construction */ + //! Do not copy accounts. ever. + explicit MojangAccount(const MojangAccount &other, QObject *parent) = delete; - /** - * Constructs a new MojangAccount with the given username, client token, and access token. - */ - explicit MojangAccount(const QString &username, const QString &clientToken, - const QString &accessToken, QObject *parent = 0); + //! Default constructor + explicit MojangAccount(QObject *parent = 0) : QObject(parent) {}; - /** - * Constructs a new MojangAccount matching the given account. - */ - MojangAccount(const MojangAccount &other, QObject *parent); + //! Creates an empty account for the specified user name. + static MojangAccountPtr createFromUsername(const QString &username); - /** - * Loads a MojangAccount from the given JSON object. - */ + //! Loads a MojangAccount from the given JSON object. static MojangAccountPtr loadFromJson(const QJsonObject &json); - /** - * Saves a MojangAccount to a JSON object and returns it. - */ - QJsonObject saveToJson(); + //! Saves a MojangAccount to a JSON object and returns it. + QJsonObject saveToJson() const; +public: /* manipulation */ /** - * Update the account on disk and lists (it changed, for whatever reason) - * This is called by various Yggdrasil tasks. - */ - void propagateChange(); + * Sets the currently selected profile to the profile with the given ID string. + * If profileId is not in the list of available profiles, the function will simply return + * false. + */ + bool setCurrentProfile(const QString &profileId); + + /** + * Attempt to login. Empty password means we use the token. + * If the attempt fails because we already are performing some task, it returns false. + */ + std::shared_ptr<Task> login(QString password = QString()); + + void downgrade() + { + m_online = false; + } +public: /* queries */ + const QString &username() const + { + return m_username; + } + + const QString &clientToken() const + { + return m_clientToken; + } + + const QString &accessToken() const + { + return m_accessToken; + } + + const QList<AccountProfile> &profiles() const + { + return m_profiles; + } + + const User & user() + { + return m_user; + } + + //! Get the session ID required for legacy Minecraft versions + QString sessionId() const + { + if (m_currentProfile != -1 && !m_accessToken.isEmpty()) + return "token:" + m_accessToken + ":" + m_profiles[m_currentProfile].id; + return "-"; + } + + //! Returns the currently selected profile (if none, returns nullptr) + const AccountProfile *currentProfile() const; - /** - * This MojangAccount's username. May be an email address if the account is migrated. - */ - QString username() const; + //! Returns whether the account is NotVerified, Verified or Online + AccountStatus accountStatus() const; +signals: /** - * This MojangAccount's client token. This is a UUID used by Mojang's auth servers to identify this client. - * This is unique for each MojangAccount. + * This signal is emitted when the account changes */ - QString clientToken() const; + void changed(); - /** - * Sets the MojangAccount's client token to the given value. - */ - void setClientToken(const QString &token); + // TODO: better signalling for the various possible state changes - especially errors - /** - * This MojangAccount's access token. - * If the user has not chosen to stay logged in, this will be an empty string. - */ - QString accessToken() const; +protected: /* variables */ + QString m_username; - /** - * Changes this MojangAccount's access token to the given value. - */ - void setAccessToken(const QString &token); + // Used to identify the client - the user can have multiple clients for the same account + // Think: different launchers, all connecting to the same account/profile + QString m_clientToken; - /** - * Get full session ID - */ - QString sessionId() const; + // Blank if not logged in. + QString m_accessToken; - /** - * Returns a list of the available account profiles. - */ - const ProfileList profiles() const; + // Index of the selected profile within the list of available + // profiles. -1 if nothing is selected. + int m_currentProfile = -1; - /** - * Returns a pointer to the currently selected profile. - * If no profile is selected, returns the first profile in the profile list or nullptr if there are none. - */ - const AccountProfile *currentProfile() const; + // List of available profiles. + QList<AccountProfile> m_profiles; - /** - * Sets the currently selected profile to the profile with the given ID string. - * If profileId is not in the list of available profiles, the function will simply return false. - */ - bool setProfile(const QString &profileId); + // the user structure, whatever it is. + User m_user; - /** - * Clears the current account profile list and replaces it with the given profile list. - */ - void loadProfiles(const ProfileList &profiles); + // true when the account is verified + bool m_online = false; -signals: - /** - * This isgnal is emitted whrn the account changes - */ - void changed(); + // current task we are executing here + std::shared_ptr<YggdrasilTask> m_currentTask; -protected: - QString m_username; - QString m_clientToken; - QString m_accessToken; // Blank if not logged in. - int m_currentProfile; // Index of the selected profile within the list of available - // profiles. -1 if nothing is selected. - ProfileList m_profiles; // List of available profiles. - User m_user; // the user structure, whatever it is. +private slots: + void authSucceeded(); + void authFailed(QString reason); + +public: + friend class YggdrasilTask; + friend class AuthenticateTask; + friend class ValidateTask; + friend class RefreshTask; }; diff --git a/logic/lists/MojangAccountList.cpp b/logic/auth/MojangAccountList.cpp index 439b5da6..33990662 100644 --- a/logic/lists/MojangAccountList.cpp +++ b/logic/auth/MojangAccountList.cpp @@ -13,7 +13,7 @@ * limitations under the License. */ -#include "logic/lists/MojangAccountList.h" +#include "logic/auth/MojangAccountList.h" #include <QIODevice> #include <QFile> @@ -28,7 +28,7 @@ #include "logic/auth/MojangAccount.h" -#define ACCOUNT_LIST_FORMAT_VERSION 1 +#define ACCOUNT_LIST_FORMAT_VERSION 2 MojangAccountList::MojangAccountList(QObject *parent) : QAbstractListModel(parent) { @@ -84,10 +84,7 @@ void MojangAccountList::removeAccount(QModelIndex index) MojangAccountPtr MojangAccountList::activeAccount() const { - if (m_activeAccount.isEmpty()) - return nullptr; - else - return findAccount(m_activeAccount); + return m_activeAccount; } void MojangAccountList::setActiveAccount(const QString &username) @@ -95,14 +92,14 @@ void MojangAccountList::setActiveAccount(const QString &username) beginResetModel(); if (username.isEmpty()) { - m_activeAccount = ""; + m_activeAccount = nullptr; } else { for (MojangAccountPtr account : m_accounts) { if (account->username() == username) - m_activeAccount = username; + m_activeAccount = account; } } endResetModel(); @@ -152,9 +149,6 @@ QVariant MojangAccountList::data(const QModelIndex &index, int role) const case Qt::DisplayRole: switch (index.column()) { - case ActiveColumn: - return account->username() == m_activeAccount; - case NameColumn: return account->username(); @@ -168,6 +162,13 @@ QVariant MojangAccountList::data(const QModelIndex &index, int role) const case PointerRole: return qVariantFromValue(account); + case Qt::CheckStateRole: + switch (index.column()) + { + case ActiveColumn: + return account == m_activeAccount; + } + default: return QVariant(); } @@ -216,6 +217,36 @@ int MojangAccountList::columnCount(const QModelIndex &parent) const return 2; } +Qt::ItemFlags MojangAccountList::flags(const QModelIndex &index) const +{ + if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) + { + return Qt::NoItemFlags; + } + + return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +bool MojangAccountList::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if (index.row() < 0 || index.row() >= rowCount(index) || !index.isValid()) + { + return false; + } + + if(role == Qt::CheckStateRole) + { + if(value == Qt::Checked) + { + MojangAccountPtr account = this->at(index.row()); + this->setActiveAccount(account->username()); + } + } + + emit dataChanged(index, index); + return true; +} + void MojangAccountList::updateListData(QList<MojangAccountPtr> versions) { beginResetModel(); @@ -303,11 +334,9 @@ bool MojangAccountList::loadList(const QString &filePath) QLOG_WARN() << "Failed to load an account."; } } - endResetModel(); - // Load the active account. - m_activeAccount = root.value("activeAccount").toString(""); - + m_activeAccount = findAccount(root.value("activeAccount").toString("")); + endResetModel(); return true; } @@ -347,8 +376,11 @@ bool MojangAccountList::saveList(const QString &filePath) // Insert the account list into the root object. root.insert("accounts", accounts); - // Save the active account. - root.insert("activeAccount", m_activeAccount); + if(m_activeAccount) + { + // Save the active account. + root.insert("activeAccount", m_activeAccount->username()); + } // Create a JSON document object to convert our JSON to bytes. QJsonDocument doc(root); diff --git a/logic/lists/MojangAccountList.h b/logic/auth/MojangAccountList.h index 744f3c51..c7e30958 100644 --- a/logic/lists/MojangAccountList.h +++ b/logic/auth/MojangAccountList.h @@ -64,6 +64,8 @@ public: virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const; virtual int rowCount(const QModelIndex &parent) const; virtual int columnCount(const QModelIndex &parent) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role); /*! * Adds a the given Mojang account to the account list. @@ -161,10 +163,9 @@ protected: QList<MojangAccountPtr> m_accounts; /*! - * Username of the account that is currently active. - * Empty string if no account is active. + * Account that is currently active. */ - QString m_activeAccount; + MojangAccountPtr m_activeAccount; //! Path to the account list file. Empty string if there isn't one. QString m_listFilePath; diff --git a/logic/auth/YggdrasilTask.cpp b/logic/auth/YggdrasilTask.cpp index 797e84cd..088e1fc0 100644 --- a/logic/auth/YggdrasilTask.cpp +++ b/logic/auth/YggdrasilTask.cpp @@ -24,17 +24,11 @@ #include <MultiMC.h> #include <logic/auth/MojangAccount.h> +#include <logic/net/URLConstants.h> -YggdrasilTask::YggdrasilTask(MojangAccountPtr account, QObject *parent) : Task(parent) +YggdrasilTask::YggdrasilTask(MojangAccount *account, QObject *parent) + : Task(parent), m_account(account) { - m_error = nullptr; - m_account = account; -} - -YggdrasilTask::~YggdrasilTask() -{ - if (m_error) - delete m_error; } void YggdrasilTask::executeTask() @@ -45,97 +39,126 @@ void YggdrasilTask::executeTask() QJsonDocument doc(getRequestContent()); auto worker = MMC->qnam(); - connect(worker.get(), SIGNAL(finished(QNetworkReply *)), this, - SLOT(processReply(QNetworkReply *))); - - QUrl reqUrl("https://authserver.mojang.com/" + getEndpoint()); + QUrl reqUrl("https://" + URLConstants::AUTH_BASE + getEndpoint()); QNetworkRequest netRequest(reqUrl); netRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); QByteArray requestData = doc.toJson(); m_netReply = worker->post(netRequest, requestData); + connect(m_netReply, &QNetworkReply::finished, this, &YggdrasilTask::processReply); + connect(m_netReply, &QNetworkReply::uploadProgress, this, &YggdrasilTask::refreshTimers); + connect(m_netReply, &QNetworkReply::downloadProgress, this, &YggdrasilTask::refreshTimers); + timeout_keeper.setSingleShot(true); + timeout_keeper.start(timeout_max); + counter.setSingleShot(false); + counter.start(time_step); + progress(0, timeout_max); + connect(&timeout_keeper, &QTimer::timeout, this, &YggdrasilTask::abort); + connect(&counter, &QTimer::timeout, this, &YggdrasilTask::heartbeat); } -void YggdrasilTask::processReply(QNetworkReply *reply) +void YggdrasilTask::refreshTimers(qint64, qint64) { - setStatus(getStateMessage(STATE_PROCESSING_RESPONSE)); + timeout_keeper.stop(); + timeout_keeper.start(timeout_max); + progress(count = 0, timeout_max); +} +void YggdrasilTask::heartbeat() +{ + count += time_step; + progress(count, timeout_max); +} - if (m_netReply != reply) - // Wrong reply for some reason... - return; +void YggdrasilTask::abort() +{ + progress(timeout_max, timeout_max); + m_netReply->abort(); +} - if (reply->error() == QNetworkReply::OperationCanceledError) +void YggdrasilTask::processReply() +{ + setStatus(getStateMessage(STATE_PROCESSING_RESPONSE)); + + // any network errors lead to offline mode right now + if (m_netReply->error() >= QNetworkReply::ConnectionRefusedError && + m_netReply->error() <= QNetworkReply::UnknownNetworkError) { + // WARNING/FIXME: the value here is used in MojangAccount to detect the cancel/timeout emitFailed("Yggdrasil task cancelled."); return; } - else + + // Try to parse the response regardless of the response code. + // Sometimes the auth server will give more information and an error code. + QJsonParseError jsonError; + QByteArray replyData = m_netReply->readAll(); + QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); + // Check the response code. + int responseCode = m_netReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); + + if (responseCode == 200) { - // Try to parse the response regardless of the response code. - // Sometimes the auth server will give more information and an error code. - QJsonParseError jsonError; - QByteArray replyData = reply->readAll(); - QJsonDocument doc = QJsonDocument::fromJson(replyData, &jsonError); - // Check the response code. - int responseCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt(); - - if (responseCode == 200) + // If the response code was 200, then there shouldn't be an error. Make sure + // anyways. + // Also, sometimes an empty reply indicates success. If there was no data received, + // pass an empty json object to the processResponse function. + if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) { - // If the response code was 200, then there shouldn't be an error. Make sure anyways. - // Also, sometimes an empty reply indicates success. If there was no data received, - // pass an empty json object to the processResponse function. - if (jsonError.error == QJsonParseError::NoError || replyData.size() == 0) - { - if (!processResponse(replyData.size() > 0 ? doc.object() : QJsonObject())) - { - YggdrasilTask::Error *err = getError(); - if (err) - emitFailed(err->getErrorMessage()); - else - emitFailed(tr("An unknown error occurred when processing the response " - "from the authentication server.")); - } - else - { - emitSucceeded(); - } - } - else + if (processResponse(replyData.size() > 0 ? doc.object() : QJsonObject())) { - emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset)); + emitSucceeded(); + return; } + + // errors happened anyway? + emitFailed(m_error ? m_error->m_errorMessageVerbose + : tr("An unknown error occurred when processing the response " + "from the authentication server.")); } else { - // If the response code was not 200, then Yggdrasil may have given us information about the error. - // If we can parse the response, then get information from it. Otherwise just say there was an unknown error. - if (jsonError.error == QJsonParseError::NoError) - { - // We were able to parse the server's response. Woo! - // Call processError. If a subclass has overridden it then they'll handle their stuff there. - QLOG_DEBUG() << "The request failed, but the server gave us an error message. Processing error."; - emitFailed(processError(doc.object())); - } - else - { - // The server didn't say anything regarding the error. Give the user an unknown error. - QLOG_DEBUG() << "The request failed and the server gave no error message. Unknown error."; - emitFailed(tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(reply->errorString())); - } + emitFailed(tr("Failed to parse Yggdrasil JSON response: %1 at offset %2.") + .arg(jsonError.errorString()) + .arg(jsonError.offset)); } + return; + } + + // If the response code was not 200, then Yggdrasil may have given us information + // about the error. + // If we can parse the response, then get information from it. Otherwise just say + // there was an unknown error. + if (jsonError.error == QJsonParseError::NoError) + { + // We were able to parse the server's response. Woo! + // Call processError. If a subclass has overridden it then they'll handle their + // stuff there. + QLOG_DEBUG() << "The request failed, but the server gave us an error message. " + "Processing error."; + emitFailed(processError(doc.object())); + } + else + { + // The server didn't say anything regarding the error. Give the user an unknown + // error. + QLOG_DEBUG() << "The request failed and the server gave no error message. " + "Unknown error."; + emitFailed(tr("An unknown error occurred when trying to communicate with the " + "authentication server: %1").arg(m_netReply->errorString())); } } QString YggdrasilTask::processError(QJsonObject responseData) { QJsonValue errorVal = responseData.value("error"); - QJsonValue msgVal = responseData.value("errorMessage"); + QJsonValue errorMessageValue = responseData.value("errorMessage"); QJsonValue causeVal = responseData.value("cause"); - if (errorVal.isString() && msgVal.isString()) + if (errorVal.isString() && errorMessageValue.isString()) { - m_error = new Error(errorVal.toString(""), msgVal.toString(""), causeVal.toString("")); - return m_error->getDisplayMessage(); + m_error = std::shared_ptr<Error>(new Error{ + errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")}); + return m_error->m_errorMessageVerbose; } else { @@ -156,13 +179,3 @@ QString YggdrasilTask::getStateMessage(const YggdrasilTask::State state) const return tr("Processing. Please wait."); } } - -YggdrasilTask::Error *YggdrasilTask::getError() const -{ - return this->m_error; -} - -MojangAccountPtr YggdrasilTask::getMojangAccount() const -{ - return this->m_account; -} diff --git a/logic/auth/YggdrasilTask.h b/logic/auth/YggdrasilTask.h index 62638c9d..1f81a2d0 100644 --- a/logic/auth/YggdrasilTask.h +++ b/logic/auth/YggdrasilTask.h @@ -19,6 +19,7 @@ #include <QString> #include <QJsonObject> +#include <QTimer> #include "logic/auth/MojangAccount.h" @@ -31,45 +32,18 @@ class YggdrasilTask : public Task { Q_OBJECT public: - explicit YggdrasilTask(MojangAccountPtr account, QObject *parent = 0); - ~YggdrasilTask(); + explicit YggdrasilTask(MojangAccount * account, QObject *parent = 0); /** * Class describing a Yggdrasil error response. */ - class Error + struct Error { - public: - Error(const QString& shortError, const QString& errorMessage, const QString& cause) : - m_shortError(shortError), m_errorMessage(errorMessage), m_cause(cause) {} - - QString getShortError() const { return m_shortError; } - QString getErrorMessage() const { return m_errorMessage; } - QString getCause() const { return m_cause; } - - /// Gets the string to display in the GUI for describing this error. - QString getDisplayMessage() - { - return getErrorMessage(); - } - - protected: - QString m_shortError; - QString m_errorMessage; + QString m_errorMessageShort; + QString m_errorMessageVerbose; QString m_cause; }; - /** - * Gets the Mojang account that this task is operating on. - */ - virtual MojangAccountPtr getMojangAccount() const; - - /** - * Returns a pointer to a YggdrasilTask::Error object if an error has occurred. - * If no error has occurred, returns a null pointer. - */ - virtual Error *getError() const; - protected: /** * Enum for describing the state of the current task. @@ -120,13 +94,25 @@ protected: */ virtual QString getStateMessage(const State state) const; - MojangAccountPtr m_account; - - QNetworkReply *m_netReply; - - Error *m_error; - protected slots: - void processReply(QNetworkReply *reply); + void processReply(); + void refreshTimers(qint64, qint64); + void heartbeat(); + +public +slots: + virtual void abort() override; + +protected: + // FIXME: segfault disaster waiting to happen + MojangAccount *m_account = nullptr; + QNetworkReply *m_netReply = nullptr; + std::shared_ptr<Error> m_error; + QTimer timeout_keeper; + QTimer counter; + int count = 0; // num msec since time reset + + const int timeout_max = 10000; + const int time_step = 50; }; diff --git a/logic/auth/flows/AuthenticateTask.cpp b/logic/auth/flows/AuthenticateTask.cpp index ec2004d6..f60be35d 100644 --- a/logic/auth/flows/AuthenticateTask.cpp +++ b/logic/auth/flows/AuthenticateTask.cpp @@ -26,7 +26,7 @@ #include "logger/QsLog.h" -AuthenticateTask::AuthenticateTask(MojangAccountPtr account, const QString &password, +AuthenticateTask::AuthenticateTask(MojangAccount * account, const QString &password, QObject *parent) : YggdrasilTask(account, parent), m_password(password) { @@ -59,14 +59,14 @@ QJsonObject AuthenticateTask::getRequestContent() const req.insert("agent", agent); } - req.insert("username", getMojangAccount()->username()); + req.insert("username", m_account->username()); req.insert("password", m_password); req.insert("requestUser", true); // If we already have a client token, give it to the server. // Otherwise, let the server give us one. - if (!getMojangAccount()->clientToken().isEmpty()) - req.insert("clientToken", getMojangAccount()->clientToken()); + if (!m_account->m_clientToken.isEmpty()) + req.insert("clientToken", m_account->m_clientToken); return req; } @@ -76,7 +76,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData) // Read the response data. We need to get the client token, access token, and the selected // profile. QLOG_DEBUG() << "Processing authentication response."; - + // QLOG_DEBUG() << responseData; // If we already have a client token, make sure the one the server gave us matches our // existing one. QLOG_DEBUG() << "Getting client token."; @@ -88,8 +88,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData) QLOG_ERROR() << "Server didn't send a client token."; return false; } - if (!getMojangAccount()->clientToken().isEmpty() && - clientToken != getMojangAccount()->clientToken()) + if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken) { // The server changed our client token! Obey its wishes, but complain. That's what I do // for my parents, so... @@ -97,7 +96,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData) << "'. This shouldn't happen, but it isn't really a big deal."; } // Set the client token. - getMojangAccount()->setClientToken(clientToken); + m_account->m_clientToken = clientToken; // Now, we set the access token. QLOG_DEBUG() << "Getting access token."; @@ -109,7 +108,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData) QLOG_ERROR() << "Server didn't send an access token."; } // Set the access token. - getMojangAccount()->setAccessToken(accessToken); + m_account->m_accessToken = accessToken; // Now we load the list of available profiles. // Mojang hasn't yet implemented the profile system, @@ -117,13 +116,14 @@ bool AuthenticateTask::processResponse(QJsonObject responseData) // don't have trouble implementing it later. QLOG_DEBUG() << "Loading profile list."; QJsonArray availableProfiles = responseData.value("availableProfiles").toArray(); - ProfileList loadedProfiles; + QList<AccountProfile> loadedProfiles; for (auto iter : availableProfiles) { QJsonObject profile = iter.toObject(); // Profiles are easy, we just need their ID and name. QString id = profile.value("id").toString(""); QString name = profile.value("name").toString(""); + bool legacy = profile.value("legacy").toBool(false); if (id.isEmpty() || name.isEmpty()) { @@ -135,10 +135,10 @@ bool AuthenticateTask::processResponse(QJsonObject responseData) } // Now, add a new AccountProfile entry to the list. - loadedProfiles.append(AccountProfile(id, name)); + loadedProfiles.append({id, name, legacy}); } // Put the list of profiles we loaded into the MojangAccount object. - getMojangAccount()->loadProfiles(loadedProfiles); + m_account->m_profiles = loadedProfiles; // Finally, we set the current profile to the correct value. This is pretty simple. // We do need to make sure that the current profile that the server gave us @@ -153,7 +153,7 @@ bool AuthenticateTask::processResponse(QJsonObject responseData) QLOG_ERROR() << "Server didn't specify a currently selected profile."; return false; } - if (!getMojangAccount()->setProfile(currentProfileId)) + if (!m_account->setCurrentProfile(currentProfileId)) { // TODO: Set an error to display to the user. QLOG_ERROR() << "Server specified a selected profile that wasn't in the available " @@ -162,23 +162,20 @@ bool AuthenticateTask::processResponse(QJsonObject responseData) } // this is what the vanilla launcher passes to the userProperties launch param - // doesn't seem to be used for anything so far? I don't get any of this data on my account - // (peterixxx) - // is it a good idea to log this? if (responseData.contains("user")) { + User u; auto obj = responseData.value("user").toObject(); - auto userId = obj.value("id").toString(); + u.id = obj.value("id").toString(); auto propArray = obj.value("properties").toArray(); - QLOG_DEBUG() << "User ID: " << userId; - QLOG_DEBUG() << "User Properties: "; for (auto prop : propArray) { auto propTuple = prop.toObject(); auto name = propTuple.value("name").toString(); auto value = propTuple.value("value").toString(); - QLOG_DEBUG() << name << " : " << value; + u.properties.insert(name, value); } + m_account->m_user = u; } // We've made it through the minefield of possible errors. Return true to indicate that diff --git a/logic/auth/flows/AuthenticateTask.h b/logic/auth/flows/AuthenticateTask.h index 3b99caad..b6564657 100644 --- a/logic/auth/flows/AuthenticateTask.h +++ b/logic/auth/flows/AuthenticateTask.h @@ -30,7 +30,7 @@ class AuthenticateTask : public YggdrasilTask { Q_OBJECT public: - AuthenticateTask(MojangAccountPtr account, const QString &password, QObject *parent = 0); + AuthenticateTask(MojangAccount *account, const QString &password, QObject *parent = 0); protected: virtual QJsonObject getRequestContent() const; diff --git a/logic/auth/flows/InvalidateTask.cpp b/logic/auth/flows/InvalidateTask.cpp deleted file mode 100644 index e69de29b..00000000 --- a/logic/auth/flows/InvalidateTask.cpp +++ /dev/null diff --git a/logic/auth/flows/InvalidateTask.h b/logic/auth/flows/InvalidateTask.h deleted file mode 100644 index e69de29b..00000000 --- a/logic/auth/flows/InvalidateTask.h +++ /dev/null diff --git a/logic/auth/flows/RefreshTask.cpp b/logic/auth/flows/RefreshTask.cpp index b56ed9bc..5f68ccc7 100644 --- a/logic/auth/flows/RefreshTask.cpp +++ b/logic/auth/flows/RefreshTask.cpp @@ -25,7 +25,7 @@ #include "logger/QsLog.h" -RefreshTask::RefreshTask(MojangAccountPtr account, QObject *parent) +RefreshTask::RefreshTask(MojangAccount *account, QObject *parent) : YggdrasilTask(account, parent) { } @@ -44,13 +44,12 @@ QJsonObject RefreshTask::getRequestContent() const * "requestUser": true/false // request the user structure * } */ - auto account = getMojangAccount(); QJsonObject req; - req.insert("clientToken", account->clientToken()); - req.insert("accessToken", account->accessToken()); + req.insert("clientToken", m_account->m_clientToken); + req.insert("accessToken", m_account->m_accessToken); /* { - auto currentProfile = account->currentProfile(); + auto currentProfile = m_account->currentProfile(); QJsonObject profile; profile.insert("id", currentProfile->id()); profile.insert("name", currentProfile->name()); @@ -64,12 +63,11 @@ QJsonObject RefreshTask::getRequestContent() const bool RefreshTask::processResponse(QJsonObject responseData) { - auto account = getMojangAccount(); - // Read the response data. We need to get the client token, access token, and the selected // profile. QLOG_DEBUG() << "Processing authentication response."; + // QLOG_DEBUG() << responseData; // If we already have a client token, make sure the one the server gave us matches our // existing one. QString clientToken = responseData.value("clientToken").toString(""); @@ -80,7 +78,7 @@ bool RefreshTask::processResponse(QJsonObject responseData) QLOG_ERROR() << "Server didn't send a client token."; return false; } - if (!account->clientToken().isEmpty() && clientToken != account->clientToken()) + if (!m_account->m_clientToken.isEmpty() && clientToken != m_account->m_clientToken) { // The server changed our client token! Obey its wishes, but complain. That's what I do // for my parents, so... @@ -104,7 +102,7 @@ bool RefreshTask::processResponse(QJsonObject responseData) // profile) QJsonObject currentProfile = responseData.value("selectedProfile").toObject(); QString currentProfileId = currentProfile.value("id").toString(""); - if (account->currentProfile()->id() != currentProfileId) + if (m_account->currentProfile()->id != currentProfileId) { // TODO: Set an error to display to the user. QLOG_ERROR() << "Server didn't specify the same selected profile as ours."; @@ -114,26 +112,26 @@ bool RefreshTask::processResponse(QJsonObject responseData) // this is what the vanilla launcher passes to the userProperties launch param if (responseData.contains("user")) { + User u; auto obj = responseData.value("user").toObject(); - auto userId = obj.value("id").toString(); + u.id = obj.value("id").toString(); auto propArray = obj.value("properties").toArray(); - QLOG_DEBUG() << "User ID: " << userId; - QLOG_DEBUG() << "User Properties: "; for (auto prop : propArray) { auto propTuple = prop.toObject(); auto name = propTuple.value("name").toString(); auto value = propTuple.value("value").toString(); - QLOG_DEBUG() << name << " : " << value; + u.properties.insert(name, value); } + m_account->m_user = u; } + // We've made it through the minefield of possible errors. Return true to indicate that // we've succeeded. QLOG_DEBUG() << "Finished reading refresh response."; // Reset the access token. - account->setAccessToken(accessToken); - account->propagateChange(); + m_account->m_accessToken = accessToken; return true; } @@ -147,9 +145,9 @@ QString RefreshTask::getStateMessage(const YggdrasilTask::State state) const switch (state) { case STATE_SENDING_REQUEST: - return tr("Refreshing: Sending request."); + return tr("Refreshing login token."); case STATE_PROCESSING_RESPONSE: - return tr("Refreshing: Processing response."); + return tr("Refreshing login token: Processing response."); default: return YggdrasilTask::getStateMessage(state); } diff --git a/logic/auth/flows/RefreshTask.h b/logic/auth/flows/RefreshTask.h index 2596f6c7..2fd50c60 100644 --- a/logic/auth/flows/RefreshTask.h +++ b/logic/auth/flows/RefreshTask.h @@ -30,7 +30,7 @@ class RefreshTask : public YggdrasilTask { Q_OBJECT public: - RefreshTask(MojangAccountPtr account, QObject *parent = 0); + RefreshTask(MojangAccount * account, QObject *parent = 0); protected: virtual QJsonObject getRequestContent() const; diff --git a/logic/auth/flows/ValidateTask.cpp b/logic/auth/flows/ValidateTask.cpp index d9e0e46b..84d5e703 100644 --- a/logic/auth/flows/ValidateTask.cpp +++ b/logic/auth/flows/ValidateTask.cpp @@ -26,7 +26,7 @@ #include "logger/QsLog.h" -ValidateTask::ValidateTask(MojangAccountPtr account, QObject *parent) +ValidateTask::ValidateTask(MojangAccount * account, QObject *parent) : YggdrasilTask(account, parent) { } @@ -34,7 +34,7 @@ ValidateTask::ValidateTask(MojangAccountPtr account, QObject *parent) QJsonObject ValidateTask::getRequestContent() const { QJsonObject req; - req.insert("accessToken", getMojangAccount()->accessToken()); + req.insert("accessToken", m_account->m_accessToken); return req; } diff --git a/logic/auth/flows/ValidateTask.h b/logic/auth/flows/ValidateTask.h index 3ff78c6a..0e34f0c3 100644 --- a/logic/auth/flows/ValidateTask.h +++ b/logic/auth/flows/ValidateTask.h @@ -13,6 +13,10 @@ * limitations under the License. */ +/* + * :FIXME: DEAD CODE, DEAD CODE, DEAD CODE! :FIXME: + */ + #pragma once #include <logic/auth/YggdrasilTask.h> @@ -28,7 +32,7 @@ class ValidateTask : public YggdrasilTask { Q_OBJECT public: - ValidateTask(MojangAccountPtr account, QObject *parent = 0); + ValidateTask(MojangAccount *account, QObject *parent = 0); protected: virtual QJsonObject getRequestContent() const; diff --git a/logic/lists/JavaVersionList.cpp b/logic/lists/JavaVersionList.cpp index 3dc1969b..d2f0972c 100644 --- a/logic/lists/JavaVersionList.cpp +++ b/logic/lists/JavaVersionList.cpp @@ -21,7 +21,8 @@ #include <QRegExp> #include "logger/QsLog.h" -#include <logic/JavaUtils.h> +#include "logic/JavaCheckerJob.h" +#include "logic/JavaUtils.h" JavaVersionList::JavaVersionList(QObject *parent) : BaseVersionList(parent) { @@ -49,7 +50,7 @@ int JavaVersionList::count() const int JavaVersionList::columnCount(const QModelIndex &parent) const { - return 4; + return 3; } QVariant JavaVersionList::data(const QModelIndex &index, int role) const @@ -75,9 +76,6 @@ QVariant JavaVersionList::data(const QModelIndex &index, int role) const case 2: return version->path; - case 3: - return version->recommended ? tr("Yes") : tr("No"); - default: return QVariant(); } @@ -109,9 +107,6 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i case 2: return "Path"; - case 3: - return "Recommended"; - default: return QVariant(); } @@ -128,9 +123,6 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i case 2: return "Path to this Java version."; - case 3: - return "Whether the version is recommended or not."; - default: return QVariant(); } @@ -142,15 +134,15 @@ QVariant JavaVersionList::headerData(int section, Qt::Orientation orientation, i BaseVersionPtr JavaVersionList::getTopRecommended() const { - for (int i = 0; i < m_vlist.length(); i++) + auto first = m_vlist.first(); + if(first != nullptr) { - auto ver = std::dynamic_pointer_cast<JavaVersion>(m_vlist.at(i)); - if (ver->recommended) - { - return m_vlist.at(i); - } + return first; + } + else + { + return BaseVersionPtr(); } - return BaseVersionPtr(); } void JavaVersionList::updateListData(QList<BaseVersionPtr> versions) @@ -183,20 +175,63 @@ void JavaListLoadTask::executeTask() setStatus("Detecting Java installations..."); JavaUtils ju; - QList<JavaVersionPtr> javas = ju.FindJavaPaths(); + QList<QString> candidate_paths = ju.FindJavaPaths(); + + auto job = new JavaCheckerJob("Java detection"); + connect(job, SIGNAL(finished(QList<JavaCheckResult>)), this, SLOT(javaCheckerFinished(QList<JavaCheckResult>))); + connect(job, SIGNAL(progress(int, int)), this, SLOT(checkerProgress(int, int))); + + QLOG_DEBUG() << "Probing the following Java paths: "; + for(QString candidate : candidate_paths) + { + QLOG_DEBUG() << " " << candidate; + + auto candidate_checker = new JavaChecker(); + candidate_checker->path = candidate; + job->addJavaCheckerAction(JavaCheckerPtr(candidate_checker)); + } + + job->start(); +} + +void JavaListLoadTask::checkerProgress(int current, int total) +{ + float progress = (current * 100.0) / total; + this->setProgress((int) progress); +} + +void JavaListLoadTask::javaCheckerFinished(QList<JavaCheckResult> results) +{ + QList<JavaVersionPtr> candidates; + + QLOG_DEBUG() << "Found the following valid Java installations:"; + for(JavaCheckResult result : results) + { + if(result.valid) + { + JavaVersionPtr javaVersion(new JavaVersion()); + + javaVersion->id = result.javaVersion; + javaVersion->arch = result.mojangPlatform; + javaVersion->path = result.path; + candidates.append(javaVersion); + + QLOG_DEBUG() << " " << javaVersion->id << javaVersion->arch << javaVersion->path; + } + } QList<BaseVersionPtr> javas_bvp; - for (int i = 0; i < javas.length(); i++) + for (auto &java : candidates) { - BaseVersionPtr java = std::dynamic_pointer_cast<BaseVersion>(javas.at(i)); + //QLOG_INFO() << java->id << java->arch << " at " << java->path; + BaseVersionPtr bp_java = std::dynamic_pointer_cast<BaseVersion>(java); - if (java) + if (bp_java) { - javas_bvp.append(java); + javas_bvp.append(bp_java); } } m_list->updateListData(javas_bvp); - emitSucceeded(); } diff --git a/logic/lists/JavaVersionList.h b/logic/lists/JavaVersionList.h index f816c932..879b2480 100644 --- a/logic/lists/JavaVersionList.h +++ b/logic/lists/JavaVersionList.h @@ -20,6 +20,7 @@ #include "BaseVersionList.h" #include "logic/tasks/Task.h" +#include "logic/JavaCheckerJob.h" class JavaListLoadTask; @@ -43,7 +44,6 @@ struct JavaVersion : public BaseVersion QString id; QString arch; QString path; - bool recommended; }; typedef std::shared_ptr<JavaVersion> JavaVersionPtr; @@ -85,6 +85,9 @@ public: ~JavaListLoadTask(); virtual void executeTask(); +public slots: + void javaCheckerFinished(QList<JavaCheckResult> results); + void checkerProgress(int current, int total); protected: JavaVersionList *m_list; diff --git a/logic/lists/MinecraftVersionList.cpp b/logic/lists/MinecraftVersionList.cpp index 2a9c0d3b..523b81ac 100644 --- a/logic/lists/MinecraftVersionList.cpp +++ b/logic/lists/MinecraftVersionList.cpp @@ -15,6 +15,7 @@ #include "MinecraftVersionList.h" #include "MultiMC.h" +#include "logic/net/URLConstants.h" #include <QtXml> @@ -28,10 +29,6 @@ #include <QtNetwork> -#define MCVLIST_URLBASE "http://s3.amazonaws.com/Minecraft.Download/versions/" -#define ASSETS_URLBASE "http://assets.minecraft.net/" -#define MCN_URLBASE "http://sonicrules.org/mcnweb.py" - MinecraftVersionList::MinecraftVersionList(QObject *parent) : BaseVersionList(parent) { } @@ -144,7 +141,7 @@ void MCVListLoadTask::executeTask() { setStatus("Loading instance version list..."); auto worker = MMC->qnam(); - vlistReply = worker->get(QNetworkRequest(QUrl(QString(MCVLIST_URLBASE) + "versions.json"))); + vlistReply = worker->get(QNetworkRequest(QUrl("http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + "versions.json"))); connect(vlistReply, SIGNAL(finished()), this, SLOT(list_downloaded())); } @@ -270,7 +267,7 @@ void MCVListLoadTask::list_downloaded() continue; } // Get the download URL. - QString dlUrl = QString(MCVLIST_URLBASE) + versionID + "/"; + QString dlUrl = "http://" + URLConstants::AWS_DOWNLOAD_VERSIONS + versionID + "/"; // Now, we construct the version object and add it to the list. std::shared_ptr<MinecraftVersion> mcVersion(new MinecraftVersion()); diff --git a/logic/net/FileDownload.cpp b/logic/net/MD5EtagDownload.cpp index 239af351..4b9f52af 100644 --- a/logic/net/FileDownload.cpp +++ b/logic/net/MD5EtagDownload.cpp @@ -14,12 +14,12 @@ */ #include "MultiMC.h" -#include "FileDownload.h" +#include "MD5EtagDownload.h" #include <pathutils.h> #include <QCryptographicHash> #include "logger/QsLog.h" -FileDownload::FileDownload(QUrl url, QString target_path) : NetAction() +MD5EtagDownload::MD5EtagDownload(QUrl url, QString target_path) : NetAction() { m_url = url; m_target_path = target_path; @@ -27,7 +27,7 @@ FileDownload::FileDownload(QUrl url, QString target_path) : NetAction() m_status = Job_NotStarted; } -void FileDownload::start() +void MD5EtagDownload::start() { QString filename = m_target_path; m_output_file.setFileName(filename); @@ -58,11 +58,20 @@ void FileDownload::start() return; } - QLOG_INFO() << "Downloading " << m_url.toString(); + QLOG_INFO() << "Downloading " << m_url.toString() << " expecting " << m_expected_md5; QNetworkRequest request(m_url); request.setRawHeader(QString("If-None-Match").toLatin1(), m_expected_md5.toLatin1()); request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); + // Go ahead and try to open the file. + // If we don't do this, empty files won't be created, which breaks the updater. + // Plus, this way, we don't end up starting a download for a file we can't open. + if (!m_output_file.open(QIODevice::WriteOnly)) + { + emit failed(index_within_job); + return; + } + auto worker = MMC->qnam(); QNetworkReply *rep = worker->get(request); @@ -75,19 +84,19 @@ void FileDownload::start() connect(rep, SIGNAL(readyRead()), SLOT(downloadReadyRead())); } -void FileDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) +void MD5EtagDownload::downloadProgress(qint64 bytesReceived, qint64 bytesTotal) { emit progress(index_within_job, bytesReceived, bytesTotal); } -void FileDownload::downloadError(QNetworkReply::NetworkError error) +void MD5EtagDownload::downloadError(QNetworkReply::NetworkError error) { // error happened during download. // TODO: log the reason why m_status = Job_Failed; } -void FileDownload::downloadFinished() +void MD5EtagDownload::downloadFinished() { // if the download succeeded if (m_status != Job_Failed) @@ -96,6 +105,7 @@ void FileDownload::downloadFinished() m_status = Job_Finished; m_output_file.close(); + QLOG_INFO() << "Finished " << m_url.toString() << " got " << m_reply->rawHeader("ETag").constData(); m_reply.reset(); emit succeeded(index_within_job); return; @@ -110,7 +120,7 @@ void FileDownload::downloadFinished() } } -void FileDownload::downloadReadyRead() +void MD5EtagDownload::downloadReadyRead() { if (!m_output_file.isOpen()) { diff --git a/logic/net/FileDownload.h b/logic/net/MD5EtagDownload.h index 58380e86..416ab9de 100644 --- a/logic/net/FileDownload.h +++ b/logic/net/MD5EtagDownload.h @@ -18,8 +18,8 @@ #include "NetAction.h" #include <QFile> -typedef std::shared_ptr<class FileDownload> FileDownloadPtr; -class FileDownload : public NetAction +typedef std::shared_ptr<class MD5EtagDownload> Md5EtagDownloadPtr; +class MD5EtagDownload : public NetAction { Q_OBJECT public: @@ -35,10 +35,10 @@ public: QFile m_output_file; public: - explicit FileDownload(QUrl url, QString target_path); - static FileDownloadPtr make(QUrl url, QString target_path) + explicit MD5EtagDownload(QUrl url, QString target_path); + static Md5EtagDownloadPtr make(QUrl url, QString target_path) { - return FileDownloadPtr(new FileDownload(url, target_path)); + return Md5EtagDownloadPtr(new MD5EtagDownload(url, target_path)); } protected slots: diff --git a/logic/net/NetJob.cpp b/logic/net/NetJob.cpp index 333cdcbf..8b79bc54 100644 --- a/logic/net/NetJob.cpp +++ b/logic/net/NetJob.cpp @@ -16,7 +16,7 @@ #include "NetJob.h" #include "pathutils.h" #include "MultiMC.h" -#include "FileDownload.h" +#include "MD5EtagDownload.h" #include "ByteArrayDownload.h" #include "CacheDownload.h" diff --git a/logic/net/NetJob.h b/logic/net/NetJob.h index 021a1550..6e2e7607 100644 --- a/logic/net/NetJob.h +++ b/logic/net/NetJob.h @@ -18,7 +18,7 @@ #include <QLabel> #include "NetAction.h" #include "ByteArrayDownload.h" -#include "FileDownload.h" +#include "MD5EtagDownload.h" #include "CacheDownload.h" #include "HttpMetaCache.h" #include "ForgeXzDownload.h" @@ -94,6 +94,8 @@ signals: public slots: virtual void start(); + // FIXME: implement + virtual void abort() {}; private slots: void partProgress(int index, qint64 bytesReceived, qint64 bytesTotal); diff --git a/logic/net/PasteUpload.cpp b/logic/net/PasteUpload.cpp new file mode 100644 index 00000000..acf09291 --- /dev/null +++ b/logic/net/PasteUpload.cpp @@ -0,0 +1,84 @@ +#include "PasteUpload.h" +#include "MultiMC.h" +#include "logger/QsLog.h" +#include <QJsonObject> +#include <QJsonDocument> +#include "gui/dialogs/CustomMessageBox.h" +#include <QDesktopServices> + +PasteUpload::PasteUpload(QWidget *window, QString text) : m_text(text), m_window(window) +{ +} + +void PasteUpload::executeTask() +{ + QNetworkRequest request(QUrl("http://paste.ee/api")); + request.setHeader(QNetworkRequest::UserAgentHeader, "MultiMC/5.0 (Uncached)"); + QByteArray content( + "key=public&description=MultiMC5+Log+File&language=plain&format=json&paste=" + + m_text.toUtf8()); + request.setRawHeader("Content-Type", "application/x-www-form-urlencoded"); + request.setRawHeader("Content-Length", QByteArray::number(content.size())); + + auto worker = MMC->qnam(); + QNetworkReply *rep = worker->post(request, content); + + m_reply = std::shared_ptr<QNetworkReply>(rep); + connect(rep, &QNetworkReply::downloadProgress, [&](qint64 value, qint64 max) + { setProgress(value / max * 100); }); + connect(rep, SIGNAL(error(QNetworkReply::NetworkError)), this, + SLOT(downloadError(QNetworkReply::NetworkError))); + connect(rep, SIGNAL(finished()), this, SLOT(downloadFinished())); +} + +void PasteUpload::downloadError(QNetworkReply::NetworkError error) +{ + // error happened during download. + QLOG_ERROR() << "Network error: " << error; + emitFailed(m_reply->errorString()); +} + +void PasteUpload::downloadFinished() +{ + // if the download succeeded + if (m_reply->error() == QNetworkReply::NetworkError::NoError) + { + QByteArray data = m_reply->readAll(); + m_reply.reset(); + QJsonParseError jsonError; + QJsonDocument doc = QJsonDocument::fromJson(data, &jsonError); + if (jsonError.error != QJsonParseError::NoError) + { + emitFailed(jsonError.errorString()); + return; + } + QString error; + if (!parseResult(doc, &error)) + { + emitFailed(error); + return; + } + } + // else the download failed + else + { + emitFailed(QString("Network error: %1").arg(m_reply->errorString())); + m_reply.reset(); + return; + } + emitSucceeded(); +} + +bool PasteUpload::parseResult(QJsonDocument doc, QString *parseError) +{ + auto object = doc.object(); + auto status = object.value("status").toString("error"); + if (status == "error") + { + parseError = new QString(object.value("error").toString()); + return false; + } + QString pasteUrl = object.value("paste").toObject().value("link").toString(); + QDesktopServices::openUrl(pasteUrl); + return true; +} diff --git a/logic/net/PasteUpload.h b/logic/net/PasteUpload.h new file mode 100644 index 00000000..917a0016 --- /dev/null +++ b/logic/net/PasteUpload.h @@ -0,0 +1,26 @@ +#pragma once +#include "logic/tasks/Task.h" +#include <QMessageBox> +#include <QNetworkReply> +#include <memory> + +class PasteUpload : public Task +{ + Q_OBJECT +public: + PasteUpload(QWidget *window, QString text); + +protected: + virtual void executeTask(); + +private: + bool parseResult(QJsonDocument doc, QString *parseError); + QString m_text; + QString m_error; + QWidget *m_window; + std::shared_ptr<QNetworkReply> m_reply; +public +slots: + void downloadError(QNetworkReply::NetworkError); + void downloadFinished(); +}; diff --git a/logic/net/S3ListBucket.cpp b/logic/net/S3ListBucket.cpp index 89dff951..439b7086 100644 --- a/logic/net/S3ListBucket.cpp +++ b/logic/net/S3ListBucket.cpp @@ -102,7 +102,6 @@ void S3ListBucket::processValidReply() }; // nothing went wrong... - QString prefix("http://s3.amazonaws.com/Minecraft.Resources/"); QByteArray ba = m_reply->readAll(); QString xmlErrorMsg; diff --git a/logic/net/URLConstants.h b/logic/net/URLConstants.h new file mode 100644 index 00000000..dcd5c2b1 --- /dev/null +++ b/logic/net/URLConstants.h @@ -0,0 +1,32 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include <QString> + +namespace URLConstants +{ +const QString AWS_DOWNLOAD_BASE("s3.amazonaws.com/Minecraft.Download/"); +const QString AWS_DOWNLOAD_VERSIONS(AWS_DOWNLOAD_BASE + "versions/"); +const QString AWS_DOWNLOAD_LIBRARIES(AWS_DOWNLOAD_BASE + "libraries/"); +const QString AWS_DOWNLOAD_INDEXES(AWS_DOWNLOAD_BASE + "indexes/"); +const QString ASSETS_BASE("assets.minecraft.net/"); +//const QString MCN_BASE("sonicrules.org/mcnweb.py"); +const QString RESOURCE_BASE("resources.download.minecraft.net/"); +const QString LIBRARY_BASE("libraries.minecraft.net/"); +const QString SKINS_BASE("skins.minecraft.net/MinecraftSkins/"); +const QString AUTH_BASE("authserver.mojang.com/"); +} diff --git a/logic/tasks/ProgressProvider.h b/logic/tasks/ProgressProvider.h index f6f2906a..15e453a3 100644 --- a/logic/tasks/ProgressProvider.h +++ b/logic/tasks/ProgressProvider.h @@ -38,4 +38,5 @@ public: public slots: virtual void start() = 0; + virtual void abort() = 0; }; diff --git a/logic/tasks/Task.h b/logic/tasks/Task.h index d08ef560..80d5e38b 100644 --- a/logic/tasks/Task.h +++ b/logic/tasks/Task.h @@ -44,6 +44,7 @@ public: public slots: virtual void start(); + virtual void abort() {}; protected: virtual void executeTask() = 0; diff --git a/logic/updater/DownloadUpdateTask.cpp b/logic/updater/DownloadUpdateTask.cpp new file mode 100644 index 00000000..d9aab826 --- /dev/null +++ b/logic/updater/DownloadUpdateTask.cpp @@ -0,0 +1,404 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "DownloadUpdateTask.h" + +#include "MultiMC.h" +#include "logic/updater/UpdateChecker.h" +#include "logic/net/NetJob.h" +#include "pathutils.h" + +#include <QFile> +#include <QTemporaryDir> +#include <QCryptographicHash> + +#include <QDomDocument> + + +DownloadUpdateTask::DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent) : + Task(parent) +{ + m_cVersionId = MMC->version().build; + + m_nRepoUrl = repoUrl; + m_nVersionId = versionId; + + m_updateFilesDir.setAutoRemove(false); +} + +void DownloadUpdateTask::executeTask() +{ + // GO! + // This will call the next step when it's done. + findCurrentVersionInfo(); +} + +void DownloadUpdateTask::findCurrentVersionInfo() +{ + setStatus(tr("Finding information about the current version.")); + + auto checker = MMC->updateChecker(); + + // This runs after we've tried loading the channel list. + // If the channel list doesn't need to be loaded, this will be called immediately. + // If the channel list does need to be loaded, this will be called when it's done. + auto processFunc = [this, &checker] () -> void + { + // Now, check the channel list again. + if (checker->hasChannels()) + { + // We still couldn't load the channel list. Give up. Call loadVersionInfo and return. + QLOG_INFO() << "Reloading the channel list didn't work. Giving up."; + loadVersionInfo(); + return; + } + + QList<UpdateChecker::ChannelListEntry> channels = checker->getChannelList(); + QString channelId = MMC->version().channel; + + // Search through the channel list for a channel with the correct ID. + for (auto channel : channels) + { + if (channel.id == channelId) + { + QLOG_INFO() << "Found matching channel."; + m_cRepoUrl = channel.url; + break; + } + } + + // Now that we've done that, load version info. + loadVersionInfo(); + }; + + if (checker->hasChannels()) + { + // Load the channel list and wait for it to finish loading. + QLOG_INFO() << "No channel list entries found. Will try reloading it."; + + QObject::connect(checker.get(), &UpdateChecker::channelListLoaded, processFunc); + checker->updateChanList(); + } + else + { + processFunc(); + } +} + +void DownloadUpdateTask::loadVersionInfo() +{ + setStatus(tr("Loading version information.")); + + // Create the net job for loading version info. + NetJob* netJob = new NetJob("Version Info"); + + // Find the index URL. + QUrl newIndexUrl = QUrl(m_nRepoUrl).resolved(QString::number(m_nVersionId) + ".json"); + + // Add a net action to download the version info for the version we're updating to. + netJob->addNetAction(ByteArrayDownload::make(newIndexUrl)); + + // If we have a current version URL, get that one too. + if (!m_cRepoUrl.isEmpty()) + { + QUrl cIndexUrl = QUrl(m_cRepoUrl).resolved(QString::number(m_cVersionId) + ".json"); + netJob->addNetAction(ByteArrayDownload::make(cIndexUrl)); + } + + // Connect slots so we know when it's done. + QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::vinfoDownloadFinished); + QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::vinfoDownloadFailed); + + // Store the NetJob in a class member. We don't want to lose it! + m_vinfoNetJob.reset(netJob); + + // Finally, we start the network job and the thread's event loop to wait for it to finish. + netJob->start(); +} + +void DownloadUpdateTask::vinfoDownloadFinished() +{ + // Both downloads succeeded. OK. Parse stuff. + parseDownloadedVersionInfo(); +} + +void DownloadUpdateTask::vinfoDownloadFailed() +{ + // Something failed. We really need the second download (current version info), so parse downloads anyways as long as the first one succeeded. + if (m_vinfoNetJob->first()->m_status != Job_Failed) + { + parseDownloadedVersionInfo(); + return; + } + + // TODO: Give a more detailed error message. + QLOG_ERROR() << "Failed to download version info files."; + emitFailed(tr("Failed to download version info files.")); +} + +void DownloadUpdateTask::parseDownloadedVersionInfo() +{ + setStatus(tr("Reading file lists.")); + + parseVersionInfo(NEW_VERSION, &m_nVersionFileList); + + // If there is a second entry in the network job's list, load it as the current version's info. + if (m_vinfoNetJob->size() >= 2 && m_vinfoNetJob->operator[](1)->m_status != Job_Failed) + { + parseVersionInfo(CURRENT_VERSION, &m_cVersionFileList); + } + + // We don't need this any more. + m_vinfoNetJob.reset(); + + // Now that we're done loading version info, we can move on to the next step. Process file lists and download files. + processFileLists(); +} + +void DownloadUpdateTask::parseVersionInfo(VersionInfoFileEnum vfile, VersionFileList* list) +{ + if (vfile == CURRENT_VERSION) setStatus(tr("Reading file list for current version.")); + else if (vfile == NEW_VERSION) setStatus(tr("Reading file list for new version.")); + + QLOG_DEBUG() << "Reading file list for" << (vfile == NEW_VERSION ? "new" : "current") << "version."; + + QByteArray data; + { + ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>( + vfile == NEW_VERSION ? m_vinfoNetJob->first() : m_vinfoNetJob->operator[](1)); + data = dl->m_data; + } + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + if (jsonError.error != QJsonParseError::NoError) + { + QLOG_ERROR() << "Failed to parse version info JSON:" << jsonError.errorString() << "at" << jsonError.offset; + return; + } + + QJsonObject json = jsonDoc.object(); + + QLOG_DEBUG() << "Loading version info from JSON."; + QJsonArray filesArray = json.value("Files").toArray(); + for (QJsonValue fileValue : filesArray) + { + QJsonObject fileObj = fileValue.toObject(); + + VersionFileEntry file{ + fileObj.value("Path").toString(), + fileObj.value("Perms").toVariant().toInt(), + FileSourceList(), + fileObj.value("MD5").toString(), + }; + QLOG_DEBUG() << "File" << file.path << "with perms" << file.mode; + + QJsonArray sourceArray = fileObj.value("Sources").toArray(); + for (QJsonValue val : sourceArray) + { + QJsonObject sourceObj = val.toObject(); + + QString type = sourceObj.value("SourceType").toString(); + if (type == "http") + { + file.sources.append(FileSource("http", sourceObj.value("Url").toString())); + } + else if (type == "httpc") + { + file.sources.append(FileSource("httpc", sourceObj.value("Url").toString(), sourceObj.value("CompressionType").toString())); + } + else + { + QLOG_WARN() << "Unknown source type" << type << "ignored."; + } + } + + QLOG_DEBUG() << "Loaded info for" << file.path; + + list->append(file); + } +} + +void DownloadUpdateTask::processFileLists() +{ + setStatus(tr("Processing file lists. Figuring out how to install the update.")); + + // First, if we've loaded the current version's file list, we need to iterate through it and + // delete anything in the current one version's list that isn't in the new version's list. + for (VersionFileEntry entry : m_cVersionFileList) + { + bool keep = false; + for (VersionFileEntry newEntry : m_nVersionFileList) + { + if (newEntry.path == entry.path) + { + QLOG_DEBUG() << "Not deleting" << entry.path << "because it is still present in the new version."; + keep = true; + break; + } + } + // If the loop reaches the end and we didn't find a match, delete the file. + if(!keep) + m_operationList.append(UpdateOperation::DeleteOp(entry.path)); + } + + // Create a network job for downloading files. + NetJob* netJob = new NetJob("Update Files"); + + // Next, check each file in MultiMC's folder and see if we need to update them. + for (VersionFileEntry entry : m_nVersionFileList) + { + // TODO: Let's not MD5sum a ton of files on the GUI thread. We should probably find a way to do this in the background. + QString fileMD5; + QFile entryFile(entry.path); + if (entryFile.open(QFile::ReadOnly)) + { + QCryptographicHash hash(QCryptographicHash::Md5); + hash.addData(entryFile.readAll()); + fileMD5 = hash.result().toHex(); + } + + if (!entryFile.exists() || fileMD5.isEmpty() || fileMD5 != entry.md5) + { + QLOG_DEBUG() << "Found file" << entry.path << "that needs updating."; + + // Go through the sources list and find one to use. + // TODO: Make a NetAction that takes a source list and tries each of them until one works. For now, we'll just use the first http one. + for (FileSource source : entry.sources) + { + if (source.type == "http") + { + QLOG_DEBUG() << "Will download" << entry.path << "from" << source.url; + + // Download it to updatedir/<filepath>-<md5> where filepath is the file's path with slashes replaced by underscores. + QString dlPath = PathCombine(m_updateFilesDir.path(), QString(entry.path).replace("/", "_")); + + // We need to download the file to the updatefiles folder and add a task to copy it to its install path. + auto download = MD5EtagDownload::make(source.url, dlPath); + download->m_check_md5 = true; + download->m_expected_md5 = entry.md5; + netJob->addNetAction(download); + + // Now add a copy operation to our operations list to install the file. + m_operationList.append(UpdateOperation::CopyOp(dlPath, entry.path, entry.mode)); + } + } + } + } + + // Add listeners to wait for the downloads to finish. + QObject::connect(netJob, &NetJob::succeeded, this, &DownloadUpdateTask::fileDownloadFinished); + QObject::connect(netJob, &NetJob::progress, this, &DownloadUpdateTask::fileDownloadProgressChanged); + QObject::connect(netJob, &NetJob::failed, this, &DownloadUpdateTask::fileDownloadFailed); + + // Now start the download. + setStatus(tr("Downloading %1 update files.").arg(QString::number(netJob->size()))); + QLOG_DEBUG() << "Begin downloading update files to" << m_updateFilesDir.path(); + m_filesNetJob.reset(netJob); + netJob->start(); + + writeInstallScript(m_operationList, PathCombine(m_updateFilesDir.path(), "file_list.xml")); +} + +void DownloadUpdateTask::writeInstallScript(UpdateOperationList& opsList, QString scriptFile) +{ + // Build the base structure of the XML document. + QDomDocument doc; + + QDomElement root = doc.createElement("update"); + root.setAttribute("version", "3"); + doc.appendChild(root); + + QDomElement installFiles = doc.createElement("install"); + root.appendChild(installFiles); + + QDomElement removeFiles = doc.createElement("uninstall"); + root.appendChild(removeFiles); + + // Write the operation list to the XML document. + for (UpdateOperation op : opsList) + { + QDomElement file = doc.createElement("file"); + + switch (op.type) + { + case UpdateOperation::OP_COPY: + { + // Install the file. + QDomElement name = doc.createElement("source"); + QDomElement path = doc.createElement("dest"); + QDomElement mode = doc.createElement("mode"); + name.appendChild(doc.createTextNode(op.file)); + path.appendChild(doc.createTextNode(op.dest)); + // We need to add a 0 at the beginning here, because Qt doesn't convert to octal correctly. + mode.appendChild(doc.createTextNode("0" + QString::number(op.mode, 8))); + file.appendChild(name); + file.appendChild(path); + file.appendChild(mode); + installFiles.appendChild(file); + QLOG_DEBUG() << "Will install file" << op.file; + } + break; + + case UpdateOperation::OP_DELETE: + { + // Delete the file. + file.appendChild(doc.createTextNode(op.file)); + removeFiles.appendChild(file); + QLOG_DEBUG() << "Will remove file" << op.file; + } + break; + + default: + QLOG_WARN() << "Can't write update operation of type" << op.type << "to file. Not implemented."; + continue; + } + } + + // Write the XML document to the file. + QFile outFile(scriptFile); + + if (outFile.open(QIODevice::WriteOnly)) + { + outFile.write(doc.toByteArray()); + } + else + { + emitFailed(tr("Failed to write update script file.")); + } +} + +void DownloadUpdateTask::fileDownloadFinished() +{ + emitSucceeded(); +} + +void DownloadUpdateTask::fileDownloadFailed() +{ + // TODO: Give more info about the failure. + QLOG_ERROR() << "Failed to download update files."; + emitFailed(tr("Failed to download update files.")); +} + +void DownloadUpdateTask::fileDownloadProgressChanged(qint64 current, qint64 total) +{ + setProgress((int)(((float)current / (float)total)*100)); +} + +QString DownloadUpdateTask::updateFilesDir() +{ + return m_updateFilesDir.path(); +} + diff --git a/logic/updater/DownloadUpdateTask.h b/logic/updater/DownloadUpdateTask.h new file mode 100644 index 00000000..f5b23d12 --- /dev/null +++ b/logic/updater/DownloadUpdateTask.h @@ -0,0 +1,192 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "logic/tasks/Task.h" +#include "logic/net/NetJob.h" + +/*! + * The DownloadUpdateTask is a task that takes a given version ID and repository URL, + * downloads that version's files from the repository, and prepares to install them. + */ +class DownloadUpdateTask : public Task +{ + Q_OBJECT + +public: + explicit DownloadUpdateTask(QString repoUrl, int versionId, QObject* parent=0); + + /*! + * Gets the directory that contains the update files. + */ + QString updateFilesDir(); + +protected: + // TODO: We should probably put these data structures into a separate header... + + /*! + * Struct that describes an entry in a VersionFileEntry's `Sources` list. + */ + struct FileSource + { + FileSource(QString type, QString url, QString compression="") + { + this->type = type; + this->url = url; + this->compressionType = compression; + } + + QString type; + QString url; + QString compressionType; + }; + + typedef QList<FileSource> FileSourceList; + + /*! + * Structure that describes an entry in a GoUpdate version's `Files` list. + */ + struct VersionFileEntry + { + QString path; + int mode; + FileSourceList sources; + QString md5; + }; + + typedef QList<VersionFileEntry> VersionFileList; + + + /*! + * Structure that describes an operation to perform when installing updates. + */ + struct UpdateOperation + { + static UpdateOperation CopyOp(QString fsource, QString fdest, int fmode=0644) { return UpdateOperation{OP_COPY, fsource, fdest, fmode}; } + static UpdateOperation MoveOp(QString fsource, QString fdest, int fmode=0644) { return UpdateOperation{OP_MOVE, fsource, fdest, fmode}; } + static UpdateOperation DeleteOp(QString file) { return UpdateOperation{OP_DELETE, file, "", 0644}; } + static UpdateOperation ChmodOp(QString file, int fmode) { return UpdateOperation{OP_CHMOD, file, "", fmode}; } + + //! Specifies the type of operation that this is. + enum Type + { + OP_COPY, + OP_DELETE, + OP_MOVE, + OP_CHMOD, + } type; + + //! The file to operate on. If this is a DELETE or CHMOD operation, this is the file that will be modified. + QString file; + + //! The destination file. If this is a DELETE or CHMOD operation, this field will be ignored. + QString dest; + + //! The mode to change the source file to. Ignored if this isn't a CHMOD operation. + int mode; + + // Yeah yeah, polymorphism blah blah inheritance, blah blah object oriented. I'm lazy, OK? + }; + + typedef QList<UpdateOperation> UpdateOperationList; + + /*! + * Used for arguments to parseVersionInfo and friends to specify which version info file to parse. + */ + enum VersionInfoFileEnum { NEW_VERSION, CURRENT_VERSION }; + + + //! Entry point for tasks. + virtual void executeTask(); + + /*! + * Attempts to find the version ID and repository URL for the current version. + * The function will look up the repository URL in the UpdateChecker's channel list. + * If the repository URL can't be found, this function will return false. + */ + virtual void findCurrentVersionInfo(); + + /*! + * Downloads the version info files from the repository. + * The files for both the current build, and the build that we're updating to need to be downloaded. + * If the current version's info file can't be found, MultiMC will not delete files that + * were removed between versions. It will still replace files that have changed, however. + * Note that although the repository URL for the current version is not given to the update task, + * the task will attempt to look it up in the UpdateChecker's channel list. + * If an error occurs here, the function will call emitFailed and return false. + */ + virtual void loadVersionInfo(); + + /*! + * This function is called when version information is finished downloading. + * This handles parsing the JSON downloaded by the version info network job and then calls processFileLists. + * Note that this function will sometimes be called even if the version info download emits failed. If + * we couldn't download the current version's info file, we can still update. This will be called even if the + * current version's info file fails to download, as long as the new version's info file succeeded. + */ + virtual void parseDownloadedVersionInfo(); + + /*! + * Loads the file list from the given version info JSON object into the given list. + */ + virtual void parseVersionInfo(VersionInfoFileEnum vfile, VersionFileList* list); + + /*! + * Takes a list of file entries for the current version's files and the new version's files + * and populates the downloadList and operationList with information about how to download and install the update. + */ + virtual void processFileLists(); + + /*! + * Takes the operations list and writes an install script for the updater to the update files directory. + */ + virtual void writeInstallScript(UpdateOperationList& opsList, QString scriptFile); + + VersionFileList m_downloadList; + UpdateOperationList m_operationList; + + VersionFileList m_nVersionFileList; + VersionFileList m_cVersionFileList; + + //! Network job for downloading version info files. + NetJobPtr m_vinfoNetJob; + + //! Network job for downloading update files. + NetJobPtr m_filesNetJob; + + // Version ID and repo URL for the new version. + int m_nVersionId; + QString m_nRepoUrl; + + // Version ID and repo URL for the currently installed version. + int m_cVersionId; + QString m_cRepoUrl; + + /*! + * Temporary directory to store update files in. + * This will be set to not auto delete. Task will fail if this fails to be created. + */ + QTemporaryDir m_updateFilesDir; + +protected slots: + void vinfoDownloadFinished(); + void vinfoDownloadFailed(); + + void fileDownloadFinished(); + void fileDownloadFailed(); + void fileDownloadProgressChanged(qint64 current, qint64 total); +}; + diff --git a/logic/updater/UpdateChecker.cpp b/logic/updater/UpdateChecker.cpp new file mode 100644 index 00000000..5ff1898e --- /dev/null +++ b/logic/updater/UpdateChecker.cpp @@ -0,0 +1,247 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "UpdateChecker.h" + +#include "MultiMC.h" + +#include "config.h" +#include "logger/QsLog.h" + +#include <QJsonObject> +#include <QJsonArray> +#include <QJsonValue> + +#define API_VERSION 0 +#define CHANLIST_FORMAT 0 + +UpdateChecker::UpdateChecker() +{ + m_currentChannel = VERSION_CHANNEL; + m_channelListUrl = CHANLIST_URL; + m_updateChecking = false; + m_chanListLoading = false; + m_checkUpdateWaiting = false; + m_chanListLoaded = false; +} + +QList<UpdateChecker::ChannelListEntry> UpdateChecker::getChannelList() const +{ + return m_channels; +} + +bool UpdateChecker::hasChannels() const +{ + return m_channels.isEmpty(); +} + +void UpdateChecker::checkForUpdate() +{ + QLOG_DEBUG() << "Checking for updates."; + + // If the channel list hasn't loaded yet, load it and defer checking for updates until later. + if (!m_chanListLoaded) + { + QLOG_DEBUG() << "Channel list isn't loaded yet. Loading channel list and deferring update check."; + m_checkUpdateWaiting = true; + updateChanList(); + return; + } + + if (m_updateChecking) + { + QLOG_DEBUG() << "Ignoring update check request. Already checking for updates."; + return; + } + + m_updateChecking = true; + + // Get the URL for the channel we're using. + // TODO: Allow user to select channels. For now, we'll just use the current channel. + QString updateChannel = m_currentChannel; + + // Find the desired channel within the channel list and get its repo URL. If if cannot be found, error. + m_repoUrl = ""; + for (ChannelListEntry entry : m_channels) + { + if (entry.id == updateChannel) + m_repoUrl = entry.url; + } + + // If we didn't find our channel, error. + if (m_repoUrl.isEmpty()) + { + emit updateCheckFailed(); + return; + } + + QUrl indexUrl = QUrl(m_repoUrl).resolved(QUrl("index.json")); + + auto job = new NetJob("GoUpdate Repository Index"); + job->addNetAction(ByteArrayDownload::make(indexUrl)); + connect(job, SIGNAL(succeeded()), SLOT(updateCheckFinished())); + connect(job, SIGNAL(failed()), SLOT(updateCheckFailed())); + indexJob.reset(job); + job->start(); +} + +void UpdateChecker::updateCheckFinished() +{ + QLOG_DEBUG() << "Finished downloading repo index. Checking for new versions."; + + QJsonParseError jsonError; + QByteArray data; + { + ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(indexJob->first()); + data = dl->m_data; + indexJob.reset(); + } + + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + if (jsonError.error != QJsonParseError::NoError || !jsonDoc.isObject()) + { + QLOG_ERROR() << "Failed to parse GoUpdate repository index. JSON error" << jsonError.errorString() << "at offset" << jsonError.offset; + return; + } + + QJsonObject object = jsonDoc.object(); + + bool success = false; + int apiVersion = object.value("ApiVersion").toVariant().toInt(&success); + if (apiVersion != API_VERSION || !success) + { + QLOG_ERROR() << "Failed to check for updates. API version mismatch. We're using" << API_VERSION << "server has" << apiVersion; + return; + } + + QLOG_DEBUG() << "Processing repository version list."; + QJsonObject newestVersion; + QJsonArray versions = object.value("Versions").toArray(); + for (QJsonValue versionVal : versions) + { + QJsonObject version = versionVal.toObject(); + if (newestVersion.value("Id").toVariant().toInt() < version.value("Id").toVariant().toInt()) + { + QLOG_DEBUG() << "Found newer version with ID" << version.value("Id").toVariant().toInt(); + newestVersion = version; + } + } + + // We've got the version with the greatest ID number. Now compare it to our current build number and update if they're different. + int newBuildNumber = newestVersion.value("Id").toVariant().toInt(); + if (newBuildNumber != MMC->version().build) + { + // Update! + emit updateAvailable(m_repoUrl, newestVersion.value("Name").toVariant().toString(), newBuildNumber); + } + + m_updateChecking = false; +} + +void UpdateChecker::updateCheckFailed() +{ + // TODO: log errors better + QLOG_ERROR() << "Update check failed for reasons unknown."; +} + +void UpdateChecker::updateChanList() +{ + QLOG_DEBUG() << "Loading the channel list."; + + if (m_channelListUrl.isEmpty()) + { + QLOG_ERROR() << "Failed to update channel list. No channel list URL set." + << "If you'd like to use MultiMC's update system, please pass the channel list URL to CMake at compile time."; + return; + } + + m_chanListLoading = true; + NetJob* job = new NetJob("Update System Channel List"); + job->addNetAction(ByteArrayDownload::make(QUrl(m_channelListUrl))); + QObject::connect(job, &NetJob::succeeded, this, &UpdateChecker::chanListDownloadFinished); + QObject::connect(job, &NetJob::failed, this, &UpdateChecker::chanListDownloadFailed); + chanListJob.reset(job); + job->start(); +} + +void UpdateChecker::chanListDownloadFinished() +{ + QByteArray data; + { + ByteArrayDownloadPtr dl = std::dynamic_pointer_cast<ByteArrayDownload>(chanListJob->first()); + data = dl->m_data; + chanListJob.reset(); + } + + QJsonParseError jsonError; + QJsonDocument jsonDoc = QJsonDocument::fromJson(data, &jsonError); + if (jsonError.error != QJsonParseError::NoError) + { + // TODO: Report errors to the user. + QLOG_ERROR() << "Failed to parse channel list JSON:" << jsonError.errorString() << "at" << jsonError.offset; + return; + } + + QJsonObject object = jsonDoc.object(); + + bool success = false; + int formatVersion = object.value("format_version").toVariant().toInt(&success); + if (formatVersion != CHANLIST_FORMAT || !success) + { + QLOG_ERROR() << "Failed to check for updates. Channel list format version mismatch. We're using" << CHANLIST_FORMAT << "server has" << formatVersion; + return; + } + + // Load channels into a temporary array. + QList<ChannelListEntry> loadedChannels; + QJsonArray channelArray = object.value("channels").toArray(); + for (QJsonValue chanVal : channelArray) + { + QJsonObject channelObj = chanVal.toObject(); + ChannelListEntry entry{ + channelObj.value("id").toVariant().toString(), + channelObj.value("name").toVariant().toString(), + channelObj.value("description").toVariant().toString(), + channelObj.value("url").toVariant().toString() + }; + if (entry.id.isEmpty() || entry.name.isEmpty() || entry.url.isEmpty()) + { + QLOG_ERROR() << "Channel list entry with empty ID, name, or URL. Skipping."; + continue; + } + loadedChannels.append(entry); + } + + // Swap the channel list we just loaded into the object's channel list. + m_channels.swap(loadedChannels); + + m_chanListLoading = false; + m_chanListLoaded = true; + QLOG_INFO() << "Successfully loaded UpdateChecker channel list."; + + // If we're waiting to check for updates, do that now. + if (m_checkUpdateWaiting) + checkForUpdate(); + + emit channelListLoaded(); +} + +void UpdateChecker::chanListDownloadFailed() +{ + m_chanListLoading = false; + QLOG_ERROR() << "Failed to download channel list."; + emit channelListLoaded(); +} + diff --git a/logic/updater/UpdateChecker.h b/logic/updater/UpdateChecker.h new file mode 100644 index 00000000..59fb8e47 --- /dev/null +++ b/logic/updater/UpdateChecker.h @@ -0,0 +1,106 @@ +/* Copyright 2013 MultiMC Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "logic/net/NetJob.h" + +#include <QUrl> + +class UpdateChecker : public QObject +{ + Q_OBJECT + +public: + UpdateChecker(); + void checkForUpdate(); + + /*! + * Causes the update checker to download the channel list from the URL specified in config.h (generated by CMake). + * If this isn't called before checkForUpdate(), it will automatically be called. + */ + void updateChanList(); + + /*! + * An entry in the channel list. + */ + struct ChannelListEntry + { + QString id; + QString name; + QString description; + QString url; + }; + + /*! + * Returns a the current channel list. + * If the channel list hasn't been loaded, this list will be empty. + */ + QList<ChannelListEntry> getChannelList() const; + + /*! + * Returns true if the channel list is empty. + */ + bool hasChannels() const; + +signals: + //! Signal emitted when an update is available. Passes the URL for the repo and the ID and name for the version. + void updateAvailable(QString repoUrl, QString versionName, int versionId); + + //! Signal emitted when the channel list finishes loading or fails to load. + void channelListLoaded(); + +private slots: + void updateCheckFinished(); + void updateCheckFailed(); + + void chanListDownloadFinished(); + void chanListDownloadFailed(); + +private: + NetJobPtr indexJob; + NetJobPtr chanListJob; + + QString m_repoUrl; + + QString m_channelListUrl; + QString m_currentChannel; + + QList<ChannelListEntry> m_channels; + + /*! + * True while the system is checking for updates. + * If checkForUpdate is called while this is true, it will be ignored. + */ + bool m_updateChecking; + + /*! + * True if the channel list has loaded. + * If this is false, trying to check for updates will call updateChanList first. + */ + bool m_chanListLoaded; + + /*! + * Set to true while the channel list is currently loading. + */ + bool m_chanListLoading; + + /*! + * Set to true when checkForUpdate is called while the channel list isn't loaded. + * When the channel list finishes loading, if this is true, the update checker will check for updates. + */ + bool m_checkUpdateWaiting; +}; + @@ -9,7 +9,13 @@ int main_gui(MultiMC &app) mainWin.restoreGeometry(QByteArray::fromBase64(MMC->settings()->get("MainWindowGeometry").toByteArray())); mainWin.show(); mainWin.checkSetDefaultJava(); - return app.exec(); + auto exitCode = app.exec(); + + // Update if necessary. + if (!app.getExitUpdatePath().isEmpty()) + app.installUpdates(app.getExitUpdatePath(), false); + + return exitCode; } int main(int argc, char *argv[]) diff --git a/mmc_updater/CMakeLists.txt b/mmc_updater/CMakeLists.txt new file mode 100644 index 00000000..61c8cd09 --- /dev/null +++ b/mmc_updater/CMakeLists.txt @@ -0,0 +1,44 @@ +project(updater) + +cmake_minimum_required(VERSION 2.6) +enable_testing() +set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules") + +include_directories(depends) + +if (WIN32) + include_directories(depends/win32cpp) + + if(MSVC) + # - Link the updater binary statically with the Visual C++ runtime + # so that the executable can function standalone. + # - Enable PDB generation for release builds + set(CMAKE_CXX_FLAGS_DEBUG "/MT") + set(CMAKE_C_FLAGS_DEBUG "/MT") + + set(CMAKE_CXX_FLAGS_RELEASE "/MT /Zi /O2 /Ob2 /D NDEBUG") + set(CMAKE_C_FLAGS_RELEASE "/MT /Zi /O2 /Ob2 /D NDEBUG") + remove_definitions(-DUNICODE -D_UNICODE) + endif() +else() + # optimize for reduced code size + set(CMAKE_CXX_FLAGS_RELEASE "-Os") + set(CMAKE_C_FLAGS_RELEASE "-Os") +endif() + +if (APPLE) + # Build the updater as a dual 32/64bit binary. If only one architecture + # is required, removing the other architecture will reduce the size + # of the updater binary + set(CMAKE_OSX_ARCHITECTURES i386;x86_64) + + # Build the updater so that it works on OS X 10.5 and above. + set(MIN_OSX_VERSION 10.5) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mmacosx-version-min=${MIN_OSX_VERSION}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mmacosx-version-min=${MIN_OSX_VERSION}") +endif() + +add_subdirectory(src) +add_subdirectory(depends/AnyOption) +add_subdirectory(depends/tinyxml) + diff --git a/mmc_updater/LICENSE b/mmc_updater/LICENSE new file mode 100644 index 00000000..f71a89be --- /dev/null +++ b/mmc_updater/LICENSE @@ -0,0 +1,19 @@ + +This project is licensed under a BSD license. + +The Mendeley Desktop icon graphics and name are trademarks of Mendeley Ltd. +and must be removed or replaced - see src/AppInfo.cpp and the files +in src/resources. + +=== + +Copyright (c) 2011, Mendeley Ltd. + +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/mmc_updater/README.md b/mmc_updater/README.md new file mode 100644 index 00000000..fa3c41ae --- /dev/null +++ b/mmc_updater/README.md @@ -0,0 +1,138 @@ +This tool is a component of a cross-platform auto-update system. +It is responsible for performing the installation of an update after +the necessary files have been downloaded to a temporary directory. + +It was originally written for use with Mendeley Desktop (see www.mendeley.com) + +The tool consists of a single small binary which performs update installation, +an XML file format describing the contents of an update (an 'update script') +and a tool to create update scripts from a directory containing an installed application. + +To perform an update, the application (or another separate tool) needs to download +the updater binary, an update script and one or more compressed packages +containing the files for the update to a temporary directory. It then needs +to invoke the updater, specifying the location where the application is installed, +the location of the compressed packages and the path to the update script. + +Once the updater has been started, it: + + 1. Waits for the application to exit + 2. Acquires the necessary priviledges to install the updates, prompting + the user if necessary. + 3. Installs the updates, displaying progress to the user in a small dialog + 4. Performs cleanup and any additional actions required as part of the update + 5. Starts the new version of the main application. + + In the event of a failure during the update, the installation is rolled back + to its previous state and a message is presented to the user. + +## Building the Updater + + Create a new directory for the build and from that directory run: + + cmake <path to source directory> + make + + The updater binary will be built in the src/ directory. + + You should also run the tests in src/tests to verify that the updater is + functioning correctly. + +## Preparing an Update + + 1. Create a directory containing your application's files, + laid out in the same way and with the same permissions as they would be when installed. + 2. Create a config file specifying how the application's files should be + partitioned into packages - see tools/config-template.json + 3. Use the tools/create-packages.rb script to create a file_list.xml file + and a set of package files required for updates. + 4. Upload the file_list.xml file and packages to a server + + After step 4 is done, you need to notify existing installs that an update + is available. The installed application then needs to download the + relevant packages, file_list.xml file and updater binary to a temporary + directory and invoke the updater. + + See doc/update-hosting for more details on hosting and delivering the updates. + +## Invoking the Updater + + Once the application has downloaded an update, it needs to invoke it. The syntax is: + + updater --install-dir <install-dir> --package-dir <package-dir> --script <script file> + + Where `<install-dir>` is the directory which the application is installed into, + `<package-dir>` is the directory containing the packages required for the update + and `<script>` is the `file_list.xml` file describing the update. + + Once the updater has run, it will launch the file specified in the `file_list.xml` file + as being the main application binary. + + See the updater test in `src/tests/test-update.rb` for an example + of how to invoke the updater. + + You should design the process used to download and launch the updater so that new + versions of the updater itself can be delivered as part of the update if necessary. + +## Customizing the Updater + + To customize the application name, organization and messages displayed by the updater: + + 1. Edit the AppInfo class (in AppInfo.h, AppInfo.cpp) to set the name + of the application and associated organization. + 2. Replace the icons in src/resources + 3. Change the product name and organization in src/resources/updater.rc + 4. If you are building the updater on Windows and have a suitable Authenticode + certificate, use it to sign the Windows binary. This will make the application + show a less scary UAC prompt if administrator permissions are required + to complete the installation. + +## Updater Dependencies + + The external dependencies of the updater binary are: + + * The C/C++ runtime libraries (Linux, Mac), + * pthreads (Linux, Mac), + * zlib (Linux, Mac) + * native UI library (Win32 API on Windows, Cocoa on Mac, GTK on Linux if available) + +## Full and Delta Updates + + The simplest auto-update implementation is for existing installs + to download a complete copy of the new version and install it. This is + appropriate if a full download and install will not take a long time for most users + (eg. if the application is small or they have a fast internet connection). + + With this tool, a full-update involves putting all files in a build of + the application into a single package. + + To reduce the download size, delta updates can be created which only include + the necessary files or components to update from the old to the new version. + + The file_list.xml file format can be used to represent either a complete + install - in which every file that makes up the application is included, + or a delta update - in which case only new or updated files and packages + are included. + + There are several ways in which this can be done: + + * Pre-computed Delta Updates + For each release, create a full update plus delta updates from the + previous N releases. Users of recent releases will receive a small + delta update. Users of older releases will receive the full update. + + * Server-computed Delta Updates + The server receives a request for an update from client version X and in response, + computes an update from version X to the current version Y, possibly + caching that information for future use. The client then receives the + delta file_list.xml file and downloads only the listed packages. + + Applications such as Chrome and Firefox use a mixture of the above methods. + + * Client-computed Delta Updates + The client downloads the file_list.xml file for the latest version and + computes a delta update file locally. It then downloads only the required + packages and invokes the updater, which installs only the changed or updated + files from those packages. + + This is similar to Linux package management systems. diff --git a/mmc_updater/cmake/modules/GenerateCppResourceFile.cmake b/mmc_updater/cmake/modules/GenerateCppResourceFile.cmake new file mode 100644 index 00000000..59adfd58 --- /dev/null +++ b/mmc_updater/cmake/modules/GenerateCppResourceFile.cmake @@ -0,0 +1,23 @@ + +# Convert a binary data file into a C++ +# source file for embedding into an application binary +# +# Currently only implemented for Unix. Requires the 'xxd' +# tool to be installed. +# +# TARGET_NAME : The name of the target to generate +# +# INPUT_DIR : The directory containing the input binary data file +# +# INPUT_FILE : The name of the binary data file in ${INPUT_DIR} to be converted into a C++ +# source file. The name of the input file will be used as the basis for the +# symbols in the generated C++ file referring to the data buffer and its length. +# +# CPP_FILE : The path of the C++ source file to be generated. +# See the documentation for xxd for information on +# the structure of the generated source file. +# +function (generate_cpp_resource_file TARGET_NAME INPUT_FILE CPP_FILE) + add_custom_command(OUTPUT ${CPP_FILE} COMMAND cd `dirname ${INPUT_FILE}` && xxd -i `basename ${INPUT_FILE}` ${CPP_FILE} DEPENDS ${INPUT_FILE}) + add_custom_target(${TARGET_NAME} ALL DEPENDS ${CPP_FILE}) +endfunction() diff --git a/mmc_updater/depends/AnyOption/CMakeLists.txt b/mmc_updater/depends/AnyOption/CMakeLists.txt new file mode 100644 index 00000000..fdfd6076 --- /dev/null +++ b/mmc_updater/depends/AnyOption/CMakeLists.txt @@ -0,0 +1,9 @@ +project(AnyOption) + +cmake_minimum_required(VERSION 2.6) + +add_library(anyoption + anyoption.cpp + anyoption.h +) + diff --git a/mmc_updater/depends/AnyOption/README b/mmc_updater/depends/AnyOption/README new file mode 100644 index 00000000..d91d6851 --- /dev/null +++ b/mmc_updater/depends/AnyOption/README @@ -0,0 +1,16 @@ + +http://www.hackorama.com/anyoption/ + +AnyOption is a C++ class for easy parsing of complex commandline options. It also parses options from a rsourcefile in option value pair format. + +AnyOption implements the traditional POSIX style character options ( -n ) as well as the newer GNU style long options ( --name ). Or you can use a simpler long option version ( -name ) by asking to ignore the POSIX style options. + +AnyOption supports the traditional UNIX resourcefile syntax of, any line starting with "#" is a comment and the value pairs use ":" as a delimiter. + +An option which expects a value is considered as an option value pair, while options without a value are considered flags. + +Please read the header file for the documented public interface, and demo.cpp for an example of how easy it is to use AnyOption. + +August 2004, added bug-fixes, and updates send by Michael Peters of Sandia Lab. +September 2006, fix from Boyan Asenov for a bug in mixing up option type indexes. +July 2011, fix from Min KJ and Costantino G for string allocation. diff --git a/mmc_updater/depends/AnyOption/anyoption.cpp b/mmc_updater/depends/AnyOption/anyoption.cpp new file mode 100644 index 00000000..6792fe3e --- /dev/null +++ b/mmc_updater/depends/AnyOption/anyoption.cpp @@ -0,0 +1,1176 @@ +/* + * AnyOption 1.3 + * + * kishan at hackorama dot com www.hackorama.com JULY 2001 + * + * + Acts as a common facade class for reading + * commandline options as well as options from + * an optionfile with delimited type value pairs + * + * + Handles the POSIX style single character options ( -w ) + * as well as the newer GNU long options ( --width ) + * + * + The option file assumes the traditional format of + * first character based comment lines and type value + * pairs with a delimiter , and flags which are not pairs + * + * # this is a coment + * # next line is an option value pair + * width : 100 + * # next line is a flag + * noimages + * + * + Supports printing out Help and Usage + * + * + Why not just use getopt() ? + * + * getopt() Its a POSIX standard not part of ANSI-C. + * So it may not be available on platforms like Windows. + * + * + Why it is so long ? + * + * The actual code which does command line parsing + * and option file parsing are done in few methods. + * Most of the extra code are for providing a flexible + * common public interface to both a resourcefile and + * and command line supporting POSIX style and + * GNU long option as well as mixing of both. + * + * + Please see "anyoption.h" for public method descriptions + * + */ + +/* Updated Auguest 2004 + * Fix from Michael D Peters (mpeters at sandia.gov) + * to remove static local variables, allowing multiple instantiations + * of the reader (for using multiple configuration files). There is + * an error in the destructor when using multiple instances, so you + * cannot delete your objects (it will crash), but not calling the + * destructor only introduces a small memory leak, so I + * have not bothered tracking it down. + * + * Also updated to use modern C++ style headers, rather than + * depricated iostream.h (it was causing my compiler problems) +*/ + +/* + * Updated September 2006 + * Fix from Boyan Asenov for a bug in mixing up option indexes + * leading to exception when mixing different options types + */ + +#include "anyoption.h" + +#include <string.h> + +AnyOption::AnyOption() +{ + init(); +} + +AnyOption::AnyOption(int maxopt) +{ + init( maxopt , maxopt ); +} + +AnyOption::AnyOption(int maxopt, int maxcharopt) +{ + init( maxopt , maxcharopt ); +} + +AnyOption::~AnyOption() +{ + if( mem_allocated ) + cleanup(); +} + +void +AnyOption::init() +{ + init( DEFAULT_MAXOPTS , DEFAULT_MAXOPTS ); +} + +void +AnyOption::init(int maxopt, int maxcharopt ) +{ + + max_options = maxopt; + max_char_options = maxcharopt; + max_usage_lines = DEFAULT_MAXUSAGE; + usage_lines = 0 ; + argc = 0; + argv = NULL; + posix_style = true; + verbose = false; + filename = NULL; + appname = NULL; + option_counter = 0; + optchar_counter = 0; + new_argv = NULL; + new_argc = 0 ; + max_legal_args = 0 ; + command_set = false; + file_set = false; + values = NULL; + g_value_counter = 0; + mem_allocated = false; + command_set = false; + file_set = false; + opt_prefix_char = '-'; + file_delimiter_char = ':'; + file_comment_char = '#'; + equalsign = '='; + comment = '#' ; + delimiter = ':' ; + endofline = '\n'; + whitespace = ' ' ; + nullterminate = '\0'; + set = false; + once = true; + hasoptions = false; + autousage = false; + + strcpy( long_opt_prefix , "--" ); + + if( alloc() == false ){ + cout << endl << "OPTIONS ERROR : Failed allocating memory" ; + cout << endl ; + cout << "Exiting." << endl ; + exit (0); + } +} + +bool +AnyOption::alloc() +{ + int i = 0 ; + int size = 0 ; + + if( mem_allocated ) + return true; + + size = (max_options+1) * sizeof(const char*); + options = (const char**)malloc( size ); + optiontype = (int*) malloc( (max_options+1)*sizeof(int) ); + optionindex = (int*) malloc( (max_options+1)*sizeof(int) ); + if( options == NULL || optiontype == NULL || optionindex == NULL ) + return false; + else + mem_allocated = true; + for( i = 0 ; i < max_options ; i++ ){ + options[i] = NULL; + optiontype[i] = 0 ; + optionindex[i] = -1 ; + } + optionchars = (char*) malloc( (max_char_options+1)*sizeof(char) ); + optchartype = (int*) malloc( (max_char_options+1)*sizeof(int) ); + optcharindex = (int*) malloc( (max_char_options+1)*sizeof(int) ); + if( optionchars == NULL || + optchartype == NULL || + optcharindex == NULL ) + { + mem_allocated = false; + return false; + } + for( i = 0 ; i < max_char_options ; i++ ){ + optionchars[i] = '0'; + optchartype[i] = 0 ; + optcharindex[i] = -1 ; + } + + size = (max_usage_lines+1) * sizeof(const char*) ; + usage = (const char**) malloc( size ); + + if( usage == NULL ){ + mem_allocated = false; + return false; + } + for( i = 0 ; i < max_usage_lines ; i++ ) + usage[i] = NULL; + + return true; +} + +bool +AnyOption::doubleOptStorage() +{ + options = (const char**)realloc( options, + ((2*max_options)+1) * sizeof( const char*) ); + optiontype = (int*) realloc( optiontype , + ((2 * max_options)+1)* sizeof(int) ); + optionindex = (int*) realloc( optionindex, + ((2 * max_options)+1) * sizeof(int) ); + if( options == NULL || optiontype == NULL || optionindex == NULL ) + return false; + /* init new storage */ + for( int i = max_options ; i < 2*max_options ; i++ ){ + options[i] = NULL; + optiontype[i] = 0 ; + optionindex[i] = -1 ; + } + max_options = 2 * max_options ; + return true; +} + +bool +AnyOption::doubleCharStorage() +{ + optionchars = (char*) realloc( optionchars, + ((2*max_char_options)+1)*sizeof(char) ); + optchartype = (int*) realloc( optchartype, + ((2*max_char_options)+1)*sizeof(int) ); + optcharindex = (int*) realloc( optcharindex, + ((2*max_char_options)+1)*sizeof(int) ); + if( optionchars == NULL || + optchartype == NULL || + optcharindex == NULL ) + return false; + /* init new storage */ + for( int i = max_char_options ; i < 2*max_char_options ; i++ ){ + optionchars[i] = '0'; + optchartype[i] = 0 ; + optcharindex[i] = -1 ; + } + max_char_options = 2 * max_char_options; + return true; +} + +bool +AnyOption::doubleUsageStorage() +{ + usage = (const char**)realloc( usage, + ((2*max_usage_lines)+1) * sizeof( const char*) ); + if ( usage == NULL ) + return false; + for( int i = max_usage_lines ; i < 2*max_usage_lines ; i++ ) + usage[i] = NULL; + max_usage_lines = 2 * max_usage_lines ; + return true; + +} + + +void +AnyOption::cleanup() +{ + free (options); + free (optiontype); + free (optionindex); + free (optionchars); + free (optchartype); + free (optcharindex); + free (usage); + if( values != NULL ) + free (values); + if( new_argv != NULL ) + free (new_argv); +} + +void +AnyOption::setCommandPrefixChar( char _prefix ) +{ + opt_prefix_char = _prefix; +} + +void +AnyOption::setCommandLongPrefix( char *_prefix ) +{ + if( strlen( _prefix ) > MAX_LONG_PREFIX_LENGTH ){ + *( _prefix + MAX_LONG_PREFIX_LENGTH ) = '\0'; + } + + strcpy (long_opt_prefix, _prefix); +} + +void +AnyOption::setFileCommentChar( char _comment ) +{ + file_delimiter_char = _comment; +} + + +void +AnyOption::setFileDelimiterChar( char _delimiter ) +{ + file_comment_char = _delimiter ; +} + +bool +AnyOption::CommandSet() +{ + return( command_set ); +} + +bool +AnyOption::FileSet() +{ + return( file_set ); +} + +void +AnyOption::noPOSIX() +{ + posix_style = false; +} + +bool +AnyOption::POSIX() +{ + return posix_style; +} + + +void +AnyOption::setVerbose() +{ + verbose = true ; +} + +void +AnyOption::printVerbose() +{ + if( verbose ) + cout << endl ; +} +void +AnyOption::printVerbose( const char *msg ) +{ + if( verbose ) + cout << msg ; +} + +void +AnyOption::printVerbose( char *msg ) +{ + if( verbose ) + cout << msg ; +} + +void +AnyOption::printVerbose( char ch ) +{ + if( verbose ) + cout << ch ; +} + +bool +AnyOption::hasOptions() +{ + return hasoptions; +} + +void +AnyOption::autoUsagePrint(bool _autousage) +{ + autousage = _autousage; +} + +void +AnyOption::useCommandArgs( int _argc, char **_argv ) +{ + argc = _argc; + argv = _argv; + command_set = true; + appname = argv[0]; + if(argc > 1) hasoptions = true; +} + +void +AnyOption::useFiileName( const char *_filename ) +{ + filename = _filename; + file_set = true; +} + +/* + * set methods for options + */ + +void +AnyOption::setCommandOption( const char *opt ) +{ + addOption( opt , COMMAND_OPT ); + g_value_counter++; +} + +void +AnyOption::setCommandOption( char opt ) +{ + addOption( opt , COMMAND_OPT ); + g_value_counter++; +} + +void +AnyOption::setCommandOption( const char *opt , char optchar ) +{ + addOption( opt , COMMAND_OPT ); + addOption( optchar , COMMAND_OPT ); + g_value_counter++; +} + +void +AnyOption::setCommandFlag( const char *opt ) +{ + addOption( opt , COMMAND_FLAG ); + g_value_counter++; +} + +void +AnyOption::setCommandFlag( char opt ) +{ + addOption( opt , COMMAND_FLAG ); + g_value_counter++; +} + +void +AnyOption::setCommandFlag( const char *opt , char optchar ) +{ + addOption( opt , COMMAND_FLAG ); + addOption( optchar , COMMAND_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFileOption( const char *opt ) +{ + addOption( opt , FILE_OPT ); + g_value_counter++; +} + +void +AnyOption::setFileOption( char opt ) +{ + addOption( opt , FILE_OPT ); + g_value_counter++; +} + +void +AnyOption::setFileOption( const char *opt , char optchar ) +{ + addOption( opt , FILE_OPT ); + addOption( optchar, FILE_OPT ); + g_value_counter++; +} + +void +AnyOption::setFileFlag( const char *opt ) +{ + addOption( opt , FILE_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFileFlag( char opt ) +{ + addOption( opt , FILE_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFileFlag( const char *opt , char optchar ) +{ + addOption( opt , FILE_FLAG ); + addOption( optchar , FILE_FLAG ); + g_value_counter++; +} + +void +AnyOption::setOption( const char *opt ) +{ + addOption( opt , COMMON_OPT ); + g_value_counter++; +} + +void +AnyOption::setOption( char opt ) +{ + addOption( opt , COMMON_OPT ); + g_value_counter++; +} + +void +AnyOption::setOption( const char *opt , char optchar ) +{ + addOption( opt , COMMON_OPT ); + addOption( optchar , COMMON_OPT ); + g_value_counter++; +} + +void +AnyOption::setFlag( const char *opt ) +{ + addOption( opt , COMMON_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFlag( const char opt ) +{ + addOption( opt , COMMON_FLAG ); + g_value_counter++; +} + +void +AnyOption::setFlag( const char *opt , char optchar ) +{ + addOption( opt , COMMON_FLAG ); + addOption( optchar , COMMON_FLAG ); + g_value_counter++; +} + +void +AnyOption::addOption( const char *opt, int type ) +{ + if( option_counter >= max_options ){ + if( doubleOptStorage() == false ){ + addOptionError( opt ); + return; + } + } + options[ option_counter ] = opt ; + optiontype[ option_counter ] = type ; + optionindex[ option_counter ] = g_value_counter; + option_counter++; +} + +void +AnyOption::addOption( char opt, int type ) +{ + if( !POSIX() ){ + printVerbose("Ignoring the option character \""); + printVerbose( opt ); + printVerbose( "\" ( POSIX options are turned off )" ); + printVerbose(); + return; + } + + + if( optchar_counter >= max_char_options ){ + if( doubleCharStorage() == false ){ + addOptionError( opt ); + return; + } + } + optionchars[ optchar_counter ] = opt ; + optchartype[ optchar_counter ] = type ; + optcharindex[ optchar_counter ] = g_value_counter; + optchar_counter++; +} + +void +AnyOption::addOptionError( const char *opt ) +{ + cout << endl ; + cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; + cout << "While adding the option : \""<< opt << "\"" << endl; + cout << "Exiting." << endl ; + cout << endl ; + exit(0); +} + +void +AnyOption::addOptionError( char opt ) +{ + cout << endl ; + cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; + cout << "While adding the option: \""<< opt << "\"" << endl; + cout << "Exiting." << endl ; + cout << endl ; + exit(0); +} + +void +AnyOption::processOptions() +{ + if( ! valueStoreOK() ) + return; +} + +void +AnyOption::processCommandArgs(int max_args) +{ + max_legal_args = max_args; + processCommandArgs(); +} + +void +AnyOption::processCommandArgs( int _argc, char **_argv, int max_args ) +{ + max_legal_args = max_args; + processCommandArgs( _argc, _argv ); +} + +void +AnyOption::processCommandArgs( int _argc, char **_argv ) +{ + useCommandArgs( _argc, _argv ); + processCommandArgs(); +} + +void +AnyOption::processCommandArgs() +{ + if( ! ( valueStoreOK() && CommandSet() ) ) + return; + + if( max_legal_args == 0 ) + max_legal_args = argc; + new_argv = (int*) malloc( (max_legal_args+1) * sizeof(int) ); + for( int i = 1 ; i < argc ; i++ ){/* ignore first argv */ + if( argv[i][0] == long_opt_prefix[0] && + argv[i][1] == long_opt_prefix[1] ) { /* long GNU option */ + int match_at = parseGNU( argv[i]+2 ); /* skip -- */ + if( match_at >= 0 && i < argc-1 ) /* found match */ + setValue( options[match_at] , argv[++i] ); + }else if( argv[i][0] == opt_prefix_char ) { /* POSIX char */ + if( POSIX() ){ + char ch = parsePOSIX( argv[i]+1 );/* skip - */ + if( ch != '0' && i < argc-1 ) /* matching char */ + setValue( ch , argv[++i] ); + } else { /* treat it as GNU option with a - */ + int match_at = parseGNU( argv[i]+1 ); /* skip - */ + if( match_at >= 0 && i < argc-1 ) /* found match */ + setValue( options[match_at] , argv[++i] ); + } + }else { /* not option but an argument keep index */ + if( new_argc < max_legal_args ){ + new_argv[ new_argc ] = i ; + new_argc++; + }else{ /* ignore extra arguments */ + printVerbose( "Ignoring extra argument: " ); + printVerbose( argv[i] ); + printVerbose( ); + printAutoUsage(); + } + printVerbose( "Unknown command argument option : " ); + printVerbose( argv[i] ); + printVerbose( ); + printAutoUsage(); + } + } +} + +char +AnyOption::parsePOSIX( char* arg ) +{ + + for( unsigned int i = 0 ; i < strlen(arg) ; i++ ){ + char ch = arg[i] ; + if( matchChar(ch) ) { /* keep matching flags till an option */ + /*if last char argv[++i] is the value */ + if( i == strlen(arg)-1 ){ + return ch; + }else{/* else the rest of arg is the value */ + i++; /* skip any '=' and ' ' */ + while( arg[i] == whitespace + || arg[i] == equalsign ) + i++; + setValue( ch , arg+i ); + return '0'; + } + } + } + printVerbose( "Unknown command argument option : " ); + printVerbose( arg ); + printVerbose( ); + printAutoUsage(); + return '0'; +} + +int +AnyOption::parseGNU( char *arg ) +{ + int split_at = 0; + /* if has a '=' sign get value */ + for( unsigned int i = 0 ; i < strlen(arg) ; i++ ){ + if(arg[i] == equalsign ){ + split_at = i ; /* store index */ + i = strlen(arg); /* get out of loop */ + } + } + if( split_at > 0 ){ /* it is an option value pair */ + char* tmp = (char*) malloc( (split_at+1)*sizeof(char) ); + for( int i = 0 ; i < split_at ; i++ ) + tmp[i] = arg[i]; + tmp[split_at] = '\0'; + + if ( matchOpt( tmp ) >= 0 ){ + setValue( options[matchOpt(tmp)] , arg+split_at+1 ); + free (tmp); + }else{ + printVerbose( "Unknown command argument option : " ); + printVerbose( arg ); + printVerbose( ); + printAutoUsage(); + free (tmp); + return -1; + } + }else{ /* regular options with no '=' sign */ + return matchOpt(arg); + } + return -1; +} + + +int +AnyOption::matchOpt( char *opt ) +{ + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], opt ) == 0 ){ + if( optiontype[i] == COMMON_OPT || + optiontype[i] == COMMAND_OPT ) + { /* found option return index */ + return i; + }else if( optiontype[i] == COMMON_FLAG || + optiontype[i] == COMMAND_FLAG ) + { /* found flag, set it */ + setFlagOn( opt ); + return -1; + } + } + } + printVerbose( "Unknown command argument option : " ); + printVerbose( opt ) ; + printVerbose( ); + printAutoUsage(); + return -1; +} +bool +AnyOption::matchChar( char c ) +{ + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == c ) { /* found match */ + if(optchartype[i] == COMMON_OPT || + optchartype[i] == COMMAND_OPT ) + { /* an option store and stop scanning */ + return true; + }else if( optchartype[i] == COMMON_FLAG || + optchartype[i] == COMMAND_FLAG ) { /* a flag store and keep scanning */ + setFlagOn( c ); + return false; + } + } + } + printVerbose( "Unknown command argument option : " ); + printVerbose( c ) ; + printVerbose( ); + printAutoUsage(); + return false; +} + +bool +AnyOption::valueStoreOK( ) +{ + int size= 0; + if( !set ){ + if( g_value_counter > 0 ){ + size = g_value_counter * sizeof(char*); + values = (char**)malloc( size ); + for( int i = 0 ; i < g_value_counter ; i++) + values[i] = NULL; + set = true; + } + } + return set; +} + +/* + * public get methods + */ +char* +AnyOption::getValue( const char *option ) +{ + if( !valueStoreOK() ) + return NULL; + + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], option ) == 0 ) + return values[ optionindex[i] ]; + } + return NULL; +} + +bool +AnyOption::getFlag( const char *option ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], option ) == 0 ) + return findFlag( values[ optionindex[i] ] ); + } + return false; +} + +char* +AnyOption::getValue( char option ) +{ + if( !valueStoreOK() ) + return NULL; + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == option ) + return values[ optcharindex[i] ]; + } + return NULL; +} + +bool +AnyOption::getFlag( char option ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == option ) + return findFlag( values[ optcharindex[i] ] ) ; + } + return false; +} + +bool +AnyOption::findFlag( char* val ) +{ + if( val == NULL ) + return false; + + if( strcmp( TRUE_FLAG , val ) == 0 ) + return true; + + return false; +} + +/* + * private set methods + */ +bool +AnyOption::setValue( const char *option , char *value ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], option ) == 0 ){ + values[ optionindex[i] ] = (char*) malloc((strlen(value)+1)*sizeof(char)); + strcpy( values[ optionindex[i] ], value ); + return true; + } + } + return false; +} + +bool +AnyOption::setFlagOn( const char *option ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], option ) == 0 ){ + values[ optionindex[i] ] = (char*) malloc((strlen(TRUE_FLAG)+1)*sizeof(char)); + strcpy( values[ optionindex[i] ] , TRUE_FLAG ); + return true; + } + } + return false; +} + +bool +AnyOption::setValue( char option , char *value ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == option ){ + values[ optcharindex[i] ] = (char*) malloc((strlen(value)+1)*sizeof(char)); + strcpy( values[ optcharindex[i] ], value ); + return true; + } + } + return false; +} + +bool +AnyOption::setFlagOn( char option ) +{ + if( !valueStoreOK() ) + return false; + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == option ){ + values[ optcharindex[i] ] = (char*) malloc((strlen(TRUE_FLAG)+1)*sizeof(char)); + strcpy( values[ optcharindex[i] ] , TRUE_FLAG ); + return true; + } + } + return false; +} + + +int +AnyOption::getArgc( ) +{ + return new_argc; +} + +char* +AnyOption::getArgv( int index ) +{ + if( index < new_argc ){ + return ( argv[ new_argv[ index ] ] ); + } + return NULL; +} + +/* dotfile sub routines */ + +bool +AnyOption::processFile() +{ + if( ! (valueStoreOK() && FileSet()) ) + return false; + return ( consumeFile(readFile()) ); +} + +bool +AnyOption::processFile( const char *filename ) +{ + useFiileName(filename ); + return ( processFile() ); +} + +char* +AnyOption::readFile() +{ + return ( readFile(filename) ); +} + +/* + * read the file contents to a character buffer + */ + +char* +AnyOption::readFile( const char* fname ) +{ + int length; + char *buffer; + ifstream is; + is.open ( fname , ifstream::in ); + if( ! is.good() ){ + is.close(); + return NULL; + } + is.seekg (0, ios::end); + length = is.tellg(); + is.seekg (0, ios::beg); + buffer = (char*) malloc(length*sizeof(char)); + is.read (buffer,length); + is.close(); + return buffer; +} + +/* + * scans a char* buffer for lines that does not + * start with the specified comment character. + */ +bool +AnyOption::consumeFile( char *buffer ) +{ + + if( buffer == NULL ) + return false; + + char *cursor = buffer;/* preserve the ptr */ + char *pline = NULL ; + int linelength = 0; + bool newline = true; + for( unsigned int i = 0 ; i < strlen( buffer ) ; i++ ){ + if( *cursor == endofline ) { /* end of line */ + if( pline != NULL ) /* valid line */ + processLine( pline, linelength ); + pline = NULL; + newline = true; + }else if( newline ){ /* start of line */ + newline = false; + if( (*cursor != comment ) ){ /* not a comment */ + pline = cursor ; + linelength = 0 ; + } + } + cursor++; /* keep moving */ + linelength++; + } + free (buffer); + return true; +} + + +/* + * find a valid type value pair separated by a delimiter + * character and pass it to valuePairs() + * any line which is not valid will be considered a value + * and will get passed on to justValue() + * + * assuming delimiter is ':' the behaviour will be, + * + * width:10 - valid pair valuePairs( width, 10 ); + * width : 10 - valid pair valuepairs( width, 10 ); + * + * :::: - not valid + * width - not valid + * :10 - not valid + * width: - not valid + * :: - not valid + * : - not valid + * + */ + +void +AnyOption::processLine( char *theline, int length ) +{ + bool found = false; + char *pline = (char*) malloc( (length+1)*sizeof(char) ); + for( int i = 0 ; i < length ; i ++ ) + pline[i]= *(theline++); + pline[length] = nullterminate; + char *cursor = pline ; /* preserve the ptr */ + if( *cursor == delimiter || *(cursor+length-1) == delimiter ){ + justValue( pline );/* line with start/end delimiter */ + }else{ + for( int i = 1 ; i < length-1 && !found ; i++){/* delimiter */ + if( *cursor == delimiter ){ + *(cursor-1) = nullterminate; /* two strings */ + found = true; + valuePairs( pline , cursor+1 ); + } + cursor++; + } + cursor++; + if( !found ) /* not a pair */ + justValue( pline ); + } + free (pline); +} + +/* + * removes trailing and preceeding whitespaces from a string + */ +char* +AnyOption::chomp( char *str ) +{ + while( *str == whitespace ) + str++; + char *end = str+strlen(str)-1; + while( *end == whitespace ) + end--; + *(end+1) = nullterminate; + return str; +} + +void +AnyOption::valuePairs( char *type, char *value ) +{ + if ( strlen(chomp(type)) == 1 ){ /* this is a char option */ + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == type[0] ){ /* match */ + if( optchartype[i] == COMMON_OPT || + optchartype[i] == FILE_OPT ) + { + setValue( type[0] , chomp(value) ); + return; + } + } + } + } + /* if no char options matched */ + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], type ) == 0 ){ /* match */ + if( optiontype[i] == COMMON_OPT || + optiontype[i] == FILE_OPT ) + { + setValue( type , chomp(value) ); + return; + } + } + } + printVerbose( "Unknown option in resourcefile : " ); + printVerbose( type ); + printVerbose( ); +} + +void +AnyOption::justValue( char *type ) +{ + + if ( strlen(chomp(type)) == 1 ){ /* this is a char option */ + for( int i = 0 ; i < optchar_counter ; i++ ){ + if( optionchars[i] == type[0] ){ /* match */ + if( optchartype[i] == COMMON_FLAG || + optchartype[i] == FILE_FLAG ) + { + setFlagOn( type[0] ); + return; + } + } + } + } + /* if no char options matched */ + for( int i = 0 ; i < option_counter ; i++ ){ + if( strcmp( options[i], type ) == 0 ){ /* match */ + if( optiontype[i] == COMMON_FLAG || + optiontype[i] == FILE_FLAG ) + { + setFlagOn( type ); + return; + } + } + } + printVerbose( "Unknown option in resourcefile : " ); + printVerbose( type ); + printVerbose( ); +} + +/* + * usage and help + */ + + +void +AnyOption::printAutoUsage() +{ + if( autousage ) printUsage(); +} + +void +AnyOption::printUsage() +{ + + if( once ) { + once = false ; + cout << endl ; + for( int i = 0 ; i < usage_lines ; i++ ) + cout << usage[i] << endl ; + cout << endl ; + } +} + + +void +AnyOption::addUsage( const char *line ) +{ + if( usage_lines >= max_usage_lines ){ + if( doubleUsageStorage() == false ){ + addUsageError( line ); + exit(1); + } + } + usage[ usage_lines ] = line ; + usage_lines++; +} + +void +AnyOption::addUsageError( const char *line ) +{ + cout << endl ; + cout << "OPTIONS ERROR : Failed allocating extra memory " << endl ; + cout << "While adding the usage/help : \""<< line << "\"" << endl; + cout << "Exiting." << endl ; + cout << endl ; + exit(0); + +} diff --git a/mmc_updater/depends/AnyOption/anyoption.h b/mmc_updater/depends/AnyOption/anyoption.h new file mode 100644 index 00000000..01501df2 --- /dev/null +++ b/mmc_updater/depends/AnyOption/anyoption.h @@ -0,0 +1,270 @@ +#ifndef _ANYOPTION_H +#define _ANYOPTION_H + +#include <iostream> +#include <fstream> +#include <stdlib.h> +#include <string> + +#define COMMON_OPT 1 +#define COMMAND_OPT 2 +#define FILE_OPT 3 +#define COMMON_FLAG 4 +#define COMMAND_FLAG 5 +#define FILE_FLAG 6 + +#define COMMAND_OPTION_TYPE 1 +#define COMMAND_FLAG_TYPE 2 +#define FILE_OPTION_TYPE 3 +#define FILE_FLAG_TYPE 4 +#define UNKNOWN_TYPE 5 + +#define DEFAULT_MAXOPTS 10 +#define MAX_LONG_PREFIX_LENGTH 2 + +#define DEFAULT_MAXUSAGE 3 +#define DEFAULT_MAXHELP 10 + +#define TRUE_FLAG "true" + +using namespace std; + +class AnyOption +{ + +public: /* the public interface */ + AnyOption(); + AnyOption(int maxoptions ); + AnyOption(int maxoptions , int maxcharoptions); + ~AnyOption(); + + /* + * following set methods specifies the + * special characters and delimiters + * if not set traditional defaults will be used + */ + + void setCommandPrefixChar( char _prefix ); /* '-' in "-w" */ + void setCommandLongPrefix( char *_prefix ); /* '--' in "--width" */ + void setFileCommentChar( char _comment ); /* '#' in shellscripts */ + void setFileDelimiterChar( char _delimiter );/* ':' in "width : 100" */ + + /* + * provide the input for the options + * like argv[] for commndline and the + * option file name to use; + */ + + void useCommandArgs( int _argc, char **_argv ); + void useFiileName( const char *_filename ); + + /* + * turn off the POSIX style options + * this means anything starting with a '-' or "--" + * will be considered a valid option + * which alo means you cannot add a bunch of + * POIX options chars together like "-lr" for "-l -r" + * + */ + + void noPOSIX(); + + /* + * prints warning verbose if you set anything wrong + */ + void setVerbose(); + + + /* + * there are two types of options + * + * Option - has an associated value ( -w 100 ) + * Flag - no value, just a boolean flag ( -nogui ) + * + * the options can be either a string ( GNU style ) + * or a character ( traditional POSIX style ) + * or both ( --width, -w ) + * + * the options can be common to the commandline and + * the optionfile, or can belong only to either of + * commandline and optionfile + * + * following set methods, handle all the aboove + * cases of options. + */ + + /* options comman to command line and option file */ + void setOption( const char *opt_string ); + void setOption( char opt_char ); + void setOption( const char *opt_string , char opt_char ); + void setFlag( const char *opt_string ); + void setFlag( char opt_char ); + void setFlag( const char *opt_string , char opt_char ); + + /* options read from commandline only */ + void setCommandOption( const char *opt_string ); + void setCommandOption( char opt_char ); + void setCommandOption( const char *opt_string , char opt_char ); + void setCommandFlag( const char *opt_string ); + void setCommandFlag( char opt_char ); + void setCommandFlag( const char *opt_string , char opt_char ); + + /* options read from an option file only */ + void setFileOption( const char *opt_string ); + void setFileOption( char opt_char ); + void setFileOption( const char *opt_string , char opt_char ); + void setFileFlag( const char *opt_string ); + void setFileFlag( char opt_char ); + void setFileFlag( const char *opt_string , char opt_char ); + + /* + * process the options, registerd using + * useCommandArgs() and useFileName(); + */ + void processOptions(); + void processCommandArgs(); + void processCommandArgs( int max_args ); + bool processFile(); + + /* + * process the specified options + */ + void processCommandArgs( int _argc, char **_argv ); + void processCommandArgs( int _argc, char **_argv, int max_args ); + bool processFile( const char *_filename ); + + /* + * get the value of the options + * will return NULL if no value is set + */ + char *getValue( const char *_option ); + bool getFlag( const char *_option ); + char *getValue( char _optchar ); + bool getFlag( char _optchar ); + + /* + * Print Usage + */ + void printUsage(); + void printAutoUsage(); + void addUsage( const char *line ); + void printHelp(); + /* print auto usage printing for unknown options or flag */ + void autoUsagePrint(bool flag); + + /* + * get the argument count and arguments sans the options + */ + int getArgc(); + char* getArgv( int index ); + bool hasOptions(); + +private: /* the hidden data structure */ + int argc; /* commandline arg count */ + char **argv; /* commndline args */ + const char* filename; /* the option file */ + char* appname; /* the application name from argv[0] */ + + int *new_argv; /* arguments sans options (index to argv) */ + int new_argc; /* argument count sans the options */ + int max_legal_args; /* ignore extra arguments */ + + + /* option strings storage + indexing */ + int max_options; /* maximum number of options */ + const char **options; /* storage */ + int *optiontype; /* type - common, command, file */ + int *optionindex; /* index into value storage */ + int option_counter; /* counter for added options */ + + /* option chars storage + indexing */ + int max_char_options; /* maximum number options */ + char *optionchars; /* storage */ + int *optchartype; /* type - common, command, file */ + int *optcharindex; /* index into value storage */ + int optchar_counter; /* counter for added options */ + + /* values */ + char **values; /* common value storage */ + int g_value_counter; /* globally updated value index LAME! */ + + /* help and usage */ + const char **usage; /* usage */ + int max_usage_lines; /* max usage lines reseverd */ + int usage_lines; /* number of usage lines */ + + bool command_set; /* if argc/argv were provided */ + bool file_set; /* if a filename was provided */ + bool mem_allocated; /* if memory allocated in init() */ + bool posix_style; /* enables to turn off POSIX style options */ + bool verbose; /* silent|verbose */ + bool print_usage; /* usage verbose */ + bool print_help; /* help verbose */ + + char opt_prefix_char; /* '-' in "-w" */ + char long_opt_prefix[MAX_LONG_PREFIX_LENGTH + 1]; /* '--' in "--width" */ + char file_delimiter_char; /* ':' in width : 100 */ + char file_comment_char; /* '#' in "#this is a comment" */ + char equalsign; + char comment; + char delimiter; + char endofline; + char whitespace; + char nullterminate; + + bool set; //was static member + bool once; //was static member + + bool hasoptions; + bool autousage; + +private: /* the hidden utils */ + void init(); + void init(int maxopt, int maxcharopt ); + bool alloc(); + void cleanup(); + bool valueStoreOK(); + + /* grow storage arrays as required */ + bool doubleOptStorage(); + bool doubleCharStorage(); + bool doubleUsageStorage(); + + bool setValue( const char *option , char *value ); + bool setFlagOn( const char *option ); + bool setValue( char optchar , char *value); + bool setFlagOn( char optchar ); + + void addOption( const char* option , int type ); + void addOption( char optchar , int type ); + void addOptionError( const char *opt); + void addOptionError( char opt); + bool findFlag( char* value ); + void addUsageError( const char *line ); + bool CommandSet(); + bool FileSet(); + bool POSIX(); + + char parsePOSIX( char* arg ); + int parseGNU( char *arg ); + bool matchChar( char c ); + int matchOpt( char *opt ); + + /* dot file methods */ + char *readFile(); + char *readFile( const char* fname ); + bool consumeFile( char *buffer ); + void processLine( char *theline, int length ); + char *chomp( char *str ); + void valuePairs( char *type, char *value ); + void justValue( char *value ); + + void printVerbose( const char *msg ); + void printVerbose( char *msg ); + void printVerbose( char ch ); + void printVerbose( ); + + +}; + +#endif /* ! _ANYOPTION_H */ diff --git a/mmc_updater/depends/tinyxml/CMakeLists.txt b/mmc_updater/depends/tinyxml/CMakeLists.txt new file mode 100644 index 00000000..25c9bb92 --- /dev/null +++ b/mmc_updater/depends/tinyxml/CMakeLists.txt @@ -0,0 +1,24 @@ +# TinyXML 1.0.1 +project(tinyxml) + +cmake_minimum_required(VERSION 2.6) + +Add_definitions(-DTIXML_USE_STL) + +set(SOURCES + tinystr.cpp + tinyxml.cpp + tinyxmlerror.cpp + tinyxmlparser.cpp +) + +set(HEADERS + tinystr.h + tinyxml.h +) + +add_library(tinyxml + ${SOURCES} + ${HEADERS} +) + diff --git a/mmc_updater/depends/tinyxml/readme.txt b/mmc_updater/depends/tinyxml/readme.txt new file mode 100644 index 00000000..89d9e8d3 --- /dev/null +++ b/mmc_updater/depends/tinyxml/readme.txt @@ -0,0 +1,530 @@ +/** @mainpage + +<h1> TinyXML </h1> + +TinyXML is a simple, small, C++ XML parser that can be easily +integrated into other programs. + +<h2> What it does. </h2> + +In brief, TinyXML parses an XML document, and builds from that a +Document Object Model (DOM) that can be read, modified, and saved. + +XML stands for "eXtensible Markup Language." It allows you to create +your own document markups. Where HTML does a very good job of marking +documents for browsers, XML allows you to define any kind of document +markup, for example a document that describes a "to do" list for an +organizer application. XML is a very structured and convenient format. +All those random file formats created to store application data can +all be replaced with XML. One parser for everything. + +The best place for the complete, correct, and quite frankly hard to +read spec is at <a href="http://www.w3.org/TR/2004/REC-xml-20040204/"> +http://www.w3.org/TR/2004/REC-xml-20040204/</a>. An intro to XML +(that I really like) can be found at +<a href="http://skew.org/xml/tutorial/">http://skew.org/xml/tutorial</a>. + +There are different ways to access and interact with XML data. +TinyXML uses a Document Object Model (DOM), meaning the XML data is parsed +into a C++ objects that can be browsed and manipulated, and then +written to disk or another output stream. You can also construct an XML document +from scratch with C++ objects and write this to disk or another output +stream. + +TinyXML is designed to be easy and fast to learn. It is two headers +and four cpp files. Simply add these to your project and off you go. +There is an example file - xmltest.cpp - to get you started. + +TinyXML is released under the ZLib license, +so you can use it in open source or commercial code. The details +of the license are at the top of every source file. + +TinyXML attempts to be a flexible parser, but with truly correct and +compliant XML output. TinyXML should compile on any reasonably C++ +compliant system. It does not rely on exceptions or RTTI. It can be +compiled with or without STL support. TinyXML fully supports +the UTF-8 encoding, and the first 64k character entities. + + +<h2> What it doesn't do. </h2> + +TinyXML doesn't parse or use DTDs (Document Type Definitions) or XSLs +(eXtensible Stylesheet Language.) There are other parsers out there +(check out www.sourceforge.org, search for XML) that are much more fully +featured. But they are also much bigger, take longer to set up in +your project, have a higher learning curve, and often have a more +restrictive license. If you are working with browsers or have more +complete XML needs, TinyXML is not the parser for you. + +The following DTD syntax will not parse at this time in TinyXML: + +@verbatim + <!DOCTYPE Archiv [ + <!ELEMENT Comment (#PCDATA)> + ]> +@endverbatim + +because TinyXML sees this as a !DOCTYPE node with an illegally +embedded !ELEMENT node. This may be addressed in the future. + +<h2> Tutorials. </h2> + +For the impatient, here is a tutorial to get you going. A great way to get started, +but it is worth your time to read this (very short) manual completely. + +- @subpage tutorial0 + +<h2> Code Status. </h2> + +TinyXML is mature, tested code. It is very stable. If you find +bugs, please file a bug report on the sourceforge web site +(www.sourceforge.net/projects/tinyxml). We'll get them straightened +out as soon as possible. + +There are some areas of improvement; please check sourceforge if you are +interested in working on TinyXML. + +<h2> Related Projects </h2> + +TinyXML projects you may find useful! (Descriptions provided by the projects.) + +<ul> +<li> <b>TinyXPath</b> (http://tinyxpath.sourceforge.net). TinyXPath is a small footprint + XPath syntax decoder, written in C++.</li> +<li> <b>TinyXML++</b> (http://code.google.com/p/ticpp/). TinyXML++ is a completely new + interface to TinyXML that uses MANY of the C++ strengths. Templates, + exceptions, and much better error handling.</li> +</ul> + +<h2> Features </h2> + +<h3> Using STL </h3> + +TinyXML can be compiled to use or not use STL. When using STL, TinyXML +uses the std::string class, and fully supports std::istream, std::ostream, +operator<<, and operator>>. Many API methods have both 'const char*' and +'const std::string&' forms. + +When STL support is compiled out, no STL files are included whatsoever. All +the string classes are implemented by TinyXML itself. API methods +all use the 'const char*' form for input. + +Use the compile time #define: + + TIXML_USE_STL + +to compile one version or the other. This can be passed by the compiler, +or set as the first line of "tinyxml.h". + +Note: If compiling the test code in Linux, setting the environment +variable TINYXML_USE_STL=YES/NO will control STL compilation. In the +Windows project file, STL and non STL targets are provided. In your project, +It's probably easiest to add the line "#define TIXML_USE_STL" as the first +line of tinyxml.h. + +<h3> UTF-8 </h3> + +TinyXML supports UTF-8 allowing to manipulate XML files in any language. TinyXML +also supports "legacy mode" - the encoding used before UTF-8 support and +probably best described as "extended ascii". + +Normally, TinyXML will try to detect the correct encoding and use it. However, +by setting the value of TIXML_DEFAULT_ENCODING in the header file, TinyXML +can be forced to always use one encoding. + +TinyXML will assume Legacy Mode until one of the following occurs: +<ol> + <li> If the non-standard but common "UTF-8 lead bytes" (0xef 0xbb 0xbf) + begin the file or data stream, TinyXML will read it as UTF-8. </li> + <li> If the declaration tag is read, and it has an encoding="UTF-8", then + TinyXML will read it as UTF-8. </li> + <li> If the declaration tag is read, and it has no encoding specified, then TinyXML will + read it as UTF-8. </li> + <li> If the declaration tag is read, and it has an encoding="something else", then TinyXML + will read it as Legacy Mode. In legacy mode, TinyXML will work as it did before. It's + not clear what that mode does exactly, but old content should keep working.</li> + <li> Until one of the above criteria is met, TinyXML runs in Legacy Mode.</li> +</ol> + +What happens if the encoding is incorrectly set or detected? TinyXML will try +to read and pass through text seen as improperly encoded. You may get some strange results or +mangled characters. You may want to force TinyXML to the correct mode. + +You may force TinyXML to Legacy Mode by using LoadFile( TIXML_ENCODING_LEGACY ) or +LoadFile( filename, TIXML_ENCODING_LEGACY ). You may force it to use legacy mode all +the time by setting TIXML_DEFAULT_ENCODING = TIXML_ENCODING_LEGACY. Likewise, you may +force it to TIXML_ENCODING_UTF8 with the same technique. + +For English users, using English XML, UTF-8 is the same as low-ASCII. You +don't need to be aware of UTF-8 or change your code in any way. You can think +of UTF-8 as a "superset" of ASCII. + +UTF-8 is not a double byte format - but it is a standard encoding of Unicode! +TinyXML does not use or directly support wchar, TCHAR, or Microsoft's _UNICODE at this time. +It is common to see the term "Unicode" improperly refer to UTF-16, a wide byte encoding +of unicode. This is a source of confusion. + +For "high-ascii" languages - everything not English, pretty much - TinyXML can +handle all languages, at the same time, as long as the XML is encoded +in UTF-8. That can be a little tricky, older programs and operating systems +tend to use the "default" or "traditional" code page. Many apps (and almost all +modern ones) can output UTF-8, but older or stubborn (or just broken) ones +still output text in the default code page. + +For example, Japanese systems traditionally use SHIFT-JIS encoding. +Text encoded as SHIFT-JIS can not be read by TinyXML. +A good text editor can import SHIFT-JIS and then save as UTF-8. + +The <a href="http://skew.org/xml/tutorial/">Skew.org link</a> does a great +job covering the encoding issue. + +The test file "utf8test.xml" is an XML containing English, Spanish, Russian, +and Simplified Chinese. (Hopefully they are translated correctly). The file +"utf8test.gif" is a screen capture of the XML file, rendered in IE. Note that +if you don't have the correct fonts (Simplified Chinese or Russian) on your +system, you won't see output that matches the GIF file even if you can parse +it correctly. Also note that (at least on my Windows machine) console output +is in a Western code page, so that Print() or printf() cannot correctly display +the file. This is not a bug in TinyXML - just an OS issue. No data is lost or +destroyed by TinyXML. The console just doesn't render UTF-8. + + +<h3> Entities </h3> +TinyXML recognizes the pre-defined "character entities", meaning special +characters. Namely: + +@verbatim + & & + < < + > > + " " + ' ' +@endverbatim + +These are recognized when the XML document is read, and translated to there +UTF-8 equivalents. For instance, text with the XML of: + +@verbatim + Far & Away +@endverbatim + +will have the Value() of "Far & Away" when queried from the TiXmlText object, +and will be written back to the XML stream/file as an ampersand. Older versions +of TinyXML "preserved" character entities, but the newer versions will translate +them into characters. + +Additionally, any character can be specified by its Unicode code point: +The syntax " " or " " are both to the non-breaking space characher. + +<h3> Printing </h3> +TinyXML can print output in several different ways that all have strengths and limitations. + +- Print( FILE* ). Output to a std-C stream, which includes all C files as well as stdout. + - "Pretty prints", but you don't have control over printing options. + - The output is streamed directly to the FILE object, so there is no memory overhead + in the TinyXML code. + - used by Print() and SaveFile() + +- operator<<. Output to a c++ stream. + - Integrates with standart C++ iostreams. + - Outputs in "network printing" mode without line breaks. Good for network transmission + and moving XML between C++ objects, but hard for a human to read. + +- TiXmlPrinter. Output to a std::string or memory buffer. + - API is less concise + - Future printing options will be put here. + - Printing may change slightly in future versions as it is refined and expanded. + +<h3> Streams </h3> +With TIXML_USE_STL on TinyXML supports C++ streams (operator <<,>>) streams as well +as C (FILE*) streams. There are some differences that you may need to be aware of. + +C style output: + - based on FILE* + - the Print() and SaveFile() methods + + Generates formatted output, with plenty of white space, intended to be as + human-readable as possible. They are very fast, and tolerant of ill formed + XML documents. For example, an XML document that contains 2 root elements + and 2 declarations, will still print. + +C style input: + - based on FILE* + - the Parse() and LoadFile() methods + + A fast, tolerant read. Use whenever you don't need the C++ streams. + +C++ style output: + - based on std::ostream + - operator<< + + Generates condensed output, intended for network transmission rather than + readability. Depending on your system's implementation of the ostream class, + these may be somewhat slower. (Or may not.) Not tolerant of ill formed XML: + a document should contain the correct one root element. Additional root level + elements will not be streamed out. + +C++ style input: + - based on std::istream + - operator>> + + Reads XML from a stream, making it useful for network transmission. The tricky + part is knowing when the XML document is complete, since there will almost + certainly be other data in the stream. TinyXML will assume the XML data is + complete after it reads the root element. Put another way, documents that + are ill-constructed with more than one root element will not read correctly. + Also note that operator>> is somewhat slower than Parse, due to both + implementation of the STL and limitations of TinyXML. + +<h3> White space </h3> +The world simply does not agree on whether white space should be kept, or condensed. +For example, pretend the '_' is a space, and look at "Hello____world". HTML, and +at least some XML parsers, will interpret this as "Hello_world". They condense white +space. Some XML parsers do not, and will leave it as "Hello____world". (Remember +to keep pretending the _ is a space.) Others suggest that __Hello___world__ should become +Hello___world. + +It's an issue that hasn't been resolved to my satisfaction. TinyXML supports the +first 2 approaches. Call TiXmlBase::SetCondenseWhiteSpace( bool ) to set the desired behavior. +The default is to condense white space. + +If you change the default, you should call TiXmlBase::SetCondenseWhiteSpace( bool ) +before making any calls to Parse XML data, and I don't recommend changing it after +it has been set. + + +<h3> Handles </h3> + +Where browsing an XML document in a robust way, it is important to check +for null returns from method calls. An error safe implementation can +generate a lot of code like: + +@verbatim +TiXmlElement* root = document.FirstChildElement( "Document" ); +if ( root ) +{ + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. +@endverbatim + +Handles have been introduced to clean this up. Using the TiXmlHandle class, +the previous code reduces to: + +@verbatim +TiXmlHandle docHandle( &document ); +TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); +if ( child2 ) +{ + // do something useful +@endverbatim + +Which is much easier to deal with. See TiXmlHandle for more information. + + +<h3> Row and Column tracking </h3> +Being able to track nodes and attributes back to their origin location +in source files can be very important for some applications. Additionally, +knowing where parsing errors occured in the original source can be very +time saving. + +TinyXML can tracks the row and column origin of all nodes and attributes +in a text file. The TiXmlBase::Row() and TiXmlBase::Column() methods return +the origin of the node in the source text. The correct tabs can be +configured in TiXmlDocument::SetTabSize(). + + +<h2> Using and Installing </h2> + +To Compile and Run xmltest: + +A Linux Makefile and a Windows Visual C++ .dsw file is provided. +Simply compile and run. It will write the file demotest.xml to your +disk and generate output on the screen. It also tests walking the +DOM by printing out the number of nodes found using different +techniques. + +The Linux makefile is very generic and runs on many systems - it +is currently tested on mingw and +MacOSX. You do not need to run 'make depend'. The dependecies have been +hard coded. + +<h3>Windows project file for VC6</h3> +<ul> +<li>tinyxml: tinyxml library, non-STL </li> +<li>tinyxmlSTL: tinyxml library, STL </li> +<li>tinyXmlTest: test app, non-STL </li> +<li>tinyXmlTestSTL: test app, STL </li> +</ul> + +<h3>Makefile</h3> +At the top of the makefile you can set: + +PROFILE, DEBUG, and TINYXML_USE_STL. Details (such that they are) are in +the makefile. + +In the tinyxml directory, type "make clean" then "make". The executable +file 'xmltest' will be created. + + + +<h3>To Use in an Application:</h3> + +Add tinyxml.cpp, tinyxml.h, tinyxmlerror.cpp, tinyxmlparser.cpp, tinystr.cpp, and tinystr.h to your +project or make file. That's it! It should compile on any reasonably +compliant C++ system. You do not need to enable exceptions or +RTTI for TinyXML. + + +<h2> How TinyXML works. </h2> + +An example is probably the best way to go. Take: +@verbatim + <?xml version="1.0" standalone=no> + <!-- Our to do list data --> + <ToDo> + <Item priority="1"> Go to the <bold>Toy store!</bold></Item> + <Item priority="2"> Do bills</Item> + </ToDo> +@endverbatim + +Its not much of a To Do list, but it will do. To read this file +(say "demo.xml") you would create a document, and parse it in: +@verbatim + TiXmlDocument doc( "demo.xml" ); + doc.LoadFile(); +@endverbatim + +And its ready to go. Now lets look at some lines and how they +relate to the DOM. + +@verbatim +<?xml version="1.0" standalone=no> +@endverbatim + + The first line is a declaration, and gets turned into the + TiXmlDeclaration class. It will be the first child of the + document node. + + This is the only directive/special tag parsed by TinyXML. + Generally directive tags are stored in TiXmlUnknown so the + commands wont be lost when it is saved back to disk. + +@verbatim +<!-- Our to do list data --> +@endverbatim + + A comment. Will become a TiXmlComment object. + +@verbatim +<ToDo> +@endverbatim + + The "ToDo" tag defines a TiXmlElement object. This one does not have + any attributes, but does contain 2 other elements. + +@verbatim +<Item priority="1"> +@endverbatim + + Creates another TiXmlElement which is a child of the "ToDo" element. + This element has 1 attribute, with the name "priority" and the value + "1". + +@verbatim +Go to the +@endverbatim + + A TiXmlText. This is a leaf node and cannot contain other nodes. + It is a child of the "Item" TiXmlElement. + +@verbatim +<bold> +@endverbatim + + + Another TiXmlElement, this one a child of the "Item" element. + +Etc. + +Looking at the entire object tree, you end up with: +@verbatim +TiXmlDocument "demo.xml" + TiXmlDeclaration "version='1.0'" "standalone=no" + TiXmlComment " Our to do list data" + TiXmlElement "ToDo" + TiXmlElement "Item" Attribtutes: priority = 1 + TiXmlText "Go to the " + TiXmlElement "bold" + TiXmlText "Toy store!" + TiXmlElement "Item" Attributes: priority=2 + TiXmlText "Do bills" +@endverbatim + +<h2> Documentation </h2> + +The documentation is build with Doxygen, using the 'dox' +configuration file. + +<h2> License </h2> + +TinyXML is released under the zlib license: + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. + +<h2> References </h2> + +The World Wide Web Consortium is the definitive standard body for +XML, and their web pages contain huge amounts of information. + +The definitive spec: <a href="http://www.w3.org/TR/2004/REC-xml-20040204/"> +http://www.w3.org/TR/2004/REC-xml-20040204/</a> + +I also recommend "XML Pocket Reference" by Robert Eckstein and published by +OReilly...the book that got the whole thing started. + +<h2> Contributors, Contacts, and a Brief History </h2> + +Thanks very much to everyone who sends suggestions, bugs, ideas, and +encouragement. It all helps, and makes this project fun. A special thanks +to the contributors on the web pages that keep it lively. + +So many people have sent in bugs and ideas, that rather than list here +we try to give credit due in the "changes.txt" file. + +TinyXML was originally written by Lee Thomason. (Often the "I" still +in the documentation.) Lee reviews changes and releases new versions, +with the help of Yves Berquin, Andrew Ellerton, and the tinyXml community. + +We appreciate your suggestions, and would love to know if you +use TinyXML. Hopefully you will enjoy it and find it useful. +Please post questions, comments, file bugs, or contact us at: + +www.sourceforge.net/projects/tinyxml + +Lee Thomason, Yves Berquin, Andrew Ellerton +*/ diff --git a/mmc_updater/depends/tinyxml/tinystr.cpp b/mmc_updater/depends/tinyxml/tinystr.cpp new file mode 100644 index 00000000..06657682 --- /dev/null +++ b/mmc_updater/depends/tinyxml/tinystr.cpp @@ -0,0 +1,111 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1); + + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/mmc_updater/depends/tinyxml/tinystr.h b/mmc_updater/depends/tinyxml/tinystr.h new file mode 100644 index 00000000..89cca334 --- /dev/null +++ b/mmc_updater/depends/tinyxml/tinystr.h @@ -0,0 +1,305 @@ +/* +www.sourceforge.net/projects/tinyxml + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include <assert.h> +#include <string.h> + +/* The support for explicit isn't that universal, and it isn't really + required - it is used to check that the TiXmlString class isn't incorrectly + used. Be nice to old compilers and macro it here: +*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + #define TIXML_EXPLICIT explicit +#elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + #define TIXML_EXPLICIT explicit +#else + #define TIXML_EXPLICIT +#endif + + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef size_t size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString ( const TiXmlString & copy) : rep_(0) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0) + { + init( static_cast<size_type>( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast<size_type>( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast<int*>( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/mmc_updater/depends/tinyxml/tinyxml.cpp b/mmc_updater/depends/tinyxml/tinyxml.cpp new file mode 100644 index 00000000..9c161dfc --- /dev/null +++ b/mmc_updater/depends/tinyxml/tinyxml.cpp @@ -0,0 +1,1886 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> + +#ifdef TIXML_USE_STL +#include <sstream> +#include <iostream> +#endif + +#include "tinyxml.h" + +FILE* TiXmlFOpen( const char* filename, const char* mode ); + +bool TiXmlBase::condenseWhiteSpace = true; + +// Microsoft compiler security +FILE* TiXmlFOpen( const char* filename, const char* mode ) +{ + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + FILE* fp = 0; + errno_t err = fopen_s( &fp, filename, mode ); + if ( !err && fp ) + return fp; + return 0; + #else + return fopen( filename, mode ); + #endif +} + +void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; + target->location = location; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + assert( node->parent == 0 || node->parent == this ); + assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() ); + + if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + delete node; + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) { + return 0; + } + if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT ) + { + if ( GetDocument() ) + GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( !replaceThis ) + return 0; + + if ( replaceThis->parent != this ) + return 0; + + if ( withThis.ToDocument() ) { + // A document can never be a child. Thanks to Noam. + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( !removeThis ) { + return false; + } + + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + #ifdef TIXML_USE_STL + TIXML_STRING str( name ); + TiXmlAttribute* node = attributeSet.Find( str ); + #else + TiXmlAttribute* node = attributeSet.Find( name ); + #endif + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::TINYXML_ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); + return *this; +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char* TiXmlElement::Attribute( const char* name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + return node->Value(); + return 0; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( attrib ) + return &attrib->ValueStr(); + return 0; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( i ) { + attrib->QueryIntValue( i ); + } + } + return result; +} +#endif + + +const char* TiXmlElement::Attribute( const char* name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const char* result = 0; + + if ( attrib ) { + result = attrib->Value(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} + + +#ifdef TIXML_USE_STL +const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + const std::string* result = 0; + + if ( attrib ) { + result = &attrib->ValueStr(); + if ( d ) { + attrib->QueryDoubleValue( d ); + } + } + return result; +} +#endif + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int ival = 0; + int result = node->QueryIntValue( &ival ); + *value = (unsigned)ival; + return result; +} + + +int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + int result = TIXML_WRONG_TYPE; + if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = true; + result = TIXML_SUCCESS; + } + else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN ) + || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) ) + { + *bval = false; + result = TIXML_SUCCESS; + } + return result; +} + + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryIntValue( ival ); +} +#endif + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} + + +#ifdef TIXML_USE_STL +int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const +{ + const TiXmlAttribute* attrib = attributeSet.Find( name ); + if ( !attrib ) + return TIXML_NO_ATTRIBUTE; + return attrib->QueryDoubleValue( dval ); +} +#endif + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& name, int val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetIntValue( val ); + } +} +#endif + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetDoubleAttribute( const std::string& name, double val ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( name ); + if ( attrib ) { + attrib->SetDoubleValue( val ); + } +} +#endif + + +void TiXmlElement::SetAttribute( const char * cname, const char * cvalue ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname ); + if ( attrib ) { + attrib->SetValue( cvalue ); + } +} + + +#ifdef TIXML_USE_STL +void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value ) +{ + TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name ); + if ( attrib ) { + attrib->SetValue( _value ); + } +} +#endif + + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + assert( cfile ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + + fprintf( cfile, "<%s", value.c_str() ); + + const TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a <foo /> node + // 2) An element with only a text child is printed as <foo> text </foo> + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "</%s>", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i<depth; ++i ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "</%s>", value.c_str() ); + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + +bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this, attributeSet.First() ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + return LoadFile( Value(), encoding ); +} + + +bool TiXmlDocument::SaveFile() const +{ + return SaveFile( Value() ); +} + +bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding ) +{ + TIXML_STRING filename( _filename ); + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = TiXmlFOpen( value.c_str (), "rb" ); + + if ( file ) + { + bool result = LoadFile( file, encoding ); + fclose( file ); + return result; + } + else + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } +} + +bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding ) +{ + if ( !file ) + { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Delete the existing data: + Clear(); + location.Clear(); + + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length <= 0 ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // <snip> + // <quote> + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // </quote> + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + delete [] buf; + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; + } + + // Process the buffer in place to normalize new lines. (See comment above.) + // Copies from the 'p' to 'q' pointer, where p can advance faster if + // a newline-carriage return is hit. + // + // Wikipedia: + // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or + // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)... + // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others + // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS + // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9 + + const char* p = buf; // the read head + char* q = buf; // the write head + const char CR = 0x0d; + const char LF = 0x0a; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + assert( q <= (buf+length) ); + assert( q <= p ); + + if ( *p == CR ) { + *q++ = LF; + p++; + if ( *p == LF ) { // check for CR+LF (and skip LF) + p++; + } + } + else { + *q++ = *p++; + } + } + assert( q <= (buf+length) ); + *q = 0; + + Parse( buf, 0, encoding ); + + delete [] buf; + return !Error(); +} + + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = TiXmlFOpen( filename, "w" ); + if ( fp ) + { + bool result = SaveFile( fp ); + fclose( fp ); + return result; + } + return false; +} + + +bool TiXmlDocument::SaveFile( FILE* fp ) const +{ + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + return (ferror(fp) == 0); +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorId = errorId; + target->errorDesc = errorDesc; + target->tabsize = tabsize; + target->errorLocation = errorLocation; + target->useMicrosoftBOM = useMicrosoftBOM; + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + + +bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const +{ + if ( visitor->VisitEnter( *this ) ) + { + for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() ) + { + if ( !node->Accept( visitor ) ) + break; + } + } + return visitor->VisitExit( *this ); +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +/* +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} +*/ + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +/* +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} +*/ + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + TIXML_STRING n, v; + + EncodeString( name, &n ); + EncodeString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) { + if ( cfile ) { + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\""; + } + } + else { + if ( cfile ) { + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); + } + if ( str ) { + (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'"; + } + } +} + + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value); + #else + sprintf (buf, "%g", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) +{ + copy.CopyTo( this ); +} + + +TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); + return *this; +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + for ( int i=0; i<depth; i++ ) + { + fprintf( cfile, " " ); + } + fprintf( cfile, "<!--%s-->", value.c_str() ); +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + assert( cfile ); + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i<depth; i++ ) { + fprintf( cfile, " " ); + } + fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output + } + else + { + TIXML_STRING buffer; + EncodeString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +bool TiXmlText::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) +{ + copy.CopyTo( this ); +} + + +TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); + return *this; +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const +{ + if ( cfile ) fprintf( cfile, "<?xml " ); + if ( str ) (*str) += "<?xml "; + + if ( !version.empty() ) { + if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ()); + if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; } + } + if ( !encoding.empty() ) { + if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ()); + if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; } + } + if ( !standalone.empty() ) { + if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ()); + if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; } + } + if ( cfile ) fprintf( cfile, "?>" ); + if ( str ) (*str) += "?>"; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i<depth; i++ ) + fprintf( cfile, " " ); + fprintf( cfile, "<%s>", value.c_str() ); +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const +{ + return visitor->Visit( *this ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + #ifdef TIXML_USE_STL + assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set. + #else + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + #endif + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + + +#ifdef TIXML_USE_STL +TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} +#endif + + +TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const +{ + for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( strcmp( node->name.c_str(), name ) == 0 ) + return node; + } + return 0; +} + + +TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name ) +{ + TiXmlAttribute* attrib = Find( _name ); + if ( !attrib ) { + attrib = new TiXmlAttribute(); + Add( attrib ); + attrib->SetName( _name ); + } + return attrib; +} + + +#ifdef TIXML_USE_STL +std::istream& operator>> (std::istream & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +#ifdef TIXML_USE_STL +std::ostream& operator<< (std::ostream & out, const TiXmlNode & base) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out << printer.Str(); + + return out; +} + + +std::string& operator<< (std::string& out, const TiXmlNode& base ) +{ + TiXmlPrinter printer; + printer.SetStreamPrinting(); + base.Accept( &printer ); + out.append( printer.Str() ); + + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && i<count; + child = child->NextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && i<count; + child = child->NextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && i<count; + child = child->NextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && i<count; + child = child->NextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +bool TiXmlPrinter::VisitEnter( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitExit( const TiXmlDocument& ) +{ + return true; +} + +bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ) +{ + DoIndent(); + buffer += "<"; + buffer += element.Value(); + + for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() ) + { + buffer += " "; + attrib->Print( 0, 0, &buffer ); + } + + if ( !element.FirstChild() ) + { + buffer += " />"; + DoLineBreak(); + } + else + { + buffer += ">"; + if ( element.FirstChild()->ToText() + && element.LastChild() == element.FirstChild() + && element.FirstChild()->ToText()->CDATA() == false ) + { + simpleTextPrint = true; + // no DoLineBreak()! + } + else + { + DoLineBreak(); + } + } + ++depth; + return true; +} + + +bool TiXmlPrinter::VisitExit( const TiXmlElement& element ) +{ + --depth; + if ( !element.FirstChild() ) + { + // nothing. + } + else + { + if ( simpleTextPrint ) + { + simpleTextPrint = false; + } + else + { + DoIndent(); + } + buffer += "</"; + buffer += element.Value(); + buffer += ">"; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlText& text ) +{ + if ( text.CDATA() ) + { + DoIndent(); + buffer += "<![CDATA["; + buffer += text.Value(); + buffer += "]]>"; + DoLineBreak(); + } + else if ( simpleTextPrint ) + { + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + } + else + { + DoIndent(); + TIXML_STRING str; + TiXmlBase::EncodeString( text.ValueTStr(), &str ); + buffer += str; + DoLineBreak(); + } + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration ) +{ + DoIndent(); + declaration.Print( 0, 0, &buffer ); + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlComment& comment ) +{ + DoIndent(); + buffer += "<!--"; + buffer += comment.Value(); + buffer += "-->"; + DoLineBreak(); + return true; +} + + +bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown ) +{ + DoIndent(); + buffer += "<"; + buffer += unknown.Value(); + buffer += ">"; + DoLineBreak(); + return true; +} + diff --git a/mmc_updater/depends/tinyxml/tinyxml.h b/mmc_updater/depends/tinyxml/tinyxml.h new file mode 100644 index 00000000..a3589e5b --- /dev/null +++ b/mmc_updater/depends/tinyxml/tinyxml.h @@ -0,0 +1,1805 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#ifdef TIXML_USE_STL + #include <string> + #include <iostream> + #include <sstream> + #define TIXML_STRING std::string +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE +#define TIXML_SAFE + +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1400 ) + // Microsoft visual studio, version 2005 and higher. + #define TIXML_SNPRINTF _snprintf_s + #define TIXML_SSCANF sscanf_s + #elif defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SSCANF sscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #else + #define TIXML_SNPRINTF snprintf + #define TIXML_SSCANF sscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 6; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +/** + Implements the interface to the "Visitor pattern" (see the Accept() method.) + If you call the Accept() method, it requires being passed a TiXmlVisitor + class to handle callbacks. For nodes that contain other nodes (Document, Element) + you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves + are simply called with Visit(). + + If you return 'true' from a Visit method, recursive parsing will continue. If you return + false, <b>no children of this node or its sibilings</b> will be Visited. + + All flavors of Visit methods have a default implementation that returns 'true' (continue + visiting). You need to only override methods that are interesting to you. + + Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting. + + You should never change the document from a callback. + + @sa TiXmlNode::Accept() +*/ +class TiXmlVisitor +{ +public: + virtual ~TiXmlVisitor() {} + + /// Visit a document. + virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; } + /// Visit a document. + virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; } + + /// Visit an element. + virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; } + /// Visit an element. + virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; } + + /// Visit a declaration + virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; } + /// Visit a text node + virtual bool Visit( const TiXmlText& /*text*/ ) { return true; } + /// Visit a comment node + virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; } + /// Visit an unknown node + virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; } +}; + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream + or the string class (TiXmlString in non-STL mode, std::string + in STL mode.) Either or both cfile and str can be null. + + This is a formatted print, and will insert + tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + value is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data. + void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data. + const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data. + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc, + or they will be transformed into entities! + */ + static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out ); + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + TIXML_ERROR_DOCUMENT_TOP_ONLY, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + inline static bool IsWhiteSpace( int c ) + { + if ( c < 256 ) + return IsWhiteSpace( (char) c ); + return false; // Again, only truly correct for English/Latin...but usually works. + } + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ); + static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((const unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + TINYXML_DOCUMENT, + TINYXML_ELEMENT, + TINYXML_COMMENT, + TINYXML_UNKNOWN, + TINYXML_TEXT, + TINYXML_DECLARATION, + TINYXML_TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + const TIXML_STRING& ValueTStr() const { return value; } + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + /// The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * _value ) { + // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe) + // call the method, cast the return back to non-const. + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value )); + } + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * _value ) { + return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value )); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) ); + } + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char *_prev ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char* _next ) { + return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() ); + } + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char *_next ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement() { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() ); + } + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * _value ) const; + TiXmlElement* FirstChildElement( const char * _value ) { + return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) ); + } + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT, + TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument() { + return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() ); + } + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + + /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the + XML tree will be conditionally visited and the host will be called back + via the TiXmlVisitor interface. + + This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse + the XML for the callbacks, so the performance of TinyXML is unchanged by using this + interface versus any other.) + + The interface has been based on ideas from: + + - http://www.saxproject.org/ + - http://c2.com/cgi/wiki?HierarchicalVisitorPattern + + Which are both good references for "visiting". + + An example of using Accept(): + @verbatim + TiXmlPrinter printer; + tinyxmlDoc.Accept( &printer ); + const char* xmlcstr = printer.CStr(); + @endverbatim + */ + virtual bool Accept( TiXmlVisitor* visitor ) const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str(); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str(); } ///< Return the value of this attribute. + #ifdef TIXML_USE_STL + const std::string& ValueStr() const { return value; } ///< Return the value of this attribute. + #endif + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + // Get the tinyxml string representation + const TIXML_STRING& NameTStr() const { return name; } + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) { name = _name; } + /// STL std::string form. + void SetValue( const std::string& _value ) { value = _value; } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() ); + } + + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous() { + return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() ); + } + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + TiXmlAttribute* Find( const char* _name ) const; + TiXmlAttribute* FindOrCreate( const char* _name ); + +# ifdef TIXML_USE_STL + TiXmlAttribute* Find( const std::string& _name ) const; + TiXmlAttribute* FindOrCreate( const std::string& _name ); +# endif + + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + TiXmlElement& operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute(). + int QueryUnsignedAttribute( const char* name, unsigned* _value ) const; + /** QueryBoolAttribute examines the attribute - see QueryIntAttribute(). + Note that '1', 'true', or 'yes' are considered true, while '0', 'false' + and 'no' are considered false. + */ + int QueryBoolAttribute( const char* name, bool* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + #ifdef TIXML_USE_STL + /// QueryStringAttribute examines the attribute - see QueryIntAttribute(). + int QueryStringAttribute( const char* name, std::string* _value ) const { + const char* cstr = Attribute( name ); + if ( cstr ) { + *_value = std::string( cstr ); + return TIXML_SUCCESS; + } + return TIXML_NO_ATTRIBUTE; + } + + /** Template form of the attribute query which will try to read the + attribute into the specified type. Very easy, very powerful, but + be careful to make sure to call this with the correct type. + + NOTE: This method doesn't work correctly for 'string' types that contain spaces. + + @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE + */ + template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + std::stringstream sstream( node->ValueStr() ); + sstream >> *outValue; + if ( !sstream.fail() ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; + } + + int QueryValueAttribute( const std::string& name, std::string* outValue ) const + { + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + *outValue = node->ValueStr(); + return TIXML_SUCCESS; + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const std::string* Attribute( const std::string& name ) const; + const std::string* Attribute( const std::string& name, int* i ) const; + const std::string* Attribute( const std::string& name, double* d ) const; + int QueryIntAttribute( const std::string& name, int* _value ) const; + int QueryDoubleAttribute( const std::string& name, double* _value ) const; + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ); + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ); + ///< STL std::string form. + void SetDoubleAttribute( const std::string& name, double value ); + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + <foo>This is text</foo> + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + <foo><b>This is text</b></foo> + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + <foo>This is <b>text</b></foo> + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {} + /// Construct a comment from text. + TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) { + SetValue( _value ); + } + TiXmlComment( const TiXmlComment& ); + TiXmlComment& operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + // Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif +// virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); } + TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; } + + // Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() const { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + <?xml version="1.0" standalone="yes"?> + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + TiXmlDeclaration& operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + // Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const; + virtual void Print( FILE* cfile, int depth ) const { + Print( cfile, depth, 0 ); + } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* visitor ) const; + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); } + TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + // Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + TiXmlDocument& operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + /** Load a file using the given FILE*. Returns true if successful. Note that this method + doesn't stream - the entire object pointed at by the FILE* + will be interpreted as an XML file. TinyXML doesn't stream in XML from the current + file location. Streaming may be added in the future. + */ + bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given FILE*. Returns true if successful. + bool SaveFile( FILE* ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + return LoadFile( filename.c_str(), encoding ); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + return SaveFile( filename.c_str() ); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() const { return errorLocation.row+1; } + int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Write the document to standard out using formatted printing ("pretty print"). */ + void Print() const { Print( stdout, 0 ); } + + /* Write the document to a string using formatted printing ("pretty print"). This + will allocate a character array (new char[]) and return it as a pointer. The + calling code pust call delete[] on the return char* to avoid a memory leak. + */ + //char* PrintToMemory() const; + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + + virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Walk the XML tree visiting this node and all of its children. + */ + virtual bool Accept( TiXmlVisitor* content ) const; + +protected : + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( std::istream * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + <Document> + <Element attributeA = "valueA"> + <Child attributeB = "value1" /> + <Child attributeB = "value2" /> + </Element> + <Document> + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /** Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* ToNode() const { return node; } + /** Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /** Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /** Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + + /** @deprecated use ToNode. + Return the handle as a TiXmlNode. This may return null. + */ + TiXmlNode* Node() const { return ToNode(); } + /** @deprecated use ToElement. + Return the handle as a TiXmlElement. This may return null. + */ + TiXmlElement* Element() const { return ToElement(); } + /** @deprecated use ToText() + Return the handle as a TiXmlText. This may return null. + */ + TiXmlText* Text() const { return ToText(); } + /** @deprecated use ToUnknown() + Return the handle as a TiXmlUnknown. This may return null. + */ + TiXmlUnknown* Unknown() const { return ToUnknown(); } + +private: + TiXmlNode* node; +}; + + +/** Print to memory functionality. The TiXmlPrinter is useful when you need to: + + -# Print to memory (especially in non-STL mode) + -# Control formatting (line endings, etc.) + + When constructed, the TiXmlPrinter is in its default "pretty printing" mode. + Before calling Accept() you can call methods to control the printing + of the XML document. After TiXmlNode::Accept() is called, the printed document can + be accessed via the CStr(), Str(), and Size() methods. + + TiXmlPrinter uses the Visitor API. + @verbatim + TiXmlPrinter printer; + printer.SetIndent( "\t" ); + + doc.Accept( &printer ); + fprintf( stdout, "%s", printer.CStr() ); + @endverbatim +*/ +class TiXmlPrinter : public TiXmlVisitor +{ +public: + TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ), + buffer(), indent( " " ), lineBreak( "\n" ) {} + + virtual bool VisitEnter( const TiXmlDocument& doc ); + virtual bool VisitExit( const TiXmlDocument& doc ); + + virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute ); + virtual bool VisitExit( const TiXmlElement& element ); + + virtual bool Visit( const TiXmlDeclaration& declaration ); + virtual bool Visit( const TiXmlText& text ); + virtual bool Visit( const TiXmlComment& comment ); + virtual bool Visit( const TiXmlUnknown& unknown ); + + /** Set the indent characters for printing. By default 4 spaces + but tab (\t) is also useful, or null/empty string for no indentation. + */ + void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; } + /// Query the indention string. + const char* Indent() { return indent.c_str(); } + /** Set the line breaking string. By default set to newline (\n). + Some operating systems prefer other characters, or can be + set to the null/empty string for no indenation. + */ + void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; } + /// Query the current line breaking string. + const char* LineBreak() { return lineBreak.c_str(); } + + /** Switch over to "stream printing" which is the most dense formatting without + linebreaks. Common when the XML is needed for network transmission. + */ + void SetStreamPrinting() { indent = ""; + lineBreak = ""; + } + /// Return the result. + const char* CStr() { return buffer.c_str(); } + /// Return the length of the result string. + size_t Size() { return buffer.size(); } + + #ifdef TIXML_USE_STL + /// Return the result. + const std::string& Str() { return buffer; } + #endif + +private: + void DoIndent() { + for( int i=0; i<depth; ++i ) + buffer += indent; + } + void DoLineBreak() { + buffer += lineBreak; + } + + int depth; + bool simpleTextPrint; + TIXML_STRING buffer; + TIXML_STRING indent; + TIXML_STRING lineBreak; +}; + + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif diff --git a/mmc_updater/depends/tinyxml/tinyxmlerror.cpp b/mmc_updater/depends/tinyxml/tinyxmlerror.cpp new file mode 100644 index 00000000..538c21d0 --- /dev/null +++ b/mmc_updater/depends/tinyxml/tinyxmlerror.cpp @@ -0,0 +1,52 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + +// The goal of the seperate error file is to make the first +// step towards localization. tinyxml (currently) only supports +// english error messages, but the could now be translated. +// +// It also cleans up the code a bit. +// + +const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] = +{ + "No error", + "Error", + "Failed to open file", + "Error parsing Element.", + "Failed to read Element name", + "Error reading Element value.", + "Error reading Attributes.", + "Error: empty tag.", + "Error reading end tag.", + "Error parsing Unknown.", + "Error parsing Comment.", + "Error parsing Declaration.", + "Error document empty.", + "Error null (0) or unexpected EOF found in input stream.", + "Error parsing CDATA.", + "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.", +}; diff --git a/mmc_updater/depends/tinyxml/tinyxmlparser.cpp b/mmc_updater/depends/tinyxml/tinyxmlparser.cpp new file mode 100644 index 00000000..81b7eae9 --- /dev/null +++ b/mmc_updater/depends/tinyxml/tinyxmlparser.cpp @@ -0,0 +1,1638 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code by Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include <ctype.h> +#include <stddef.h> + +#include "tinyxml.h" + +//#define DEBUG_PARSER +#if defined( DEBUG_PARSER ) +# if defined( DEBUG ) && defined( _MSC_VER ) +# include <windows.h> +# define TIXML_LOG OutputDebugString +# else +# define TIXML_LOG printf +# endif +#endif + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() const { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The +// "assign" optimization removes over 10% of the execution time. +// +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + // Oddly, not supported on some comilers, + //name->clear(); + // So use this: + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + const char* start = p; + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + //(*name) += *p; // expensive + ++p; + } + if ( p-start > 0 ) { + name->assign( start, p-start ); + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; i<NUM_ENTITY; ++i ) + { + if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 ) + { + assert( strlen( entity[i].str ) == entity[i].strLength ); + *value = entity[i].chr; + *length = 1; + return ( p + entity[i].strLength ); + } + } + + // So it wasn't an entity, its unrecognized, or something like that. + *value = *p; // Don't put back the last one, since we return it! + //*length = 1; // Leave unrecognized entities - this doesn't really work. + // Just writes strange XML. + return p+1; +} + + +bool TiXmlBase::StringEqual( const char* p, + const char* tag, + bool ignoreCase, + TiXmlEncoding encoding ) +{ + assert( p ); + assert( tag ); + if ( !p || !*p ) + { + assert( 0 ); + return false; + } + + const char* q = p; + + if ( ignoreCase ) + { + while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) + return true; + } + else + { + while ( *q && *tag && *q == *tag ) + { + ++q; + ++tag; + } + + if ( *tag == 0 ) // Have we found the end of the tag, and everything equal? + return true; + } + return false; +} + +const char* TiXmlBase::ReadText( const char* p, + TIXML_STRING * text, + bool trimWhiteSpace, + const char* endTag, + bool caseInsensitive, + TiXmlEncoding encoding ) +{ + *text = ""; + if ( !trimWhiteSpace // certain tags always keep whitespace + || !condenseWhiteSpace ) // if true, whitespace is always kept + { + // Keep all the white space. + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) + ) + { + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + text->append( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + if ( p && *p ) + p += strlen( endTag ); + return ( p && *p ) ? p : 0; +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: <!-- + // - Decleration: <?xml + // - Everthing else is unknown to tinyxml. + // + + const char* xmlHeader = { "<?xml" }; + const char* commentHeader = { "<!--" }; + const char* dtdHeader = { "<!" }; + const char* cdataHeader = { "<![CDATA[" }; + + if ( StringEqual( p, xmlHeader, true, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Declaration\n" ); + #endif + returnNode = new TiXmlDeclaration(); + } + else if ( StringEqual( p, commentHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Comment\n" ); + #endif + returnNode = new TiXmlComment(); + } + else if ( StringEqual( p, cdataHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing CDATA\n" ); + #endif + TiXmlText* text = new TiXmlText( "" ); + text->SetCDATA( true ); + returnNode = text; + } + else if ( StringEqual( p, dtdHeader, false, encoding ) ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(1)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + else if ( IsAlpha( *(p+1), encoding ) + || *(p+1) == '_' ) + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Element\n" ); + #endif + returnNode = new TiXmlElement( "" ); + } + else + { + #ifdef DEBUG_PARSER + TIXML_LOG( "XML parsing Unknown(2)\n" ); + #endif + returnNode = new TiXmlUnknown(); + } + + if ( returnNode ) + { + // Set the parent, so it can report errors + returnNode->parent = this; + } + return returnNode; +} + +#ifdef TIXML_USE_STL + +void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag) +{ + // We're called with some amount of pre-parsing. That is, some of "this" + // element is in "tag". Go ahead and stream to the closing ">" + while( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c ; + + if ( c == '>' ) + break; + } + + if ( tag->length() < 3 ) return; + + // Okay...if we are a "/>" tag, then we're done. We've read a complete tag. + // If not, identify and stream. + + if ( tag->at( tag->length() - 1 ) == '>' + && tag->at( tag->length() - 2 ) == '/' ) + { + // All good! + return; + } + else if ( tag->at( tag->length() - 1 ) == '>' ) + { + // There is more. Could be: + // text + // cdata text (which looks like another node) + // closing tag + // another node. + for ( ;; ) + { + StreamWhiteSpace( in, tag ); + + // Do we have text? + if ( in->good() && in->peek() != '<' ) + { + // Yep, text. + TiXmlText text( "" ); + text.StreamIn( in, tag ); + + // What follows text is a closing tag or another node. + // Go around again and figure it out. + continue; + } + + // We now have either a closing tag...or another node. + // We should be at a "<", regardless. + if ( !in->good() ) return; + assert( in->peek() == '<' ); + int tagIndex = (int) tag->length(); + + bool closingTag = false; + bool firstCharFound = false; + + for( ;; ) + { + if ( !in->good() ) + return; + + int c = in->peek(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + if ( c == '>' ) + break; + + *tag += (char) c; + in->get(); + + // Early out if we find the CDATA id. + if ( c == '[' && tag->size() >= 9 ) + { + size_t len = tag->size(); + const char* start = tag->c_str() + len - 9; + if ( strcmp( start, "<![CDATA[" ) == 0 ) { + assert( !closingTag ); + break; + } + } + + if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) ) + { + firstCharFound = true; + if ( c == '/' ) + closingTag = true; + } + } + // If it was a closing tag, then read in the closing '>' to clean up the input stream. + // If it was not, the streaming will be done by the tag. + if ( closingTag ) + { + if ( !in->good() ) + return; + + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + assert( c == '>' ); + *tag += (char) c; + + // We are done, once we've found our closing tag. + return; + } + else + { + // If not a closing tag, id it, and stream. + const char* tagloc = tag->c_str() + tagIndex; + TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING ); + if ( !node ) + return; + node->StreamIn( in, tag ); + delete node; + node = 0; + + // No return: go around from the beginning: text, closing tag, or node. + } + } + } +} +#endif + +const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + TiXmlDocument* document = GetDocument(); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding ); + return 0; + } + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + if ( *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding ); + return 0; + } + + p = SkipWhiteSpace( p+1, encoding ); + + // Read the name. + const char* pErr = p; + + p = ReadName( p, &value, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding ); + return 0; + } + + TIXML_STRING endTag ("</"); + endTag += value; + + // Check for and read attributes. Also look for an empty + // tag or an end tag. + while ( p && *p ) + { + pErr = p; + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + if ( *p == '/' ) + { + ++p; + // Empty tag. + if ( *p != '>' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding ); + return 0; + } + return (p+1); + } + else if ( *p == '>' ) + { + // Done with attributes (if there were any.) + // Read the value -- which can include other + // elements -- read the end tag, and return. + ++p; + p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens. + if ( !p || !*p ) { + // We were looking for the end tag, but found nothing. + // Fix for [ 1663758 ] Failure to report error on bad XML + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + + // We should find the end tag now + // note that: + // </foo > and + // </foo> + // are both valid end tags. + if ( StringEqual( p, endTag.c_str(), false, encoding ) ) + { + p += endTag.length(); + p = SkipWhiteSpace( p, encoding ); + if ( p && *p && *p == '>' ) { + ++p; + return p; + } + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + else + { + if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding ); + return 0; + } + } + else + { + // Try to read an attribute: + TiXmlAttribute* attrib = new TiXmlAttribute(); + if ( !attrib ) + { + return 0; + } + + attrib->SetDocument( document ); + pErr = p; + p = attrib->Parse( p, data, encoding ); + + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + // Handle the strange case of double attributes: + #ifdef TIXML_USE_STL + TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() ); + #else + TiXmlAttribute* node = attributeSet.Find( attrib->Name() ); + #endif + if ( node ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding ); + delete attrib; + return 0; + } + + attributeSet.Add( attrib ); + } + } + return p; +} + + +const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + + // Read in text and elements in any order. + const char* pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + + while ( p && *p ) + { + if ( *p != '<' ) + { + // Take what we have, make a text element. + TiXmlText* textNode = new TiXmlText( "" ); + + if ( !textNode ) + { + return 0; + } + + if ( TiXmlBase::IsWhiteSpaceCondensed() ) + { + p = textNode->Parse( p, data, encoding ); + } + else + { + // Special case: we want to keep the white space + // so that leading spaces aren't removed. + p = textNode->Parse( pWithWhiteSpace, data, encoding ); + } + + if ( !textNode->Blank() ) + LinkEndChild( textNode ); + else + delete textNode; + } + else + { + // We hit a '<' + // Have we hit a new element or an end tag? This could also be + // a TiXmlText in the "CDATA" style. + if ( StringEqual( p, "</", false, encoding ) ) + { + return p; + } + else + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, data, encoding ); + LinkEndChild( node ); + } + else + { + return 0; + } + } + } + pWithWhiteSpace = p; + p = SkipWhiteSpace( p, encoding ); + } + + if ( !p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding ); + } + return p; +} + + +#ifdef TIXML_USE_STL +void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + if ( !p || !*p || *p != '<' ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding ); + return 0; + } + ++p; + value = ""; + + while ( p && *p && *p != '>' ) + { + value += *p; + ++p; + } + + if ( !p ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding ); + } + if ( p && *p == '>' ) + return p+1; + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + + if ( c == '>' + && tag->at( tag->length() - 2 ) == '-' + && tag->at( tag->length() - 3 ) == '-' ) + { + // All is well. + return; + } + } +} +#endif + + +const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + TiXmlDocument* document = GetDocument(); + value = ""; + + p = SkipWhiteSpace( p, encoding ); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + const char* startTag = "<!--"; + const char* endTag = "-->"; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // [ 1475201 ] TinyXML parses entities in comments + // Oops - ReadText doesn't work, because we don't want to parse the entities. + // p = ReadText( p, &value, false, endTag, false, encoding ); + // + // from the XML spec: + /* + [Definition: Comments may appear anywhere in a document outside other markup; in addition, + they may appear within the document type declaration at places allowed by the grammar. + They are not part of the document's character data; an XML processor MAY, but need not, + make it possible for an application to retrieve the text of comments. For compatibility, + the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity + references MUST NOT be recognized within comments. + + An example of a comment: + + <!-- declarations for <head> & <body> --> + */ + + value = ""; + // Keep all the white space. + while ( p && *p && !StringEqual( p, endTag, false, encoding ) ) + { + value.append( p, 1 ); + ++p; + } + if ( p && *p ) + p += strlen( endTag ); + + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + const char SINGLE_QUOTE = '\''; + const char DOUBLE_QUOTE = '\"'; + + if ( *p == SINGLE_QUOTE ) + { + ++p; + end = "\'"; // single quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == DOUBLE_QUOTE ) + { + ++p; + end = "\""; // double quote in string + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) // whitespace + && *p != '/' && *p != '>' ) // tag end + { + if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) { + // [ 1451649 ] Attribute values with trailing quotes not handled correctly + // We did not have an opening quote but seem to have a + // closing one. Give up and throw an error. + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->peek(); + if ( !cdata && (c == '<' ) ) + { + return; + } + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); // "commits" the peek made above + + if ( cdata && c == '>' && tag->size() >= 3 ) { + size_t len = tag->size(); + if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) { + // terminator of cdata. + return; + } + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = "<![CDATA["; + const char* const endTag = "]]>"; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + if ( document ) + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p && *p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) ) + { + if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i<value.length(); i++ ) + if ( !IsWhiteSpace( value[i] ) ) + return false; + return true; +} + diff --git a/mmc_updater/depends/win32cpp/controls.h b/mmc_updater/depends/win32cpp/controls.h new file mode 100644 index 00000000..a15c8b5f --- /dev/null +++ b/mmc_updater/depends/win32cpp/controls.h @@ -0,0 +1,1074 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// controls.h +// Declaration of the following classes: +// CAnimation, CComboBox, CComboBoxEx, CProgressBar, +// CScrollBar, CSlider, CSpinButton + + +#ifndef _WIN32XX_CONTROLS_H_ +#define _WIN32XX_CONTROLS_H_ + +#include "wincore.h" + +namespace Win32xx +{ + class CAnimation : public CWnd + { + public: + CAnimation() {} + virtual ~CAnimation() {} + + BOOL Close() const; + BOOL Open(LPTSTR lpszName) const; + BOOL Play(UINT wFrom, UINT wTo, UINT cRepeat) const; + BOOL Seek(UINT wFrame) const; + BOOL Stop() const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = ANIMATE_CLASS; } + }; + + + class CComboBox : public CWnd + { + public: + CComboBox() {} + virtual ~CComboBox() {} + + int AddString(LPCTSTR lpszString) const; + void Clear() const; + void Copy() const; + void Cut() const; + int DeleteString(int nIndex) const; + int Dir(UINT attr, LPCTSTR lpszWildCard ) const; + int FindString(int nIndexStart, LPCTSTR lpszString) const; + int FindStringExact(int nIndexStart, LPCTSTR lpszString) const; + int GetCount() const; + int GetCurSel() const; + CRect GetDroppedControlRect() const; + BOOL GetDroppedState() const; + int GetDroppedWidth() const; + DWORD GetEditSel() const; + BOOL GetExtendedUI() const; + int GetHorizontalExtent() const; + DWORD GetItemData(int nIndex) const; + int GetItemHeight(int nIndex) const; + int GetLBText(int nIndex, LPTSTR lpszText) const; + int GetLBTextLen(int nIndex) const; + LCID GetLocale() const; + int GetTopIndex() const; + int InitStorage(int nItems, int nBytes) const; + int InsertString(int nIndex, LPCTSTR lpszString) const; + void LimitText(int nMaxChars) const; + void Paste() const; + void ResetContent() const; + int SelectString(int nStartAfter, LPCTSTR lpszString) const; + int SetCurSel(int nIndex) const; + int SetDroppedWidth(int nWidth) const; + BOOL SetEditSel(int nStartChar, int nEndChar) const; + int SetExtendedUI(BOOL bExtended = TRUE) const; + void SetHorizontalExtent(UINT nExtent ) const; + int SetItemData(int nIndex, DWORD dwItemData) const; + int SetItemHeight(int nIndex, UINT cyItemHeight) const; + LCID SetLocale( LCID NewLocale ) const; + int SetTopIndex(int nIndex) const; + void ShowDropDown(BOOL bShow = TRUE) const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = _T("ComboBox"); } + }; + + + class CComboBoxEx : public CWnd + { + public: + CComboBoxEx() {} + virtual ~CComboBoxEx() {} + + int DeleteItem(int nIndex ) const; + CWnd* GetComboBoxCtrl() const; + CWnd* GetEditCtrl() const; + DWORD GetExtendedStyle() const; + HIMAGELIST GetImageList() const; + BOOL GetItem(COMBOBOXEXITEM* pCBItem) const; + BOOL HasEditChanged () const; + int InsertItem(COMBOBOXEXITEM* lpcCBItem) const; + DWORD SetExtendedStyle(DWORD dwExMask, DWORD dwExStyles ) const; + HIMAGELIST SetImageList(HIMAGELIST himl) const; + BOOL SetItem(PCOMBOBOXEXITEM lpcCBItem) const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = WC_COMBOBOXEX; } + }; + + + class CProgressBar : public CWnd + { + public: + CProgressBar() {} + virtual ~CProgressBar() {} + + int GetPos() const; + int GetRange(BOOL fWhichLimit, PPBRANGE ppBRange) const; + int OffsetPos(int nIncrement) const; + int SetPos(int nNewPos) const; + int SetRange(short nMinRange, short nMaxRange) const; + int SetStep(int nStepInc) const; + int StepIt() const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = PROGRESS_CLASS; } + }; + + + class CScrollBar : public CWnd + { + public: + CScrollBar() {} + virtual ~CScrollBar() {} + + BOOL EnableScrollBar( UINT nArrowFlags = ESB_ENABLE_BOTH ) const; + BOOL GetScrollInfo(LPSCROLLINFO lpsi) const; + int GetScrollPos() const; + BOOL GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos ) const; + BOOL SetScrollInfo(LPSCROLLINFO lpsi, BOOL bRedraw = TRUE ) const; + int SetScrollPos(int nPos, BOOL bRedraw) const; + BOOL SetScrollRange( int nMinPos, int nMaxPos, BOOL bRedraw = TRUE ) const; + BOOL ShowScrollBar(BOOL bShow) const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = _T("SCROLLBAR"); ; } + }; + + + class CSlider : public CWnd + { + public: + CSlider() {} + virtual ~CSlider() {} + + void ClearSel() const; + void ClearTics(BOOL bRedraw = FALSE ) const; + CWnd* GetBuddy(BOOL fLocation = TRUE ) const; + CRect GetChannelRect() const; + int GetLineSize() const; + int GetNumTics() const; + int GetPageSize() const; + int GetPos() const; + int GetRangeMax() const; + int GetRangeMin() const; + int GetSelEnd() const; + int GetSelStart() const; + int GetThumbLength() const; + CRect GetThumbRect() const; + int GetTic(int nTic ) const; + int GetTicPos(int nTic) const; + CWnd* GetToolTips() const; + CWnd* SetBuddy(CWnd* pBuddy, BOOL fLocation = TRUE ) const; + int SetLineSize(int nSize) const; + int SetPageSize(int nSize) const; + void SetPos(int nPos, BOOL bRedraw = FALSE) const; + void SetRangeMax(int nMax, BOOL bRedraw = FALSE) const; + void SetRangeMin(int nMax, BOOL bRedraw = FALSE) const; + void SetSelection(int nMin, int nMax, BOOL bRedraw = FALSE) const; + BOOL SetTic(int nTic) const; + void SetTicFreq(int nFreq) const; + int SetTipSide(int nLocation) const; + void SetToolTips(CWnd* pToolTip) const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc) { wc.lpszClassName = TRACKBAR_CLASS; } + }; + + + // Also known as an Up/Down control + class CSpinButton : public CWnd + { + public: + CSpinButton() {} + virtual ~CSpinButton() {} + + int GetAccel(int cAccels, LPUDACCEL paAccels) const; + int GetBase() const; + CWnd* GetBuddy() const; + int GetPos() const; + DWORD GetRange() const; + BOOL SetAccel(int cAccels, LPUDACCEL paAccels) const; + int SetBase(int nBase) const; + CWnd* SetBuddy(CWnd* hwndBuddy) const; + int SetPos(int nPos) const; + void SetRange(int nLower, int nUpper) const; + + protected: + // Overridables + virtual void PreCreate(CREATESTRUCT &cs); + virtual void PreRegisterClass(WNDCLASS &wc); + }; + +} // namespace Win32xx + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +namespace Win32xx +{ + + //////////////////////////////////////// + // Definitions for the CAnimation class + // + inline BOOL CAnimation::Close() const + // Closes an AVI clip. + { + assert(IsWindow()); + return Animate_Close(m_hWnd); + } + + inline BOOL CAnimation::Open(LPTSTR lpszName) const + // Opens an AVI clip and displays its first frame in an animation control. + { + assert(IsWindow()); + return Animate_Open(m_hWnd, lpszName); + } + + inline BOOL CAnimation::Play(UINT wFrom, UINT wTo, UINT cRepeat) const + // Plays an AVI clip in an animation control. The control plays the clip + // in the background while the thread continues executing. + { + assert(IsWindow()); + return Animate_Play(m_hWnd, wFrom, wTo, cRepeat); + } + + inline BOOL CAnimation::Seek(UINT wFrame) const + // Directs an animation control to display a particular frame of an AVI clip. + // The control displays the clip in the background while the thread continues executing. + { + assert(IsWindow()); + return Animate_Seek(m_hWnd, wFrame); + } + + inline BOOL CAnimation::Stop() const + // Stops playing an AVI clip in an animation control. + { + assert(IsWindow()); + return Animate_Stop(m_hWnd); + } + + + //////////////////////////////////////// + // Definitions for the CComboBox class + // + inline int CComboBox::AddString(LPCTSTR lpszString) const + // Adds a string to the list box of a combo box. If the combo box does not + // have the CBS_SORT style, the string is added to the end of the list. + // Otherwise, the string is inserted into the list, and the list is sorted. + { + assert(IsWindow()); + return (int)SendMessage(CB_ADDSTRING, 0, (LPARAM)lpszString); + } + + inline void CComboBox::Clear() const + // Deletes the current selection, if any, from the combo box's edit control. + { + assert(IsWindow()); + SendMessage(WM_CLEAR, 0, 0); + } + + inline void CComboBox::Copy() const + // Copies the current selection to the clipboard in CF_TEXT format. + { + assert(IsWindow()); + SendMessage(WM_COPY, 0, 0); + } + + inline void CComboBox::Cut() const + // Deletes the current selection, if any, in the edit control and copies + // the deleted text to the clipboard in CF_TEXT format. + { + assert(IsWindow()); + SendMessage(WM_CUT, 0, 0); + } + + inline int CComboBox::DeleteString(int nIndex) const + // Deletes a string in the list box of a combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_DELETESTRING, (WPARAM)nIndex, 0); + } + + inline int CComboBox::Dir(UINT attr, LPCTSTR lpszWildCard ) const + // Adds the names of directories and files that match a specified string + // and set of file attributes. + { + assert(IsWindow()); + return (int)SendMessage(CB_DIR, (WPARAM)attr, (LPARAM)lpszWildCard); + } + + inline int CComboBox::FindString(int nIndexStart, LPCTSTR lpszString) const + // Search the list box of a combo box for an item beginning with the + // characters in a specified string. + { + assert(IsWindow()); + return (int)SendMessage(CB_FINDSTRING, (WPARAM)nIndexStart, (LPARAM)lpszString); + } + + inline int CComboBox::FindStringExact(int nIndexStart, LPCTSTR lpszString) const + // Find the first list box string in a combo box that matches the string specified in lpszString. + { + assert(IsWindow()); + return (int)SendMessage(CB_FINDSTRINGEXACT, (WPARAM)nIndexStart, (LPARAM)lpszString); + } + + inline int CComboBox::GetCount() const + // Retrieves the number of items in the list box of the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_GETCOUNT, 0,0); + } + + inline int CComboBox::GetCurSel() const + // Retrieves the index of the currently selected item, if any, in the list box of the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_GETCURSEL, 0,0); + } + + inline CRect CComboBox::GetDroppedControlRect() const + // Retrieves the screen coordinates of the combo box in its dropped-down state. + { + assert(IsWindow()); + CRect rc; + SendMessage(CB_GETDROPPEDCONTROLRECT, 0, (LPARAM)&rc); + return rc; + } + + inline BOOL CComboBox::GetDroppedState() const + // Determines whether the list box of the combo box is dropped down. + { + assert(IsWindow()); + return (BOOL)SendMessage(CB_GETDROPPEDSTATE, 0, 0); + } + + inline int CComboBox::GetDroppedWidth() const + // Retrieves the minimum allowable width, in pixels, of the list box of the combo box + // with the CBS_DROPDOWN or CBS_DROPDOWNLIST style. + { + assert(IsWindow()); + return (int)SendMessage(CB_GETDROPPEDWIDTH, 0, 0); + } + + inline DWORD CComboBox::GetEditSel() const + // Gets the starting and ending character positions of the current selection + // in the edit control of the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_GETEDITSEL, 0, 0); + } + + inline BOOL CComboBox::GetExtendedUI() const + // Determines whether the combo box has the default user interface or the extended user interface. + { + assert(IsWindow()); + return (BOOL)SendMessage(CB_GETEXTENDEDUI, 0, 0); + } + + inline int CComboBox::GetHorizontalExtent() const + // Retrieve from the combo box the width, in pixels, by which the list box can + // be scrolled horizontally (the scrollable width). + { + assert(IsWindow()); + return (int)SendMessage(CB_GETHORIZONTALEXTENT, 0, 0); + } + + inline DWORD CComboBox::GetItemData(int nIndex) const + // Retrieves the application-supplied value associated with the specified item in the combo box. + { + assert(IsWindow()); + return (DWORD)SendMessage(CB_GETITEMDATA, (WPARAM)nIndex, 0); + } + + inline int CComboBox::GetItemHeight(int nIndex) const + // Determines the height of list items or the selection field in the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_GETITEMHEIGHT, (WPARAM)nIndex, 0); + } + + inline int CComboBox::GetLBText(int nIndex, LPTSTR lpszText) const + // Retrieves a string from the list of the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_GETLBTEXT, (WPARAM)nIndex, (LPARAM)lpszText); + } + + inline int CComboBox::GetLBTextLen(int nIndex) const + // Retrieves the length, in characters, of a string in the list of the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_GETLBTEXTLEN, (WPARAM)nIndex, 0); + } + + inline LCID CComboBox::GetLocale() const + // Retrieves the current locale of the combo box. + { + assert(IsWindow()); + return (LCID)SendMessage(CB_GETLOCALE, 0, 0); + } + + inline int CComboBox::GetTopIndex() const + // Retrieves the zero-based index of the first visible item in the list box portion of the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_GETTOPINDEX, 0, 0); + } + + inline int CComboBox::InitStorage(int nItems, int nBytes) const + // Allocates memory for storing list box items. Use this before adding a + // large number of items to the list box portion of a combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_INITSTORAGE, (WPARAM)nItems, (LPARAM)nBytes); + } + + inline int CComboBox::InsertString(int nIndex, LPCTSTR lpszString) const + // Inserts a string into the list box of the combo box. Unlike the AddString, + // a list with the CBS_SORT style is not sorted. + { + assert(IsWindow()); + return (int)SendMessage(CB_INSERTSTRING, (WPARAM)nIndex, (LPARAM)lpszString); + } + + inline void CComboBox::Paste() const + // Copies the current content of the clipboard to the combo box's edit control at the current caret position. + { + assert(IsWindow()); + SendMessage(WM_PASTE, 0, 0); + } + + inline void CComboBox::LimitText(int nMaxChars) const + // Limits the length of the text the user may type into the edit control of the combo box. + { + assert(IsWindow()); + SendMessage(CB_LIMITTEXT, (WPARAM)nMaxChars, 0); + } + + inline void CComboBox::ResetContent() const + // Removes all items from the list box and edit control of the combo box. + { + assert(IsWindow()); + SendMessage(CB_RESETCONTENT, 0, 0); + } + + inline int CComboBox::SelectString(int nStartAfter, LPCTSTR lpszString) const + // Searches the list of a combo box for an item that begins with the characters in a + // specified string. If a matching item is found, it is selected and copied to the edit control. + { + assert(IsWindow()); + return (int)SendMessage(CB_SELECTSTRING, (WPARAM)nStartAfter, (LPARAM)lpszString); + } + + inline int CComboBox::SetCurSel(int nIndex) const + // Selects a string in the list of the combo box. If necessary, the list scrolls the string into view. + { + assert(IsWindow()); + return (int)SendMessage(CB_SETCURSEL, (WPARAM)nIndex, 0); + } + + inline int CComboBox::SetDroppedWidth(int nWidth) const + // Sets the maximum allowable width, in pixels, of the list box of the combo box with + // the CBS_DROPDOWN or CBS_DROPDOWNLIST style. + { + assert(IsWindow()); + return (int)SendMessage(CB_SETDROPPEDWIDTH, (WPARAM)nWidth, 0); + } + + inline BOOL CComboBox::SetEditSel(int nStartChar, int nEndChar) const + // Selects characters in the edit control of the combo box. + { + assert(IsWindow()); + return (BOOL)SendMessage(CB_SETEDITSEL, 0, (LPARAM)MAKELONG(nStartChar,nEndChar)); + } + + inline int CComboBox::SetExtendedUI(BOOL bExtended) const + // Selects either the default user interface or the extended user interface for the combo box that + // has the CBS_DROPDOWN or CBS_DROPDOWNLIST style. + { + assert(IsWindow()); + return (int)SendMessage(CB_SETEXTENDEDUI, (WPARAM)bExtended, 0); + } + + inline void CComboBox::SetHorizontalExtent(UINT nExtent ) const + // Sets the width, in pixels, by which the list box can be scrolled horizontally (the scrollable width). + { + assert(IsWindow()); + SendMessage(CB_SETHORIZONTALEXTENT, (WPARAM)nExtent, 0); + } + + inline int CComboBox::SetItemData(int nIndex, DWORD dwItemData) const + // Sets the value associated with the specified item in the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_SETITEMDATA, (WPARAM)nIndex, (LPARAM)dwItemData); + } + + inline int CComboBox::SetItemHeight(int nIndex, UINT cyItemHeight) const + // Sets the height of list items or the selection field in the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_SETITEMHEIGHT, (WPARAM)nIndex, (LPARAM)cyItemHeight); + } + + inline LCID CComboBox::SetLocale( LCID NewLocale ) const + // Sets the current locale of the combo box. + { + assert(IsWindow()); + return (LCID)SendMessage(CB_SETLOCALE, (WPARAM)NewLocale, 0); + } + + inline int CComboBox::SetTopIndex(int nIndex) const + // Ensure that a particular item is visible in the list box of the combo box. + { + assert(IsWindow()); + return (int)SendMessage(CB_SETTOPINDEX, (WPARAM)nIndex, 0); + } + + inline void CComboBox::ShowDropDown(BOOL bShow) const + // Shows or hides the list box of the combo box that has the CBS_DROPDOWN or CBS_DROPDOWNLIST style. + { + assert(IsWindow()); + SendMessage(CB_SHOWDROPDOWN, (WPARAM)bShow, 0); + } + + + //////////////////////////////////////// + // Definitions for the CComboBoxEx class + // + inline int CComboBoxEx::DeleteItem(int nIndex ) const + // Removes an item from the ComboBoxEx control. + { + assert(IsWindow()); + return (int)SendMessage(CBEM_DELETEITEM, (WPARAM)nIndex, 0); + } + + inline CWnd* CComboBoxEx::GetComboBoxCtrl() const + // Retrieves the handle to the child combo box control. + { + assert(IsWindow()); + return FromHandle((HWND)SendMessage(CBEM_GETCOMBOCONTROL, 0, 0)); + } + + inline CWnd* CComboBoxEx::GetEditCtrl() const + // Retrieves the handle to the edit control portion of the ComboBoxEx control. + { + assert(IsWindow()); + return FromHandle((HWND)SendMessage(CBEM_GETEDITCONTROL, 0, 0)); + } + + inline DWORD CComboBoxEx::GetExtendedStyle() const + // Retrieves the extended styles that are in use for the ComboBoxEx control. + { + assert(IsWindow()); + return (DWORD)SendMessage(CBEM_GETEXTENDEDSTYLE, 0, 0); + } + + inline HIMAGELIST CComboBoxEx::GetImageList() const + // Retrieves the handle to an image list assigned to the ComboBoxEx control. + { + assert(IsWindow()); + return (HIMAGELIST)SendMessage(CBEM_GETIMAGELIST, 0, 0); + } + + inline BOOL CComboBoxEx::GetItem(COMBOBOXEXITEM* pCBItem) const + // Retrieves item information for the given ComboBoxEx item. + { + assert(IsWindow()); + return (BOOL)SendMessage(CBEM_GETITEM, 0, (LPARAM)pCBItem); + } + + inline BOOL CComboBoxEx::HasEditChanged () const + // Determines whether or not the user has changed the text of the ComboBoxEx edit control. + { + assert(IsWindow()); + return (BOOL)SendMessage(CBEM_HASEDITCHANGED, 0, 0); + } + + inline int CComboBoxEx::InsertItem(COMBOBOXEXITEM* lpcCBItem) const + // Inserts a new item in the ComboBoxEx control. + { + assert(IsWindow()); + return (int)SendMessage(CBEM_INSERTITEM, 0, (LPARAM)lpcCBItem); + } + + inline DWORD CComboBoxEx::SetExtendedStyle(DWORD dwExMask, DWORD dwExStyles ) const + // Sets extended styles within the ComboBoxEx control. + { + assert(IsWindow()); + return (DWORD)SendMessage(CBEM_SETEXTENDEDSTYLE, (WPARAM)dwExMask, (LPARAM)dwExStyles); + } + + inline HIMAGELIST CComboBoxEx::SetImageList(HIMAGELIST himl) const + // Sets an image list for the ComboBoxEx control. + { + assert(IsWindow()); + return (HIMAGELIST)SendMessage(CBEM_SETIMAGELIST, 0, (LPARAM)himl); + } + + inline BOOL CComboBoxEx::SetItem(PCOMBOBOXEXITEM lpcCBItem) const + // Sets the attributes for an item in the ComboBoxEx control. + { + assert(IsWindow()); + return (BOOL)SendMessage(CBEM_SETITEM, 0, (LPARAM)lpcCBItem); + } + + + //////////////////////////////////////// + // Definitions for the CProgressBar class + // + inline int CProgressBar::GetPos() const + // Retrieves the current position of the progress bar. + { + assert(IsWindow()); + return (int)SendMessage(PBM_GETPOS, 0, 0); + } + + inline int CProgressBar::GetRange(BOOL fWhichLimit, PPBRANGE ppBRange) const + // Retrieves information about the current high and low limits of the progress bar control. + { + assert(IsWindow()); + return (int)SendMessage(PBM_GETRANGE, (WPARAM)fWhichLimit, (LPARAM) (PPBRANGE) ppBRange); + } + + inline int CProgressBar::OffsetPos(int nIncrement) const + // Advances the current position of the progress bar by a specified increment and redraws + // the bar to reflect the new position. + { + assert(IsWindow()); + return (int)SendMessage(PBM_DELTAPOS, (WPARAM)nIncrement, 0); + } + + inline int CProgressBar::SetPos(int nNewPos) const + // Sets the current position for the progress bar and redraws the bar to reflect the new position. + { + assert(IsWindow()); + return (int)SendMessage(PBM_SETPOS, (WPARAM)nNewPos, 0); + } + + inline int CProgressBar::SetRange(short nMinRange, short nMaxRange) const + // Sets the minimum and maximum values for the progress bar and redraws the bar to reflect the new range. + { + assert(IsWindow()); + return (int)SendMessage(PBM_SETRANGE, 0, (LPARAM) MAKELPARAM (nMinRange, nMaxRange)); + } + + inline int CProgressBar::SetStep(int nStepInc) const + // Specifies the step increment for the progress bar. + { + assert(IsWindow()); + return (int)SendMessage(PBM_SETSTEP, (WPARAM)nStepInc, 0); + } + + inline int CProgressBar::StepIt() const + // Advances the current position for a progress bar by the step increment and + // redraws the bar to reflect the new position. + { + assert(IsWindow()); + return (int)SendMessage(PBM_STEPIT, 0, 0); + } + + + //////////////////////////////////////// + // Definitions for the CScrollBar class + // + inline BOOL CScrollBar::EnableScrollBar( UINT nArrowFlags ) const + // Enables or disables the scroll bar arrows. + { + assert(IsWindow()); + return ::EnableScrollBar(m_hWnd, SB_CTL, nArrowFlags); + } + + inline BOOL CScrollBar::GetScrollInfo(LPSCROLLINFO lpsi) const + // Retrieves the parameters of a scroll bar, including the minimum and maximum + // scrolling positions, the page size, and the position of the scroll box (thumb). + { + assert(IsWindow()); + return ::GetScrollInfo(m_hWnd, SB_CTL, lpsi); + } + + inline int CScrollBar::GetScrollPos() const + // Retrieves the current position of the scroll box (thumb) in the scroll bar. + { + assert(IsWindow()); + return ::GetScrollPos(m_hWnd, SB_CTL); + } + + inline BOOL CScrollBar::GetScrollRange(LPINT lpMinPos, LPINT lpMaxPos ) const + // Retrieves the current minimum and maximum scroll box (thumb) positions for the scroll bar. + { + assert(IsWindow()); + return ::GetScrollRange(m_hWnd, SB_CTL, lpMinPos, lpMaxPos); + } + + inline BOOL CScrollBar::SetScrollInfo(LPSCROLLINFO lpsi, BOOL bRedraw ) const + // Sets the parameters of the scroll bar, including the minimum and maximum scrolling positions, + // the page size, and the position of the scroll box (thumb). + { + assert(IsWindow()); + return ::SetScrollInfo(m_hWnd, SB_CTL, lpsi, bRedraw); + } + + inline int CScrollBar::SetScrollPos(int nPos, BOOL bRedraw) const + // Sets the position of the scroll box (thumb) in the scroll bar and, if requested, + // redraws the scroll bar to reflect the new position of the scroll box. + { + assert(IsWindow()); + return ::SetScrollPos(m_hWnd, SB_CTL, nPos, bRedraw); + } + + inline BOOL CScrollBar::SetScrollRange( int nMinPos, int nMaxPos, BOOL bRedraw ) const + // Sets the minimum and maximum scroll box positions for the scroll bar. + { + assert(IsWindow()); + return ::SetScrollRange(m_hWnd, SB_CTL, nMinPos, nMaxPos, bRedraw); + } + + inline BOOL CScrollBar::ShowScrollBar(BOOL bShow) const + // Shows or hides the scroll bar. + { + assert(IsWindow()); + return ::ShowScrollBar(m_hWnd, SB_CTL, bShow); + } + + //////////////////////////////////////// + // Definitions for the CSlider class + // + inline void CSlider::ClearSel() const + // Clears the current selection range in the trackbar. + { + assert(IsWindow()); + SendMessage(TBM_CLEARSEL, 0, 0); + } + + inline void CSlider::ClearTics(BOOL bRedraw) const + // Removes the current tick marks from the trackbar. + { + assert(IsWindow()); + SendMessage(TBM_CLEARTICS, (WPARAM)bRedraw, 0); + } + + inline CWnd* CSlider::GetBuddy(BOOL fLocation) const + // Retrieves the handle to the trackbar control buddy window at a given location. + { + assert(IsWindow()); + return FromHandle((HWND)SendMessage(TBM_GETBUDDY, (WPARAM)fLocation, 0)); + } + + inline CRect CSlider::GetChannelRect() const + // Retrieves the size and position of the bounding rectangle for the trackbar's channel. + { + assert(IsWindow()); + CRect rc; + SendMessage(TBM_GETCHANNELRECT, 0, (LPARAM)&rc); + return rc; + } + + inline int CSlider::GetLineSize() const + // Retrieves the number of logical positions the trackbar's slider moves in response + // to keyboard input from the arrow keys. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETLINESIZE, 0, 0); + } + + inline int CSlider::GetNumTics() const + // Retrieves the number of tick marks in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETNUMTICS, 0, 0); + } + + inline int CSlider::GetPageSize() const + // Retrieves the number of logical positions the trackbar's slider moves in response to + // keyboard input, or mouse input, such as clicks in the trackbar's channel. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETPAGESIZE, 0, 0); + } + + inline int CSlider::GetPos() const + // Retrieves the current logical position of the slider in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETPOS, 0, 0); + } + + inline int CSlider::GetRangeMax() const + // Retrieves the maximum position for the slider in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETRANGEMAX, 0, 0); + } + + inline int CSlider::GetRangeMin() const + // Retrieves the minimum position for the slider in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETRANGEMIN, 0, 0); + } + + inline int CSlider::GetSelEnd() const + // Retrieves the ending position of the current selection range in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETSELEND, 0, 0); + } + + inline int CSlider::GetSelStart() const + // Retrieves the starting position of the current selection range in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETSELSTART, 0, 0); + } + + inline int CSlider::GetThumbLength() const + // Retrieves the length of the slider in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETTHUMBLENGTH, 0, 0); + } + + inline CRect CSlider::GetThumbRect() const + // Retrieves the size and position of the bounding rectangle for the slider in the trackbar. + { + CRect rc; + SendMessage(TBM_GETTHUMBRECT, 0, (LPARAM)&rc); + return rc; + } + + inline int CSlider::GetTic(int nTic ) const + // Retrieves the logical position of a tick mark in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETTIC, (WPARAM)nTic, 0); + } + + inline int CSlider::GetTicPos(int nTic) const + // Retrieves the current physical position of a tick mark in the trackbar. + { + assert(IsWindow()); + return (int)SendMessage(TBM_GETTICPOS, (WPARAM)nTic, 0); + } + + inline CWnd* CSlider::GetToolTips() const + // Retrieves the handle to the ToolTip control assigned to the trackbar, if any. + { + assert(IsWindow()); + return FromHandle((HWND)SendMessage(TBM_GETTOOLTIPS, 0, 0)); + } + + inline CWnd* CSlider::SetBuddy(CWnd* pBuddy, BOOL fLocation /*= TRUE*/ ) const + // Assigns a window as the buddy window for the trackbar control. + { + assert(IsWindow()); + return FromHandle((HWND)SendMessage(TBM_SETBUDDY, (WPARAM)fLocation, (LPARAM)pBuddy->GetHwnd())); + } + + inline int CSlider::SetLineSize(int nSize) const + // Sets the number of logical positions the trackbar's slider moves in response to + // keyboard input from the arrow keys. + { + assert(IsWindow()); + return(int)SendMessage(TBM_SETLINESIZE, 0, (LPARAM)nSize); + } + + inline int CSlider::SetPageSize(int nSize) const + // Sets the number of logical positions the trackbar's slider moves in response to + // keyboard input, or mouse input such as clicks in the trackbar's channel. + { + assert(IsWindow()); + return(int)SendMessage(TBM_SETPAGESIZE, 0, (LPARAM)nSize); + } + + inline void CSlider::SetPos(int nPos, BOOL bRedraw) const + // Sets the current logical position of the slider in the trackbar. + { + assert(IsWindow()); + SendMessage(TBM_SETPOS, (WPARAM)bRedraw, (LPARAM)nPos); + } + + inline void CSlider::SetRangeMax(int nMax, BOOL bRedraw) const + // Sets the maximum logical position for the slider in the trackbar. + { + assert(IsWindow()); + SendMessage(TBM_SETRANGEMAX, (WPARAM)bRedraw, (LPARAM)nMax); + } + + inline void CSlider::SetRangeMin(int nMax, BOOL bRedraw) const + // Sets the minimum logical position for the slider in the trackbar. + { + assert(IsWindow()); + SendMessage(TBM_SETRANGEMIN, (WPARAM)bRedraw, (LPARAM)nMax); + } + + inline void CSlider::SetSelection(int nMin, int nMax, BOOL bRedraw) const + // Sets the starting and ending positions for the available selection range in the trackbar. + { + assert(IsWindow()); + SendMessage(TBM_SETSEL, (WPARAM)bRedraw, (LPARAM)MAKELONG(nMax, nMin)); + } + + inline BOOL CSlider::SetTic(int nTic) const + // Sets a tick mark in the trackbar at the specified logical position. + { + assert(IsWindow()); + return (BOOL)SendMessage(TBM_SETTIC, 0, nTic); + } + + inline void CSlider::SetTicFreq(int nFreq) const + // Sets the interval frequency for tick marks in the trackbar. + { + assert(IsWindow()); + SendMessage(TBM_SETTICFREQ, (WPARAM)nFreq, 0); + } + + inline int CSlider::SetTipSide(int nLocation) const + // Positions a ToolTip control used by the trackbar control. + { + assert(IsWindow()); + return (int)SendMessage(TBM_SETTIPSIDE, (WPARAM)nLocation, 0); + } + + inline void CSlider::SetToolTips(CWnd* pToolTip) const + // Assigns a ToolTip control to the trackbar control. + { + assert(IsWindow()); + SendMessage(TBM_SETTOOLTIPS, (WPARAM)pToolTip->GetHwnd(), 0); + } + + //////////////////////////////////////// + // Definitions for the CSpinButton class + // + inline int CSpinButton::GetAccel(int cAccels, LPUDACCEL paAccels) const + // Retrieves acceleration information for the up-down control. + { + assert(IsWindow()); + return (int)SendMessage(UDM_GETACCEL, (WPARAM)cAccels, (LPARAM)paAccels); + } + + inline int CSpinButton::GetBase() const + // Retrieves the current radix base (that is, either base 10 or 16) for the up-down control. + { + assert(IsWindow()); + return (int)SendMessage(UDM_GETBASE, 0, 0); + } + + inline CWnd* CSpinButton::GetBuddy() const + // Retrieves the handle to the current buddy window. + { + assert(IsWindow()); + return FromHandle((HWND)SendMessage(UDM_GETBUDDY, 0, 0)); + } + + inline int CSpinButton::GetPos() const + // Retrieves the current position of the up-down control with 16-bit precision. + { + assert(IsWindow()); + return (int)SendMessage(UDM_GETPOS, 0, 0); + } + + inline DWORD CSpinButton::GetRange() const + // Retrieves the minimum and maximum positions (range) for the up-down control. + { + assert(IsWindow()); + return (DWORD)SendMessage(UDM_GETRANGE, 0, 0); + } + + inline void CSpinButton::PreCreate(CREATESTRUCT &cs) + { + cs.style = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_VISIBLE |UDS_SETBUDDYINT; + } + + inline void CSpinButton::PreRegisterClass(WNDCLASS &wc) + { + wc.lpszClassName = UPDOWN_CLASS; + } + + inline BOOL CSpinButton::SetAccel(int cAccels, LPUDACCEL paAccels) const + // Sets the acceleration for the up-down control. + { + assert(IsWindow()); + return (BOOL)SendMessage(UDM_SETACCEL, (WPARAM)cAccels, (LPARAM)paAccels); + } + + inline int CSpinButton::SetBase(int nBase) const + // Sets the radix base for the up-down control. + { + assert(IsWindow()); + return (int)SendMessage(UDM_SETBASE, (WPARAM)nBase, 0); + } + + inline CWnd* CSpinButton::SetBuddy(CWnd* pBuddy) const + // Sets the buddy window for the up-down control. + { + assert(IsWindow()); + return FromHandle((HWND)SendMessage(UDM_SETBUDDY, (WPARAM)pBuddy->GetHwnd(), 0)); + } + + inline int CSpinButton::SetPos(int nPos) const + // Sets the current position for the up-down control with 16-bit precision. + { + assert(IsWindow()); + return (int)SendMessage(UDM_SETPOS, 0, (LPARAM)MAKELONG ((short) nPos, 0)); + } + + inline void CSpinButton::SetRange(int nLower, int nUpper) const + // Sets the minimum and maximum positions (range) for the up-down control. + { + assert(IsWindow()); + SendMessage(UDM_SETRANGE, 0, (LPARAM)MAKELONG(nUpper, nLower)); + } + +} // namespace Win32xx + +#endif // define _WIN32XX_CONTROLS_H_ + diff --git a/mmc_updater/depends/win32cpp/copyright.txt b/mmc_updater/depends/win32cpp/copyright.txt new file mode 100644 index 00000000..d2222fa7 --- /dev/null +++ b/mmc_updater/depends/win32cpp/copyright.txt @@ -0,0 +1,33 @@ +Win32++ Version 7.2 +Released: 5th AUgust 2011 + + David Nash + email: dnash@bigpond.net.au + url: https://sourceforge.net/projects/win32-framework + + +Copyright (c) 2005-2011 David Nash + +Permission is hereby granted, free of charge, to +any person obtaining a copy of this software and +associated documentation files (the "Software"), +to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, subject to the +following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/mmc_updater/depends/win32cpp/cstring.h b/mmc_updater/depends/win32cpp/cstring.h new file mode 100644 index 00000000..faf574d5 --- /dev/null +++ b/mmc_updater/depends/win32cpp/cstring.h @@ -0,0 +1,905 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +// Acknowledgements: +// Thanks to Adam Szulc for his initial CString code. + +//////////////////////////////////////////////////////// +// cstring.h +// Declaration of the cstring.h + +// This class is intended to provide a simple alternative to the MFC/ATL +// CString class that ships with Microsoft compilers. The CString class +// specified here is compatible with other compilers such as Borland 5.5 +// and MinGW. + +// Differences between this class and the MFC/ATL CString class +// ------------------------------------------------------------ +// 1) The constructors for this class accepts only TCHARs. The various text conversion +// functions can be used to convert from other character types to TCHARs. +// +// 2) This class is not reference counted, so these CStrings should be passed as +// references or const references when used as function arguments. As a result there +// is no need for functions like LockBuffer and UnLockBuffer. +// +// 3) The Format functions only accepts POD (Plain Old Data) arguments. It does not +// accept arguments which are class or struct objects. In particular it does not +// accept CString objects, unless these are cast to LPCTSTR. +// This is demonstrates valid and invalid usage: +// CString string1(_T("Hello World")); +// CString string2; +// +// // This is invalid, and produces undefined behaviour. +// string2.Format(_T("String1 is: %s"), string1); // No! you can't do this +// +// // This is ok +// string2.Format(_T("String1 is: %s"), (LPCTSTR)string1); // Yes, this is correct +// +// Note: The MFC/ATL CString class uses a non portable hack to make its CString class +// behave like a POD. Other compilers (such as the MinGW compiler) specifically +// prohibit the use of non POD types for functions with variable argument lists. +// +// 4) This class provides a few additional functions: +// b_str Returns a BSTR string. This an an alternative for casting to BSTR. +// c_str Returns a const TCHAR string. This is an alternative for casting to LPCTSTR. +// GetErrorString Assigns CString to the error string for the specified System Error Code +// (from ::GetLastErrror() for example). +// GetString Returns a reference to the underlying std::basic_string<TCHAR>. This +// reference can be used to modify the string directly. + + + +#ifndef _WIN32XX_CSTRING_H_ +#define _WIN32XX_CSTRING_H_ + + +#include "wincore.h" + + +namespace Win32xx +{ + + class CString + { + // friend functions allow the left hand side to be something other than CString + friend CString operator + (const CString& string1, const CString& string2); + friend CString operator + (const CString& string, LPCTSTR pszText); + friend CString operator + (const CString& string, TCHAR ch); + friend CString operator + (LPCTSTR pszText, const CString& string); + friend CString operator + (TCHAR ch, const CString& string); + + public: + CString(); + ~CString(); + CString(const CString& str); + CString(LPCTSTR pszText); + CString(TCHAR ch, int nLength = 1); + CString(LPCTSTR pszText, int nLength); + + CString& operator = (const CString& str); + CString& operator = (const TCHAR ch); + CString& operator = (LPCTSTR pszText); + BOOL operator == (LPCTSTR pszText); + BOOL operator != (LPCTSTR pszText); + BOOL operator < (LPCTSTR pszText); + BOOL operator > (LPCTSTR pszText); + BOOL operator <= (LPCTSTR pszText); + BOOL operator >= (LPCTSTR pszText); + operator LPCTSTR() const; + operator BSTR() const; + TCHAR& operator [] (int nIndex); + CString& operator += (const CString& str); + + // Attributes + BSTR b_str() const { return T2W(m_str.c_str()); } // alternative for casting to BSTR + LPCTSTR c_str() const { return m_str.c_str(); } // alternative for casting to LPCTSTR + tString& GetString() { return m_str; } // returns a reference to the underlying std::basic_string<TCHAR> + int GetLength() const { return (int)m_str.length(); } // returns the length in characters + + // Operations + BSTR AllocSysString() const; + void AppendFormat(LPCTSTR pszFormat,...); + void AppendFormat(UINT nFormatID, ...); + int Compare(LPCTSTR pszText) const; + int CompareNoCase(LPCTSTR pszText) const; + int Delete(int nIndex, int nCount = 1); + int Find(TCHAR ch, int nIndex = 0 ) const; + int Find(LPCTSTR pszText, int nStart = 0) const; + int FindOneOf(LPCTSTR pszText) const; + void Format(UINT nID, ...); + void Format(LPCTSTR pszFormat,...); + void FormatV(LPCTSTR pszFormat, va_list args); + void FormatMessage(LPCTSTR pszFormat,...); + void FormatMessageV(LPCTSTR pszFormat, va_list args); + TCHAR GetAt(int nIndex) const; + LPTSTR GetBuffer(int nMinBufLength); + void GetErrorString(DWORD dwError); + void Empty(); + int Insert(int nIndex, TCHAR ch); + int Insert(int nIndex, const CString& str); + BOOL IsEmpty() const; + CString Left(int nCount) const; + BOOL LoadString(UINT nID); + void MakeLower(); + void MakeReverse(); + void MakeUpper(); + CString Mid(int nFirst) const; + CString Mid(int nFirst, int nCount) const; + void ReleaseBuffer( int nNewLength = -1 ); + int Remove(LPCTSTR pszText); + int Replace(TCHAR chOld, TCHAR chNew); + int Replace(const LPCTSTR pszOld, LPCTSTR pszNew); + int ReverseFind(LPCTSTR pszText, int nStart = -1) const; + CString Right(int nCount) const; + void SetAt(int nIndex, TCHAR ch); + BSTR SetSysString(BSTR* pBstr) const; + CString SpanExcluding(LPCTSTR pszText) const; + CString SpanIncluding(LPCTSTR pszText) const; + CString Tokenize(LPCTSTR pszTokens, int& iStart) const; + void Trim(); + void TrimLeft(); + void TrimLeft(TCHAR chTarget); + void TrimLeft(LPCTSTR pszTargets); + void TrimRight(); + void TrimRight(TCHAR chTarget); + void TrimRight(LPCTSTR pszTargets); + void Truncate(int nNewLength); + +#ifndef _WIN32_WCE + int Collate(LPCTSTR pszText) const; + int CollateNoCase(LPCTSTR pszText) const; + BOOL GetEnvironmentVariable(LPCTSTR pszVar); +#endif + + private: + tString m_str; + std::vector<TCHAR> m_buf; + }; + + inline CString::CString() + { + } + + inline CString::~CString() + { + } + + inline CString::CString(const CString& str) + { + m_str.assign(str); + } + + inline CString::CString(LPCTSTR pszText) + { + m_str.assign(pszText); + } + + inline CString::CString(TCHAR ch, int nLength) + { + m_str.assign(nLength, ch); + } + + inline CString::CString(LPCTSTR pszText, int nLength) + { + m_str.assign(pszText, nLength); + } + + inline CString& CString::operator = (const CString& str) + { + m_str.assign(str); + return *this; + } + + inline CString& CString::operator = (const TCHAR ch) + { + m_str.assign(1, ch); + return *this; + } + + inline CString& CString::operator = (LPCTSTR pszText) + { + m_str.assign(pszText); + return *this; + } + + inline BOOL CString::operator == (LPCTSTR pszText) + // Returns TRUE if the strings have the same content + { + assert(pszText); + return (0 == Compare(pszText)); + } + + inline BOOL CString::operator != (LPCTSTR pszText) + // Returns TRUE if the strings have a different content + { + assert(pszText); + return Compare(pszText) != 0; + } + + inline BOOL CString::operator < (LPCTSTR pszText) + { + assert(pszText); + return Compare(pszText) < 0; + } + + inline BOOL CString::operator > (LPCTSTR pszText) + { + assert(pszText); + return Compare(pszText) > 0; + } + + inline BOOL CString::operator <= (LPCTSTR pszText) + { + assert(pszText); + return Compare(pszText) <= 0; + } + + inline BOOL CString::operator >= (LPCTSTR pszText) + { + assert(pszText); + return Compare(pszText) >= 0; + } + + inline CString::operator LPCTSTR() const + { + return m_str.c_str(); + } + + inline TCHAR& CString::operator [] (int nIndex) + { + assert(nIndex >= 0); + assert(nIndex < GetLength()); + return m_str[nIndex]; + } + + inline CString& CString::operator += (const CString& str) + { + m_str.append(str); + return *this; + } + + inline BSTR CString::AllocSysString() const + // Allocates a BSTR from the CString content. + { + return ::SysAllocStringLen(T2W(m_str.c_str()), (UINT)m_str.size()); + } + + inline void CString::AppendFormat(LPCTSTR pszFormat,...) + // Appends formatted data to an the CString content. + { + CString str; + str.Format(pszFormat); + m_str.append(str); + } + + inline void CString::AppendFormat(UINT nFormatID, ...) + // Appends formatted data to an the CString content. + { + CString str1; + CString str2; + if (str1.LoadString(nFormatID)) + { + str2.Format(str1); + m_str.append(str2); + } + } + +#ifndef _WIN32_WCE + inline int CString::Collate(LPCTSTR pszText) const + // Performs a case sensitive comparison of the two strings using locale-specific information. + { + assert(pszText); + return _tcscoll(m_str.c_str(), pszText); + } + + inline int CString::CollateNoCase(LPCTSTR pszText) const + // Performs a case insensitive comparison of the two strings using locale-specific information. + { + assert(pszText); + return _tcsicoll(m_str.c_str(), pszText); + } +#endif // _WIN32_WCE + + inline int CString::Compare(LPCTSTR pszText) const + // Performs a case sensitive comparison of the two strings. + { + assert(pszText); + return m_str.compare(pszText); + } + + inline int CString::CompareNoCase(LPCTSTR pszText) const + // Performs a case insensitive comparison of the two strings. + { + assert(pszText); + return _tcsicmp(m_str.data(), pszText); + } + + inline int CString::Delete(int nIndex, int nCount /* = 1 */) + // Deletes a character or characters from the string. + { + assert(nIndex >= 0); + assert(nCount >= 0); + + m_str.erase(nIndex, nCount); + return (int)m_str.size(); + } + + inline void CString::Empty() + // Erases the contents of the string. + { + m_str.erase(); + } + + inline int CString::Find(TCHAR ch, int nIndex /* = 0 */) const + // Finds a character in the string. + { + assert(nIndex >= 0); + return (int)m_str.find(ch, nIndex); + } + + inline int CString::Find(LPCTSTR pszText, int nIndex /* = 0 */) const + // Finds a substring within the string. + { + assert(pszText); + assert(nIndex >= 0); + return (int)m_str.find(pszText, nIndex); + } + + inline int CString::FindOneOf(LPCTSTR pszText) const + // Finds the first matching character from a set. + { + assert(pszText); + return (int)m_str.find_first_of(pszText); + } + + inline void CString::Format(LPCTSTR pszFormat,...) + // Formats the string as sprintf does. + { + va_list args; + va_start(args, pszFormat); + FormatV(pszFormat, args); + va_end(args); + } + + inline void CString::Format(UINT nID, ...) + // Formats the string as sprintf does. + { + Empty(); + CString str; + if (str.LoadString(nID)) + Format(str); + } + + inline void CString::FormatV(LPCTSTR pszFormat, va_list args) + // Formats the string using a variable list of arguments. + { + if (pszFormat) + { + int nResult = -1, nLength = 256; + + // A vector is used to store the TCHAR array + std::vector<TCHAR> vBuffer;( nLength+1, _T('\0') ); + + while (-1 == nResult) + { + vBuffer.assign( nLength+1, _T('\0') ); + nResult = _vsntprintf(&vBuffer[0], nLength, pszFormat, args); + nLength *= 2; + } + m_str.assign(&vBuffer[0]); + } + } + + inline void CString::FormatMessage(LPCTSTR pszFormat,...) + // Formats a message string. + { + va_list args; + va_start(args, pszFormat); + FormatMessageV(pszFormat, args); + va_end(args); + } + + inline void CString::FormatMessageV(LPCTSTR pszFormat, va_list args) + // Formats a message string using a variable argument list. + { + LPTSTR pszTemp = 0; + if (pszFormat) + { + DWORD dwResult = ::FormatMessage(FORMAT_MESSAGE_FROM_STRING|FORMAT_MESSAGE_ALLOCATE_BUFFER, pszFormat, 0, 0, pszTemp, 0, &args); + + if (0 == dwResult || 0 == pszTemp ) + throw std::bad_alloc(); + + m_str = pszTemp; + LocalFree(pszTemp); + } + } + + inline TCHAR CString::GetAt(int nIndex) const + // Returns the character at the specified location within the string. + { + assert(nIndex >= 0); + assert(nIndex < GetLength()); + return m_str[nIndex]; + } + + inline LPTSTR CString::GetBuffer(int nMinBufLength) + // Creates a buffer of nMinBufLength charaters (+1 extra for NULL termination) and returns + // a pointer to this buffer. This buffer can be used by any function which accepts a LPTSTR. + // Care must be taken not to exceed the length of the buffer. Use ReleaseBuffer to safely + // copy this buffer back to the CString object. + // + // Note: The buffer uses a vector. Vectors are required to be contiguous in memory under + // the current standard, whereas std::strings do not have this requirement. + { + assert (nMinBufLength >= 0); + + m_buf.assign(nMinBufLength + 1, _T('\0')); + tString::iterator it_end; + + if (m_str.length() >= (size_t)nMinBufLength) + { + it_end = m_str.begin(); + std::advance(it_end, nMinBufLength); + } + else + it_end = m_str.end(); + + std::copy(m_str.begin(), it_end, m_buf.begin()); + + return &m_buf[0]; + } + +#ifndef _WIN32_WCE + inline BOOL CString::GetEnvironmentVariable(LPCTSTR pszVar) + // Sets the string to the value of the specified environment variable. + { + assert(pszVar); + Empty(); + + int nLength = ::GetEnvironmentVariable(pszVar, NULL, 0); + if (nLength > 0) + { + std::vector<TCHAR> vBuffer( nLength+1, _T('\0') ); + ::GetEnvironmentVariable(pszVar, &vBuffer[0], nLength); + m_str = &vBuffer[0]; + } + + return (BOOL)nLength; + } +#endif // _WIN32_WCE + + inline void CString::GetErrorString(DWORD dwError) + // Returns the error string for the specified System Error Code (e.g from GetLastErrror). + { + m_str.erase(); + + if (dwError != 0) + { + TCHAR* pTemp = 0; + DWORD dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + ::FormatMessage(dwFlags, NULL, dwError, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&pTemp, 1, NULL); + m_str.assign(pTemp); + ::LocalFree(pTemp); + } + } + + inline int CString::Insert(int nIndex, TCHAR ch) + // Inserts a single character or a substring at the given index within the string. + { + assert(nIndex >= 0); + assert(ch); + + m_str.insert(nIndex, &ch, 1); + return (int)m_str.size(); + } + + inline int CString::Insert(int nIndex, const CString& str) + // Inserts a single character or a substring at the given index within the string. + { + assert(nIndex >= 0); + + m_str.insert(nIndex, str); + return (int)m_str.size(); + } + + inline BOOL CString::IsEmpty() const + // Returns TRUE if the string is empty + { + return m_str.empty(); + } + + inline CString CString::Left(int nCount) const + // Extracts the left part of a string. + { + assert(nCount >= 0); + + CString str; + str.m_str.assign(c_str(), 0, nCount); + return str; + } + + inline BOOL CString::LoadString(UINT nID) + // Loads the string from a Windows resource. + { + assert (GetApp()); + + int nSize = 64; + TCHAR* pTCharArray = 0; + std::vector<TCHAR> vString; + int nTChars = nSize; + + Empty(); + + // Increase the size of our array in a loop until we load the entire string + // The ANSI and _UNICODE versions of LoadString behave differently. This technique works for both. + while ( nSize-1 <= nTChars ) + { + nSize = nSize * 4; + vString.assign(nSize+1, _T('\0')); + pTCharArray = &vString[0]; + nTChars = ::LoadString (GetApp()->GetResourceHandle(), nID, pTCharArray, nSize); + } + + if (nTChars > 0) + m_str.assign(pTCharArray); + + return (nTChars != 0); + } + + inline void CString::MakeLower() + // Converts all the characters in this string to lowercase characters. + { + std::transform(m_str.begin(), m_str.end(), m_str.begin(), &::tolower); + } + + inline void CString::MakeReverse() + // Reverses the string. + { + std::reverse(m_str.begin(), m_str.end()); + } + + inline void CString::MakeUpper() + // Converts all the characters in this string to uppercase characters. + { + std::transform(m_str.begin(), m_str.end(), m_str.begin(), &::toupper); + } + + inline CString CString::Mid(int nFirst) const + // Extracts the middle part of a string. + { + return Mid(nFirst, GetLength()); + } + + inline CString CString::Mid(int nFirst, int nCount) const + // Extracts the middle part of a string. + { + assert(nFirst >= 0); + assert(nCount >= 0); + + CString str; + str.m_str.assign(c_str(), nFirst, nFirst + nCount); + return str; + } + + inline int CString::ReverseFind(LPCTSTR pszText, int nIndex /* = -1 */) const + // Search for a substring within the string, starting from the end. + { + assert(pszText); + return (int)m_str.rfind(pszText, nIndex); + } + + inline void CString::SetAt(int nIndex, TCHAR ch) + // Sets the character at the specificed position to the specified value. + { + assert(nIndex >= 0); + assert(nIndex < GetLength()); + m_str[nIndex] = ch; + } + + inline void CString::ReleaseBuffer( int nNewLength /*= -1*/ ) + // This copies the contents of the buffer (acquired by GetBuffer) to this CString, + // and releases the contents of the buffer. The default length of -1 copies from the + // buffer until a null terminator is reached. If the buffer doesn't contain a null + // terminator, you must specify the buffer's length. + { + assert (nNewLength > 0 || -1 == nNewLength); + assert (nNewLength < (int)m_buf.size()); + + if (-1 == nNewLength) + nNewLength = lstrlen(&m_buf[0]); + m_str.assign(nNewLength+1, _T('\0')); + + std::vector<TCHAR>::iterator it_end = m_buf.begin(); + std::advance(it_end, nNewLength); + + std::copy(m_buf.begin(), it_end, m_str.begin()); + m_buf.clear(); + } + + inline int CString::Remove(LPCTSTR pszText) + // Removes each occurrence of the specified substring from the string. + { + assert(pszText); + + int nCount = 0; + size_t pos = 0; + while ((pos = m_str.find(pszText, pos)) != std::string::npos) + { + m_str.erase(pos, lstrlen(pszText)); + ++nCount; + } + return nCount; + } + + inline int CString::Replace(TCHAR chOld, TCHAR chNew) + // Replaces each occurance of the old character with the new character. + { + int nCount = 0; + tString::iterator it = m_str.begin(); + while (it != m_str.end()) + { + if (*it == chOld) + { + *it = chNew; + ++nCount; + } + ++it; + } + return nCount; + } + + inline int CString::Replace(LPCTSTR pszOld, LPCTSTR pszNew) + // Replaces each occurance of the old substring with the new substring. + { + assert(pszOld); + assert(pszNew); + + int nCount = 0; + size_t pos = 0; + while ((pos = m_str.find(pszOld, pos)) != std::string::npos) + { + m_str.replace(pos, lstrlen(pszOld), pszNew); + pos += lstrlen(pszNew); + ++nCount; + } + return nCount; + } + + inline CString CString::Right(int nCount) const + // Extracts the right part of a string. + { + assert(nCount >= 0); + + CString str; + str.m_str.assign(c_str(), m_str.size() - nCount, nCount); + return str; + } + + inline BSTR CString::SetSysString(BSTR* pBstr) const + // Sets an existing BSTR object to the string. + { + assert(pBstr); + + if ( !::SysReAllocStringLen(pBstr, T2W(m_str.c_str()), (UINT)m_str.length()) ) + throw std::bad_alloc(); + + return *pBstr; + } + + inline CString CString::SpanExcluding(LPCTSTR pszText) const + // Extracts characters from the string, starting with the first character, + // that are not in the set of characters identified by pszCharSet. + { + assert (pszText); + + CString str; + size_t pos = 0; + + while ((pos = m_str.find_first_not_of(pszText, pos)) != std::string::npos) + { + str.m_str.append(1, m_str[pos++]); + } + + return str; + } + + inline CString CString::SpanIncluding(LPCTSTR pszText) const + // Extracts a substring that contains only the characters in a set. + { + assert (pszText); + + CString str; + size_t pos = 0; + + while ((pos = m_str.find_first_of(pszText, pos)) != std::string::npos) + { + str.m_str.append(1, m_str[pos++]); + } + + return str; + } + + inline CString CString::Tokenize(LPCTSTR pszTokens, int& iStart) const + // Extracts specified tokens in a target string. + { + assert(pszTokens); + assert(iStart >= 0); + + CString str; + size_t pos1 = m_str.find_first_not_of(pszTokens, iStart); + size_t pos2 = m_str.find_first_of(pszTokens, pos1); + + iStart = (int)pos2 + 1; + if (pos2 == m_str.npos) + iStart = -1; + + if (pos1 != m_str.npos) + str.m_str = m_str.substr(pos1, pos2-pos1); + + return str; + } + + inline void CString::Trim() + // Trims all leading and trailing whitespace characters from the string. + { + TrimLeft(); + TrimRight(); + } + + inline void CString::TrimLeft() + // Trims leading whitespace characters from the string. + { + // This method is supported by the Borland 5.5 compiler + tString::iterator iter; + for (iter = m_str.begin(); iter < m_str.end(); ++iter) + { + if (!isspace(*iter)) + break; + } + + m_str.erase(m_str.begin(), iter); + } + + inline void CString::TrimLeft(TCHAR chTarget) + // Trims the specified character from the beginning of the string. + { + m_str.erase(0, m_str.find_first_not_of(chTarget)); + } + + inline void CString::TrimLeft(LPCTSTR pszTargets) + // Trims the specified set of characters from the beginning of the string. + { + assert(pszTargets); + m_str.erase(0, m_str.find_first_not_of(pszTargets)); + } + + inline void CString::TrimRight() + // Trims trailing whitespace characters from the string. + { + // This method is supported by the Borland 5.5 compiler + tString::reverse_iterator riter; + for (riter = m_str.rbegin(); riter < m_str.rend(); ++riter) + { + if (!isspace(*riter)) + break; + } + + m_str.erase(riter.base(), m_str.end()); + } + + inline void CString::TrimRight(TCHAR chTarget) + // Trims the specified character from the end of the string. + { + size_t pos = m_str.find_last_not_of(chTarget); + if (pos != std::string::npos) + m_str.erase(++pos); + } + + inline void CString::TrimRight(LPCTSTR pszTargets) + // Trims the specified set of characters from the end of the string. + { + assert(pszTargets); + + size_t pos = m_str.find_last_not_of(pszTargets); + if (pos != std::string::npos) + m_str.erase(++pos); + } + + inline void CString::Truncate(int nNewLength) + // Reduces the length of the string to the specified amount. + { + if (nNewLength < GetLength()) + { + assert(nNewLength >= 0); + m_str.erase(nNewLength); + } + } + + + /////////////////////////////////// + // Global Functions + // + + // friend functions of CString + inline CString operator + (const CString& string1, const CString& string2) + { + CString str(string1); + str.m_str.append(string2.m_str); + return str; + } + + inline CString operator + (const CString& string, LPCTSTR pszText) + { + CString str(string); + str.m_str.append(pszText); + return str; + } + + inline CString operator + (const CString& string, TCHAR ch) + { + CString str(string); + str.m_str.append(1, ch); + return str; + } + + inline CString operator + (LPCTSTR pszText, const CString& string) + { + CString str(pszText); + str.m_str.append(string); + return str; + } + + inline CString operator + (TCHAR ch, const CString& string) + { + CString str(ch); + str.m_str.append(string); + return str; + } + + // Global LoadString + inline CString LoadString(UINT nID) + { + CString str; + str.LoadString(nID); + return str; + } + + +} // namespace Win32xx + +#endif//_WIN32XX_CSTRING_H_ diff --git a/mmc_updater/depends/win32cpp/default_resource.h b/mmc_updater/depends/win32cpp/default_resource.h new file mode 100644 index 00000000..b616a183 --- /dev/null +++ b/mmc_updater/depends/win32cpp/default_resource.h @@ -0,0 +1,94 @@ +// This file contains the resource ID definitions for Win32++. + + +// The resource ID for MENU, ICON, ToolBar Bitmap, Accelerator, +// and Window Caption +#define IDW_MAIN 51 + +// Resource ID for the About dialog +#define IDW_ABOUT 52 + +// Resource IDs for menu items +#define IDW_VIEW_TOOLBAR 53 +#define IDW_VIEW_STATUSBAR 54 + +// Resource IDs for the Command Bands +#define IDW_CMD_BANDS 55 +#define IDW_MENUBAR 56 +#define IDW_TOOLBAR 57 + +// Resource ID for the Accelerator key +#define IDW_QUIT 58 + +// Resource IDs for MDI menu items +#define IDW_MDI_CASCADE 60 +#define IDW_MDI_TILE 61 +#define IDW_MDI_ARRANGE 62 +#define IDW_MDI_CLOSEALL 63 +#define IDW_FIRSTCHILD 64 +#define IDW_CHILD2 65 +#define IDW_CHILD3 66 +#define IDW_CHILD4 67 +#define IDW_CHILD5 68 +#define IDW_CHILD6 69 +#define IDW_CHILD7 70 +#define IDW_CHILD8 71 +#define IDW_CHILD9 72 +#define IDW_CHILD10 73 + +#define IDW_FILE_MRU_FILE1 75 +#define IDW_FILE_MRU_FILE2 76 +#define IDW_FILE_MRU_FILE3 77 +#define IDW_FILE_MRU_FILE4 78 +#define IDW_FILE_MRU_FILE5 79 +#define IDW_FILE_MRU_FILE6 80 +#define IDW_FILE_MRU_FILE7 81 +#define IDW_FILE_MRU_FILE8 82 +#define IDW_FILE_MRU_FILE9 83 +#define IDW_FILE_MRU_FILE10 84 +#define IDW_FILE_MRU_FILE11 85 +#define IDW_FILE_MRU_FILE12 86 +#define IDW_FILE_MRU_FILE13 87 +#define IDW_FILE_MRU_FILE14 88 +#define IDW_FILE_MRU_FILE15 89 +#define IDW_FILE_MRU_FILE16 90 + +// Cursor Resources +#define IDW_SPLITH 91 +#define IDW_SPLITV 92 +#define IDW_TRACK4WAY 93 + +// Docking Bitmap Resources +#define IDW_SDBOTTOM 94 +#define IDW_SDCENTER 95 +#define IDW_SDLEFT 96 +#define IDW_SDMIDDLE 97 +#define IDW_SDRIGHT 98 +#define IDW_SDTOP 99 + + +// A generic ID for any static control +#ifndef IDC_STATIC + #define IDC_STATIC -1 +#endif + + + +// Notes about Resource IDs +// * In general, resource IDs can have values from 1 to 65535. Programs with +// resource IDs higher than 65535 aren't supported on Windows 95 +// +// * CMenuBar uses resource IDs beginning from 0 for the top level menu items. +// Win32++ leaves resource IDs below 51 unallocated for top level menu items. +// +// * Windows uses the icon with the lowest resource ID as the application's +// icon. The application's icon is IDW_MAIN, which is the first resource ID +// defined by Win32++. +// +// * When more than one static control is used in a dialog, the controls should +// have a unique ID, unless a resource ID of -1 is used. +// +// * Users of Win32++ are advised to begin their resource IDs from 120 to +// allow for possible expansion of Win32++. + + diff --git a/mmc_updater/depends/win32cpp/default_resource.rc b/mmc_updater/depends/win32cpp/default_resource.rc new file mode 100644 index 00000000..d53479f5 --- /dev/null +++ b/mmc_updater/depends/win32cpp/default_resource.rc @@ -0,0 +1,250 @@ +// An example of a resource file +// + +#include "resource.h" +#include "windows.h" + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 24 DISCARDABLE "res/Win32++.manifest" + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + + +IDW_MAIN MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "New &View", IDM_FILE_NEWVIEW + MENUITEM SEPARATOR + MENUITEM "Recent Files", IDW_FILE_MRU_FILE1, GRAYED + MENUITEM SEPARATOR + MENUITEM "E&xit", IDM_FILE_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "Undo\tCtrl+Z", IDM_EDIT_UNDO + MENUITEM "Redo\tShift+Ctrl+Z", IDM_EDIT_REDO + MENUITEM SEPARATOR + MENUITEM "Cut\tCtrl+X", IDM_EDIT_CUT + MENUITEM "Copy\tCtrl+C", IDM_EDIT_COPY + MENUITEM "Paste\tCtrl+V", IDM_EDIT_PASTE + MENUITEM "Delete\tDel", IDM_EDIT_DELETE + END + POPUP "&View" + BEGIN + MENUITEM "&Tool Bar", IDW_VIEW_TOOLBAR, CHECKED + MENUITEM "&Status Bar", IDW_VIEW_STATUSBAR, CHECKED + END + POPUP "&Help" + BEGIN + MENUITEM "&About", IDM_HELP_ABOUT + END +END + +MDIMENUVIEW MENU +BEGIN + POPUP "&File" + BEGIN + MENUITEM "New &View", IDM_FILE_NEWVIEW + MENUITEM "&Close", IDM_FILE_CLOSE + MENUITEM SEPARATOR + MENUITEM "E&xit", IDM_FILE_EXIT + END + POPUP "&Edit" + BEGIN + MENUITEM "Undo", IDM_EDIT_UNDO + MENUITEM "Redo", IDM_EDIT_REDO + MENUITEM SEPARATOR + MENUITEM "Cu&t", IDM_EDIT_CUT + MENUITEM "&Copy", IDM_EDIT_COPY + MENUITEM "&Paste", IDM_EDIT_PASTE + MENUITEM "De&lete", IDM_EDIT_DELETE + END + POPUP "&View" + BEGIN + MENUITEM "Tool Bar", IDW_VIEW_TOOLBAR, CHECKED + MENUITEM "Status Bar", IDW_VIEW_STATUSBAR, CHECKED + END + POPUP "&Color" + BEGIN + MENUITEM "&Black", IDM_COLOR_BLACK + MENUITEM "&Red", IDM_COLOR_RED + MENUITEM "&Green", IDM_COLOR_GREEN + MENUITEM "B&lue", IDM_COLOR_BLUE + MENUITEM "&White", IDM_COLOR_WHITE + END + POPUP "&Window" + BEGIN + MENUITEM "&Cascade\tShift+F5", IDW_WINDOW_CASCADE + MENUITEM "&Tile\tShift+F4", IDW_WINDOW_TILE + MENUITEM "Arrange &Icons", IDW_WINDOW_ARRANGE + MENUITEM "Close &All", IDW_WINDOW_CLOSEALL + END + POPUP "&Help" + BEGIN + MENUITEM "About", IDM_HELP_ABOUT + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDW_MAIN ICON "res/mdi.ico" +IDI_VIEW ICON "res/view.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDW_MAIN BITMAP "res/toolbar.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDW_ABOUT DIALOGEX 0, 0, 186, 90 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | + WS_SYSMENU +CAPTION "About" +FONT 8, "MS Shell Dlg", 400, 0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,68,49,50,14 + CTEXT "MDI Frame",IDC_STATIC,60,22,64,11 + ICON IDW_MAIN,0,4,4,20,20 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDW_MAIN ACCELERATORS +BEGIN + "N", IDM_FILE_NEW, VIRTKEY, CONTROL, NOINVERT + "O", IDM_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "P", IDM_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT + "S", IDM_FILE_SAVE, VIRTKEY, CONTROL, NOINVERT + "C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT + "X", IDM_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT + "V", IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT + "Z", IDM_EDIT_UNDO, VIRTKEY, CONTROL, NOINVERT + "Y", IDM_EDIT_REDO, VIRTKEY, SHIFT, CONTROL, NOINVERT + VK_DELETE, IDM_EDIT_DELETE, VIRTKEY, NOINVERT +END + + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDW_MAIN "MDI Frame" +END + +STRINGTABLE +BEGIN + IDM_FILE_NEW "Create a New Document" + IDM_FILE_OPEN "Open Existing Document" + IDM_FILE_SAVE "Save the Document" + IDM_FILE_SAVEAS "Save the Document with a new name" + IDM_FILE_PRINT "Print the Document" + IDM_FILE_EXIT "End the Program" +END + +STRINGTABLE +BEGIN + IDM_EDIT_UNDO "Undo the last action" + IDM_EDIT_REDO "Redo the previously undone action" + IDM_EDIT_CUT "Cut the Selected Contents to the Clipboard" + IDM_EDIT_COPY "Copy the Selected Contents to the Clipboard" + IDM_EDIT_PASTE "Paste the Clipboard Contents to the Document" + IDM_EDIT_DELETE "Erase the selected Contents" + IDW_VIEW_TOOLBAR "Show or hide the tool bar" + IDW_VIEW_STATUSBAR "Show or hide the status bar" +END + +STRINGTABLE +BEGIN + IDM_HELP_ABOUT "Display Information about this program" +END + +STRINGTABLE +BEGIN + IDW_FIRSTCHILD "Activate this window" + IDW_CHILD2 "Activate this window" + IDW_CHILD3 "Activate this window" + IDW_CHILD4 "Activate this window" + IDW_CHILD5 "Activate this window" + IDW_CHILD6 "Activate this window" + IDW_CHILD7 "Activate this window" + IDW_CHILD8 "Activate this window" + IDW_CHILD9 "Activate this window" + IDW_CHILD10 "Select a window" +END + +STRINGTABLE +BEGIN + IDM_FILE_NEWVIEW "Create View MDI Child" + IDM_FILE_CLOSE "Close MDI Window" + IDM_COLOR_BLACK "Use Black Printing" + IDM_COLOR_RED "Use Red Printing" + IDM_COLOR_GREEN "Use Green Printing" + IDM_COLOR_BLUE "Use Blue Printing" + IDM_COLOR_WHITE "Use White Printing" + IDW_WINDOW_CASCADE "Cascade MDI Windows" + IDW_WINDOW_TILE "Tile MDI Windows" + IDW_WINDOW_ARRANGE "Arrange Icons" + IDW_WINDOW_CLOSEALL "Close All MDI Windows" +END + +STRINGTABLE +BEGIN + SC_CLOSE "Close the Window" + SC_MAXIMIZE "Maximize the Window" + SC_MINIMIZE "Minimize the WIndow" + SC_MOVE "Move the Window" + SC_NEXTWINDOW "Select Next Window" + SC_PREVWINDOW "Select Previous Window" + SC_RESTORE "Restore the Window" + SC_SIZE "Resize the Window" +END + +STRINGTABLE +BEGIN + IDW_FILE_MRU_FILE1 "Open this document" + IDW_FILE_MRU_FILE2 "Open this document" + IDW_FILE_MRU_FILE3 "Open this document" + IDW_FILE_MRU_FILE4 "Open this document" + IDW_FILE_MRU_FILE5 "Open this document" + IDW_FILE_MRU_FILE6 "Open this document" + IDW_FILE_MRU_FILE7 "Open this document" + IDW_FILE_MRU_FILE8 "Open this document" + IDW_FILE_MRU_FILE9 "Open this document" + IDW_FILE_MRU_FILE10 "Open this document" + IDW_FILE_MRU_FILE11 "Open this document" + IDW_FILE_MRU_FILE12 "Open this document" + IDW_FILE_MRU_FILE13 "Open this document" + IDW_FILE_MRU_FILE14 "Open this document" + IDW_FILE_MRU_FILE15 "Open this document" + IDW_FILE_MRU_FILE16 "Open this document" +END + + diff --git a/mmc_updater/depends/win32cpp/dialog.h b/mmc_updater/depends/win32cpp/dialog.h new file mode 100644 index 00000000..e5123304 --- /dev/null +++ b/mmc_updater/depends/win32cpp/dialog.h @@ -0,0 +1,876 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// dialog.h +// Declaration of the CDialog class + +// CDialog adds support for dialogs to Win32++. Dialogs are specialised +// windows which are a parent window for common controls. Common controls +// are special window types such as buttons, edit controls, tree views, +// list views, static text etc. + +// The layout of a dialog is typically defined in a resource script file +// (often Resource.rc). While this script file can be constructed manually, +// it is often created using a resource editor. If your compiler doesn't +// include a resource editor, you might find ResEdit useful. It is a free +// resource editor available for download at: +// http://www.resedit.net/ + +// CDialog supports modal and modeless dialogs. It also supports the creation +// of dialogs defined in a resource script file, as well as those defined in +// a dialog template. + +// Use the Dialog generic program as the starting point for your own dialog +// applications. +// The DlgSubclass sample demonstrates how to use subclassing to customise +// the behaviour of common controls in a dialog. + + +#ifndef _WIN32XX_DIALOG_H_ +#define _WIN32XX_DIALOG_H_ + +#include "wincore.h" + +#ifndef SWP_NOCOPYBITS + #define SWP_NOCOPYBITS 0x0100 +#endif + +namespace Win32xx +{ + + class CDialog : public CWnd + { + public: + CDialog(UINT nResID, CWnd* pParent = NULL); + CDialog(LPCTSTR lpszResName, CWnd* pParent = NULL); + CDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent = NULL); + virtual ~CDialog(); + + // You probably won't need to override these functions + virtual void AttachItem(int nID, CWnd& Wnd); + virtual HWND Create(CWnd* pParent = NULL); + virtual INT_PTR DoModal(); + virtual HWND DoModeless(); + virtual void SetDlgParent(CWnd* pParent); + BOOL IsModal() const { return m_IsModal; } + BOOL IsIndirect() const { return (NULL != m_lpTemplate); } + + protected: + // These are the functions you might wish to override + virtual INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual INT_PTR DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void EndDialog(INT_PTR nResult); + virtual void OnCancel(); + virtual BOOL OnInitDialog(); + virtual void OnOK(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + + // Can't override these functions + static INT_PTR CALLBACK StaticDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + #ifndef _WIN32_WCE + static LRESULT CALLBACK StaticMsgHook(int nCode, WPARAM wParam, LPARAM lParam); + #endif + + private: + CDialog(const CDialog&); // Disable copy construction + CDialog& operator = (const CDialog&); // Disable assignment operator + + BOOL m_IsModal; // a flag for modal dialogs + LPCTSTR m_lpszResName; // the resource name for the dialog + LPCDLGTEMPLATE m_lpTemplate; // the dialog template for indirect dialogs + HWND m_hParent; // handle to the dialogs's parent window + }; + + +#ifndef _WIN32_WCE + + ////////////////////////////////////// + // Declaration of the CResizer class + // + // The CResizer class can be used to rearrange a dialog's child + // windows when the dialog is resized. + + // To use CResizer, follow the following steps: + // 1) Use Initialize to specify the dialog's CWnd, and min and max size. + // 3) Use AddChild for each child window + // 4) Call HandleMessage from within DialogProc. + // + + // Resize Dialog Styles +#define RD_STRETCH_WIDTH 0x0001 // The item has a variable width +#define RD_STRETCH_HEIGHT 0x0002 // The item has a variable height + + // Resize Dialog alignments + enum Alignment { topleft, topright, bottomleft, bottomright }; + + class CResizer + { + public: + CResizer() : m_pParent(0), m_xScrollPos(0), m_yScrollPos(0) {} + virtual ~CResizer() {} + + virtual void AddChild(CWnd* pWnd, Alignment corner, DWORD dwStyle); + virtual void AddChild(HWND hWnd, Alignment corner, DWORD dwStyle); + virtual void HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void Initialize(CWnd* pParent, RECT rcMin, RECT rcMax = CRect(0,0,0,0)); + virtual void OnHScroll(WPARAM wParam, LPARAM lParam); + virtual void OnVScroll(WPARAM wParam, LPARAM lParam); + virtual void RecalcLayout(); + CRect GetMinRect() const { return m_rcMin; } + CRect GetMaxRect() const { return m_rcMax; } + + struct ResizeData + { + CRect rcInit; + CRect rcOld; + Alignment corner; + BOOL bFixedWidth; + BOOL bFixedHeight; + HWND hWnd; + }; + + private: + CWnd* m_pParent; + std::vector<ResizeData> m_vResizeData; + + CRect m_rcInit; + CRect m_rcMin; + CRect m_rcMax; + + int m_xScrollPos; + int m_yScrollPos; + }; + +#endif + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + //////////////////////////////////// + // Definitions for the CDialog class + // + inline CDialog::CDialog(LPCTSTR lpszResName, CWnd* pParent/* = NULL*/) + : m_IsModal(TRUE), m_lpszResName(lpszResName), m_lpTemplate(NULL) + { + m_hParent = pParent? pParent->GetHwnd() : NULL; + ::InitCommonControls(); + } + + inline CDialog::CDialog(UINT nResID, CWnd* pParent/* = NULL*/) + : m_IsModal(TRUE), m_lpszResName(MAKEINTRESOURCE (nResID)), m_lpTemplate(NULL) + { + m_hParent = pParent? pParent->GetHwnd() : NULL; + ::InitCommonControls(); + } + + //For indirect dialogs - created from a dialog box template in memory. + inline CDialog::CDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent/* = NULL*/) + : m_IsModal(TRUE), m_lpszResName(NULL), m_lpTemplate(lpTemplate) + { + m_hParent = pParent? pParent->GetHwnd() : NULL; + ::InitCommonControls(); + } + + inline CDialog::~CDialog() + { + if (m_hWnd != NULL) + { + if (IsModal()) + ::EndDialog(m_hWnd, 0); + else + Destroy(); + } + } + + inline void CDialog::AttachItem(int nID, CWnd& Wnd) + // Attach a dialog item to a CWnd + { + Wnd.AttachDlgItem(nID, this); + } + + inline HWND CDialog::Create(CWnd* pParent /* = NULL */) + { + // Allow a dialog to be used as a child window + + assert(GetApp()); + SetDlgParent(pParent); + return DoModeless(); + } + + inline INT_PTR CDialog::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + // Override this function in your class derrived from CDialog if you wish to handle messages + // A typical function might look like this: + + // switch (uMsg) + // { + // case MESSAGE1: // Some Windows API message + // OnMessage1(); // A user defined function + // break; // Also do default processing + // case MESSAGE2: + // OnMessage2(); + // return x; // Don't do default processing, but instead return + // // a value recommended by the Windows API documentation + // } + + // Always pass unhandled messages on to DialogProcDefault + return DialogProcDefault(uMsg, wParam, lParam); + } + + inline INT_PTR CDialog::DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + // All DialogProc functions should pass unhandled messages to this function + { + LRESULT lr = 0; + + switch (uMsg) + { + case UWM_CLEANUPTEMPS: + { + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + pTLSData->vTmpWnds.clear(); + } + break; + case WM_INITDIALOG: + { + // Center the dialog + CenterWindow(); + } + return OnInitDialog(); + case WM_COMMAND: + switch (LOWORD (wParam)) + { + case IDOK: + OnOK(); + return TRUE; + case IDCANCEL: + OnCancel(); + return TRUE; + default: + { + // Refelect this message if it's from a control + CWnd* pWnd = GetApp()->GetCWndFromMap((HWND)lParam); + if (pWnd != NULL) + lr = pWnd->OnCommand(wParam, lParam); + + // Handle user commands + if (!lr) + lr = OnCommand(wParam, lParam); + + if (lr) return 0L; + } + break; // Some commands require default processing + } + break; + + case WM_NOTIFY: + { + // Do Notification reflection if it came from a CWnd object + HWND hwndFrom = ((LPNMHDR)lParam)->hwndFrom; + CWnd* pWndFrom = GetApp()->GetCWndFromMap(hwndFrom); + + if (pWndFrom != NULL) + lr = pWndFrom->OnNotifyReflect(wParam, lParam); + else + { + // Some controls (eg ListView) have child windows. + // Reflect those notifications too. + CWnd* pWndFromParent = GetApp()->GetCWndFromMap(::GetParent(hwndFrom)); + if (pWndFromParent != NULL) + lr = pWndFromParent->OnNotifyReflect(wParam, lParam); + } + + // Handle user notifications + if (!lr) lr = OnNotify(wParam, lParam); + + // Set the return code for notifications + if (IsWindow()) + SetWindowLongPtr(DWLP_MSGRESULT, (LONG_PTR)lr); + + return (BOOL)lr; + } + + case WM_PAINT: + { + if (::GetUpdateRect(m_hWnd, NULL, FALSE)) + { + CPaintDC dc(this); + OnDraw(&dc); + } + else + // RedrawWindow can require repainting without an update rect + { + CClientDC dc(this); + OnDraw(&dc); + } + + break; + } + + case WM_ERASEBKGND: + { + CDC dc((HDC)wParam); + BOOL bResult = OnEraseBkgnd(&dc); + dc.Detach(); + if (bResult) return TRUE; + } + break; + + // A set of messages to be reflected back to the control that generated them + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORDLG: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + case WM_DRAWITEM: + case WM_MEASUREITEM: + case WM_DELETEITEM: + case WM_COMPAREITEM: + case WM_CHARTOITEM: + case WM_VKEYTOITEM: + case WM_HSCROLL: + case WM_VSCROLL: + case WM_PARENTNOTIFY: + return MessageReflect(m_hWnd, uMsg, wParam, lParam); + + } // switch(uMsg) + return FALSE; + + } // INT_PTR CALLBACK CDialog::DialogProc(...) + + inline INT_PTR CDialog::DoModal() + { + // Create a modal dialog + // A modal dialog box must be closed by the user before the application continues + + assert( GetApp() ); // Test if Win32++ has been started + assert(!::IsWindow(m_hWnd)); // Only one window per CWnd instance allowed + + INT_PTR nResult = 0; + + try + { + m_IsModal=TRUE; + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + + #ifndef _WIN32_WCE + BOOL IsHookedHere = FALSE; + if (NULL == pTLSData->hHook ) + { + pTLSData->hHook = ::SetWindowsHookEx(WH_MSGFILTER, (HOOKPROC)StaticMsgHook, NULL, ::GetCurrentThreadId()); + IsHookedHere = TRUE; + } + #endif + + HINSTANCE hInstance = GetApp()->GetInstanceHandle(); + pTLSData->pCWnd = this; + + // Create a modal dialog + if (IsIndirect()) + nResult = ::DialogBoxIndirect(hInstance, m_lpTemplate, m_hParent, (DLGPROC)CDialog::StaticDialogProc); + else + { + if (::FindResource(GetApp()->GetResourceHandle(), m_lpszResName, RT_DIALOG)) + hInstance = GetApp()->GetResourceHandle(); + nResult = ::DialogBox(hInstance, m_lpszResName, m_hParent, (DLGPROC)CDialog::StaticDialogProc); + } + + // Tidy up + m_hWnd = NULL; + pTLSData->pCWnd = NULL; + GetApp()->CleanupTemps(); + + #ifndef _WIN32_WCE + if (IsHookedHere) + { + ::UnhookWindowsHookEx(pTLSData->hHook); + pTLSData->hHook = NULL; + } + #endif + + if (nResult == -1) + throw CWinException(_T("Failed to create modal dialog box")); + + } + + catch (const CWinException &e) + { + TRACE(_T("\n*** Failed to create dialog ***\n")); + e.what(); // Display the last error message. + + // eat the exception (don't rethrow) + } + + return nResult; + } + + inline HWND CDialog::DoModeless() + { + assert( GetApp() ); // Test if Win32++ has been started + assert(!::IsWindow(m_hWnd)); // Only one window per CWnd instance allowed + + try + { + m_IsModal=FALSE; + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + + // Store the CWnd pointer in Thread Local Storage + pTLSData->pCWnd = this; + + HINSTANCE hInstance = GetApp()->GetInstanceHandle(); + + // Create a modeless dialog + if (IsIndirect()) + m_hWnd = ::CreateDialogIndirect(hInstance, m_lpTemplate, m_hParent, (DLGPROC)CDialog::StaticDialogProc); + else + { + if (::FindResource(GetApp()->GetResourceHandle(), m_lpszResName, RT_DIALOG)) + hInstance = GetApp()->GetResourceHandle(); + + m_hWnd = ::CreateDialog(hInstance, m_lpszResName, m_hParent, (DLGPROC)CDialog::StaticDialogProc); + } + + // Tidy up + pTLSData->pCWnd = NULL; + + // Now handle dialog creation failure + if (!m_hWnd) + throw CWinException(_T("Failed to create dialog")); + } + + catch (const CWinException &e) + { + TRACE(_T("\n*** Failed to create dialog ***\n")); + e.what(); // Display the last error message. + + // eat the exception (don't rethrow) + } + + return m_hWnd; + } + + inline void CDialog::EndDialog(INT_PTR nResult) + { + assert(::IsWindow(m_hWnd)); + + if (IsModal()) + ::EndDialog(m_hWnd, nResult); + else + Destroy(); + + m_hWnd = NULL; + } + + inline void CDialog::OnCancel() + { + // Override to customize OnCancel behaviour + EndDialog(IDCANCEL); + } + + inline BOOL CDialog::OnInitDialog() + { + // Called when the dialog is initialized + // Override it in your derived class to automatically perform tasks + // The return value is used by WM_INITDIALOG + + return TRUE; + } + + inline void CDialog::OnOK() + { + // Override to customize OnOK behaviour + EndDialog(IDOK); + } + + inline BOOL CDialog::PreTranslateMessage(MSG* pMsg) + { + // allow the dialog to translate keyboard input + if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST)) + { + // Process dialog keystrokes for modeless dialogs + if (!IsModal()) + { + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + if (NULL == pTLSData->hHook) + { + if (IsDialogMessage(pMsg)) + return TRUE; + } + else + { + // A modal message loop is running so we can't do IsDialogMessage. + // Avoid having modal dialogs create other windows, because those + // windows will then use the modal dialog's special message loop. + } + } + } + + return FALSE; + } + + inline void CDialog::SetDlgParent(CWnd* pParent) + // Allows the parent of the dialog to be set before the dialog is created + { + m_hParent = pParent? pParent->GetHwnd() : NULL; + } + + inline INT_PTR CALLBACK CDialog::StaticDialogProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + // Find the CWnd pointer mapped to this HWND + CDialog* w = (CDialog*)GetApp()->GetCWndFromMap(hWnd); + if (0 == w) + { + // The HWND wasn't in the map, so add it now + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + assert(pTLSData); + + // Retrieve pointer to CWnd object from Thread Local Storage TLS + w = (CDialog*)pTLSData->pCWnd; + assert(w); + pTLSData->pCWnd = NULL; + + // Store the Window pointer into the HWND map + w->m_hWnd = hWnd; + w->AddToMap(); + } + + return w->DialogProc(uMsg, wParam, lParam); + + } // INT_PTR CALLBACK CDialog::StaticDialogProc(...) + +#ifndef _WIN32_WCE + inline LRESULT CALLBACK CDialog::StaticMsgHook(int nCode, WPARAM wParam, LPARAM lParam) + { + // Used by Modal Dialogs to PreTranslate Messages + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + + if (nCode == MSGF_DIALOGBOX) + { + MSG* lpMsg = (MSG*) lParam; + + // only pre-translate keyboard events + if ((lpMsg->message >= WM_KEYFIRST && lpMsg->message <= WM_KEYLAST)) + { + for (HWND hWnd = lpMsg->hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd)) + { + CDialog* pDialog = (CDialog*)GetApp()->GetCWndFromMap(hWnd); + if (pDialog && (lstrcmp(pDialog->GetClassName(), _T("#32770")) == 0)) // only for dialogs + { + pDialog->PreTranslateMessage(lpMsg); + break; + } + } + } + } + + return ::CallNextHookEx(pTLSData->hHook, nCode, wParam, lParam); + } +#endif + + + +#ifndef _WIN32_WCE + + ///////////////////////////////////// + // Definitions for the CResizer class + // + + void inline CResizer::AddChild(CWnd* pWnd, Alignment corner, DWORD dwStyle) + // Adds a child window (usually a dialog control) to the set of windows managed by + // the Resizer. + // + // The alignment corner should be set to the closest corner of the dialog. Allowed + // values are topleft, topright, bottomleft, and bottomright. + // Set bFixedWidth to TRUE if the width should be fixed instead of variable. + // Set bFixedHeight to TRUE if the height should be fixed instead of variable. + { + ResizeData rd; + rd.corner = corner; + rd.bFixedWidth = !(dwStyle & RD_STRETCH_WIDTH); + rd.bFixedHeight = !(dwStyle & RD_STRETCH_HEIGHT); + CRect rcInit = pWnd->GetWindowRect(); + m_pParent->ScreenToClient(rcInit); + rd.rcInit = rcInit; + rd.hWnd = pWnd->GetHwnd(); + + m_vResizeData.insert(m_vResizeData.begin(), rd); + } + + void inline CResizer::AddChild(HWND hWnd, Alignment corner, DWORD dwStyle) + // Adds a child window (usually a dialog control) to the set of windows managed by + // the Resizer. + { + AddChild(FromHandle(hWnd), corner, dwStyle); + } + + inline void CResizer::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_SIZE: + RecalcLayout(); + break; + + case WM_HSCROLL: + if (0 == lParam) + OnHScroll(wParam, lParam); + break; + + case WM_VSCROLL: + if (0 == lParam) + OnVScroll(wParam, lParam); + break; + } + } + + void inline CResizer::Initialize(CWnd* pParent, RECT rcMin, RECT rcMax) + // Sets up the Resizer by specifying the parent window (usually a dialog), + // and the minimum and maximum allowed rectangle sizes. + { + assert (NULL != pParent); + + m_pParent = pParent; + m_rcInit = pParent->GetClientRect(); + m_rcMin = rcMin; + m_rcMax = rcMax; + + // Add scroll bar support to the parent window + DWORD dwStyle = (DWORD)m_pParent->GetClassLongPtr(GCL_STYLE); + dwStyle |= WS_HSCROLL | WS_VSCROLL; + m_pParent->SetClassLongPtr(GCL_STYLE, dwStyle); + } + + void inline CResizer::OnHScroll(WPARAM wParam, LPARAM /*lParam*/) + { + int xNewPos; + + switch (LOWORD(wParam)) + { + case SB_PAGEUP: // User clicked the scroll bar shaft left of the scroll box. + xNewPos = m_xScrollPos - 50; + break; + + case SB_PAGEDOWN: // User clicked the scroll bar shaft right of the scroll box. + xNewPos = m_xScrollPos + 50; + break; + + case SB_LINEUP: // User clicked the left arrow. + xNewPos = m_xScrollPos - 5; + break; + + case SB_LINEDOWN: // User clicked the right arrow. + xNewPos = m_xScrollPos + 5; + break; + + case SB_THUMBPOSITION: // User dragged the scroll box. + xNewPos = HIWORD(wParam); + break; + + case SB_THUMBTRACK: // User dragging the scroll box. + xNewPos = HIWORD(wParam); + break; + + default: + xNewPos = m_xScrollPos; + } + + // Scroll the window. + xNewPos = MAX(0, xNewPos); + xNewPos = MIN( xNewPos, GetMinRect().Width() - m_pParent->GetClientRect().Width() ); + int xDelta = xNewPos - m_xScrollPos; + m_xScrollPos = xNewPos; + m_pParent->ScrollWindow(-xDelta, 0, NULL, NULL); + + // Reset the scroll bar. + SCROLLINFO si = {0}; + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = m_xScrollPos; + m_pParent->SetScrollInfo(SB_HORZ, si, TRUE); + } + + void inline CResizer::OnVScroll(WPARAM wParam, LPARAM /*lParam*/) + { + int yNewPos; + + switch (LOWORD(wParam)) + { + case SB_PAGEUP: // User clicked the scroll bar shaft above the scroll box. + yNewPos = m_yScrollPos - 50; + break; + + case SB_PAGEDOWN: // User clicked the scroll bar shaft below the scroll box. + yNewPos = m_yScrollPos + 50; + break; + + case SB_LINEUP: // User clicked the top arrow. + yNewPos = m_yScrollPos - 5; + break; + + case SB_LINEDOWN: // User clicked the bottom arrow. + yNewPos = m_yScrollPos + 5; + break; + + case SB_THUMBPOSITION: // User dragged the scroll box. + yNewPos = HIWORD(wParam); + break; + + case SB_THUMBTRACK: // User dragging the scroll box. + yNewPos = HIWORD(wParam); + break; + + default: + yNewPos = m_yScrollPos; + } + + // Scroll the window. + yNewPos = MAX(0, yNewPos); + yNewPos = MIN( yNewPos, GetMinRect().Height() - m_pParent->GetClientRect().Height() ); + int yDelta = yNewPos - m_yScrollPos; + m_yScrollPos = yNewPos; + m_pParent->ScrollWindow(0, -yDelta, NULL, NULL); + + // Reset the scroll bar. + SCROLLINFO si = {0}; + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + si.nPos = m_yScrollPos; + m_pParent->SetScrollInfo(SB_VERT, si, TRUE); + } + + void inline CResizer::RecalcLayout() + // Repositions the child windows. Call this function when handling + // the WM_SIZE message in the parent window. + { + assert (m_rcInit.Width() > 0 && m_rcInit.Height() > 0); + assert (NULL != m_pParent); + + CRect rcCurrent = m_pParent->GetClientRect(); + + // Adjust the scrolling if required + m_xScrollPos = MIN(m_xScrollPos, MAX(0, m_rcMin.Width() - rcCurrent.Width() ) ); + m_yScrollPos = MIN(m_yScrollPos, MAX(0, m_rcMin.Height() - rcCurrent.Height()) ); + SCROLLINFO si = {0}; + si.cbSize = sizeof(si); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + si.nMax = m_rcMin.Width(); + si.nPage = rcCurrent.Width(); + si.nPos = m_xScrollPos; + m_pParent->SetScrollInfo(SB_HORZ, si, TRUE); + si.nMax = m_rcMin.Height(); + si.nPage = rcCurrent.Height(); + si.nPos = m_yScrollPos; + m_pParent->SetScrollInfo(SB_VERT, si, TRUE); + + rcCurrent.right = MAX( rcCurrent.Width(), m_rcMin.Width() ); + rcCurrent.bottom = MAX( rcCurrent.Height(), m_rcMin.Height() ); + if (!m_rcMax.IsRectEmpty()) + { + rcCurrent.right = MIN( rcCurrent.Width(), m_rcMax.Width() ); + rcCurrent.bottom = MIN( rcCurrent.Height(), m_rcMax.Height() ); + } + + // Declare an iterator to step through the vector + std::vector<ResizeData>::iterator iter; + + for (iter = m_vResizeData.begin(); iter < m_vResizeData.end(); ++iter) + { + int left = 0; + int top = 0; + int width = 0; + int height = 0; + + // Calculate the new size and position of the child window + switch( (*iter).corner ) + { + case topleft: + width = (*iter).bFixedWidth? (*iter).rcInit.Width() : (*iter).rcInit.Width() - m_rcInit.Width() + rcCurrent.Width(); + height = (*iter).bFixedHeight? (*iter).rcInit.Height() : (*iter).rcInit.Height() - m_rcInit.Height() + rcCurrent.Height(); + left = (*iter).rcInit.left; + top = (*iter).rcInit.top; + break; + case topright: + width = (*iter).bFixedWidth? (*iter).rcInit.Width() : (*iter).rcInit.Width() - m_rcInit.Width() + rcCurrent.Width(); + height = (*iter).bFixedHeight? (*iter).rcInit.Height() : (*iter).rcInit.Height() - m_rcInit.Height() + rcCurrent.Height(); + left = (*iter).rcInit.right - width - m_rcInit.Width() + rcCurrent.Width(); + top = (*iter).rcInit.top; + break; + case bottomleft: + width = (*iter).bFixedWidth? (*iter).rcInit.Width() : (*iter).rcInit.Width() - m_rcInit.Width() + rcCurrent.Width(); + height = (*iter).bFixedHeight? (*iter).rcInit.Height() : (*iter).rcInit.Height() - m_rcInit.Height() + rcCurrent.Height(); + left = (*iter).rcInit.left; + top = (*iter).rcInit.bottom - height - m_rcInit.Height() + rcCurrent.Height(); + break; + case bottomright: + width = (*iter).bFixedWidth? (*iter).rcInit.Width() : (*iter).rcInit.Width() - m_rcInit.Width() + rcCurrent.Width(); + height = (*iter).bFixedHeight? (*iter).rcInit.Height() : (*iter).rcInit.Height() - m_rcInit.Height() + rcCurrent.Height(); + left = (*iter).rcInit.right - width - m_rcInit.Width() + rcCurrent.Width(); + top = (*iter).rcInit.bottom - height - m_rcInit.Height() + rcCurrent.Height(); + break; + } + + // Position the child window. + CRect rc(left - m_xScrollPos, top - m_yScrollPos, left + width - m_xScrollPos, top + height - m_yScrollPos); + if ( rc != (*iter).rcOld) + { + CWnd* pWnd = FromHandle((*iter).hWnd); + CWnd *pWndPrev = pWnd->GetWindow(GW_HWNDPREV); // Trick to maintain the original tab order. + HWND hWnd = pWndPrev ? pWndPrev->GetHwnd():NULL; + pWnd->SetWindowPos(hWnd, rc, SWP_NOCOPYBITS); + (*iter).rcOld = rc; + } + } + } + +#endif // #ifndef _WIN32_WCE + +} // namespace Win32xx + + + +#endif // _WIN32XX_DIALOG_H_ + diff --git a/mmc_updater/depends/win32cpp/docking.h b/mmc_updater/depends/win32cpp/docking.h new file mode 100644 index 00000000..9e7c4486 --- /dev/null +++ b/mmc_updater/depends/win32cpp/docking.h @@ -0,0 +1,4214 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// docking.h +// Declaration of the CDocker class + +#ifndef _WIN32XX_DOCKING_H_ +#define _WIN32XX_DOCKING_H_ + + +#include "wincore.h" +#include "gdi.h" +#include "toolbar.h" +#include "tab.h" +#include "frame.h" +#include "default_resource.h" + + +// Docking Styles +#define DS_DOCKED_LEFT 0x0001 // Dock the child left +#define DS_DOCKED_RIGHT 0x0002 // Dock the child right +#define DS_DOCKED_TOP 0x0004 // Dock the child top +#define DS_DOCKED_BOTTOM 0x0008 // Dock the child bottom +#define DS_NO_DOCKCHILD_LEFT 0x0010 // Prevent a child docking left +#define DS_NO_DOCKCHILD_RIGHT 0x0020 // Prevent a child docking right +#define DS_NO_DOCKCHILD_TOP 0x0040 // Prevent a child docking at the top +#define DS_NO_DOCKCHILD_BOTTOM 0x0080 // Prevent a child docking at the bottom +#define DS_NO_RESIZE 0x0100 // Prevent resizing +#define DS_NO_CAPTION 0x0200 // Prevent display of caption when docked +#define DS_NO_CLOSE 0x0400 // Prevent closing of a docker while docked +#define DS_NO_UNDOCK 0x0800 // Prevent undocking and dock closing +#define DS_CLIENTEDGE 0x1000 // Has a 3D border when docked +#define DS_FIXED_RESIZE 0x2000 // Perfomed a fixed resize instead of a proportional resize on dock children +#define DS_DOCKED_CONTAINER 0x4000 // Dock a container within a container +#define DS_DOCKED_LEFTMOST 0x10000 // Leftmost outer docking +#define DS_DOCKED_RIGHTMOST 0x20000 // Rightmost outer docking +#define DS_DOCKED_TOPMOST 0x40000 // Topmost outer docking +#define DS_DOCKED_BOTTOMMOST 0x80000 // Bottommost outer docking + +// Required for Dev-C++ +#ifndef TME_NONCLIENT + #define TME_NONCLIENT 0x00000010 +#endif +#ifndef TME_LEAVE + #define TME_LEAVE 0x000000002 +#endif +#ifndef WM_NCMOUSELEAVE + #define WM_NCMOUSELEAVE 0x000002A2 +#endif + +namespace Win32xx +{ + // Class declarations + class CDockContainer; + class CDocker; + + typedef Shared_Ptr<CDocker> DockPtr; + + struct ContainerInfo + { + TCHAR szTitle[MAX_MENU_STRING]; + int iImage; + CDockContainer* pContainer; + }; + + /////////////////////////////////////// + // Declaration of the CDockContainer class + // A CDockContainer is a CTab window. A CTab has a view window, and optionally a toolbar control. + // A top level CDockContainer can contain other CDockContainers. The view for each container + // (including the top level container) along with possibly its toolbar, is displayed + // within the container parent's view page. + class CDockContainer : public CTab + { + public: + + // Nested class. This is the Wnd for the window displayed over the client area + // of the tab control. The toolbar and view window are child windows of the + // viewpage window. Only the ViewPage of the parent CDockContainer is displayed. It's + // contents are updated with the view window of the relevant container whenever + // a different tab is selected. + class CViewPage : public CWnd + { + + public: + CViewPage() : m_pView(NULL), m_pTab(NULL) {} + virtual ~CViewPage() {} + virtual CToolBar& GetToolBar() const {return (CToolBar&)m_ToolBar;} + virtual CWnd* GetView() const {return m_pView;} + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + virtual void OnCreate(); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual void RecalcLayout(); + virtual void SetView(CWnd& wndView); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + CWnd* GetTabCtrl() const { return m_pTab;} + + private: + CToolBar m_ToolBar; + tString m_tsTooltip; + CWnd* m_pView; + CWnd* m_pTab; + }; + + public: + CDockContainer(); + virtual ~CDockContainer(); + virtual void AddContainer(CDockContainer* pContainer); + virtual void AddToolBarButton(UINT nID, BOOL bEnabled = TRUE); + virtual CDockContainer* GetContainerFromIndex(UINT nPage); + virtual CDockContainer* GetContainerFromView(CWnd* pView) const; + virtual int GetContainerIndex(CDockContainer* pContainer); + virtual SIZE GetMaxTabTextSize(); + virtual CViewPage& GetViewPage() const { return (CViewPage&)m_ViewPage; } + virtual void RecalcLayout(); + virtual void RemoveContainer(CDockContainer* pWnd); + virtual void SelectPage(int nPage); + virtual void SetTabSize(); + virtual void SetupToolBar(); + + // Attributes + CDockContainer* GetActiveContainer() const {return GetContainerFromView(GetActiveView());} + CWnd* GetActiveView() const; + std::vector<ContainerInfo>& GetAllContainers() const {return m_pContainerParent->m_vContainerInfo;} + CDockContainer* GetContainerParent() const { return m_pContainerParent; } + CString& GetDockCaption() const { return (CString&)m_csCaption; } + HICON GetTabIcon() const { return m_hTabIcon; } + LPCTSTR GetTabText() const { return m_tsTabText.c_str(); } + virtual CToolBar& GetToolBar() const { return GetViewPage().GetToolBar(); } + CWnd* GetView() const { return GetViewPage().GetView(); } + void SetActiveContainer(CDockContainer* pContainer); + void SetDockCaption(LPCTSTR szCaption) { m_csCaption = szCaption; } + void SetTabIcon(HICON hTabIcon) { m_hTabIcon = hTabIcon; } + void SetTabIcon(UINT nID_Icon); + void SetTabIcon(int i, HICON hIcon) { CTab::SetTabIcon(i, hIcon); } + void SetTabText(LPCTSTR szText) { m_tsTabText = szText; } + void SetTabText(UINT nTab, LPCTSTR szText); + void SetView(CWnd& Wnd); + + protected: + virtual void OnCreate(); + virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam); + virtual void OnMouseLeave(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam); + virtual void PreCreate(CREATESTRUCT &cs); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + std::vector<ContainerInfo> m_vContainerInfo; + tString m_tsTabText; + CString m_csCaption; + CViewPage m_ViewPage; + int m_iCurrentPage; + CDockContainer* m_pContainerParent; + HICON m_hTabIcon; + int m_nTabPressed; + + }; + + typedef struct DRAGPOS + { + NMHDR hdr; + POINT ptPos; + UINT DockZone; + } *LPDRAGPOS; + + + ///////////////////////////////////////// + // Declaration of the CDocker class + // A CDocker window allows other CDocker windows to be "docked" inside it. + // A CDocker can dock on the top, left, right or bottom side of a parent CDocker. + // There is no theoretical limit to the number of CDockers within CDockers. + class CDocker : public CWnd + { + public: + // A nested class for the splitter bar that seperates the docked panes. + class CDockBar : public CWnd + { + public: + CDockBar(); + virtual ~CDockBar(); + virtual void OnDraw(CDC* pDC); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void PreRegisterClass(WNDCLASS& wc); + virtual void SendNotify(UINT nMessageID); + virtual void SetColor(COLORREF color); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + CDocker* GetDock() {return m_pDock;} + int GetWidth() {return m_DockBarWidth;} + void SetDock(CDocker* pDock) {m_pDock = pDock;} + void SetWidth(int nWidth) {m_DockBarWidth = nWidth;} + + private: + CDockBar(const CDockBar&); // Disable copy construction + CDockBar& operator = (const CDockBar&); // Disable assignment operator + + CDocker* m_pDock; + DRAGPOS m_DragPos; + CBrush m_brBackground; + int m_DockBarWidth; + }; + + // A nested class for the window inside a CDocker which includes all of this docked client. + // It's the remaining part of the CDocker that doesn't belong to the CDocker's children. + // The Docker's view window is a child window of CDockClient. + class CDockClient : public CWnd + { + public: + CDockClient(); + virtual ~CDockClient() {} + virtual void Draw3DBorder(RECT& Rect); + virtual void DrawCaption(WPARAM wParam); + virtual void DrawCloseButton(CDC& DrawDC, BOOL bFocus); + virtual CRect GetCloseRect(); + virtual void SendNotify(UINT nMessageID); + + CString& GetCaption() const { return (CString&)m_csCaption; } + CWnd* GetView() const { return m_pView; } + void SetDock(CDocker* pDock) { m_pDock = pDock;} + void SetCaption(LPCTSTR szCaption) { m_csCaption = szCaption; } + void SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2); + void SetClosePressed() { m_IsClosePressed = TRUE; } + void SetView(CWnd& Wnd) { m_pView = &Wnd; } + + protected: + virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam); + virtual void OnMouseActivate(WPARAM wParam, LPARAM lParam); + virtual void OnMouseMove(WPARAM wParam, LPARAM lParam); + virtual void OnNCCalcSize(WPARAM& wParam, LPARAM& lParam); + virtual LRESULT OnNCHitTest(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNCLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnNCMouseLeave(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNCMouseMove(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNCPaint(WPARAM wParam, LPARAM lParam); + virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam); + virtual void PreRegisterClass(WNDCLASS& wc); + virtual void PreCreate(CREATESTRUCT& cs); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CDockClient(const CDockClient&); // Disable copy construction + CDockClient& operator = (const CDockClient&); // Disable assignment operator + + CString m_csCaption; + CPoint m_Oldpt; + CDocker* m_pDock; + CWnd* m_pView; + BOOL m_IsClosePressed; + BOOL m_bOldFocus; + BOOL m_bCaptionPressed; + BOOL m_IsTracking; + COLORREF m_Foregnd1; + COLORREF m_Backgnd1; + COLORREF m_Foregnd2; + COLORREF m_Backgnd2; + }; + + // This nested class is used to indicate where a window could dock by + // displaying a blue tinted window. + class CDockHint : public CWnd + { + public: + CDockHint(); + virtual ~CDockHint(); + virtual RECT CalcHintRectContainer(CDocker* pDockTarget); + virtual RECT CalcHintRectInner(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide); + virtual RECT CalcHintRectOuter(CDocker* pDockDrag, UINT uDockSide); + virtual void DisplayHint(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide); + virtual void OnDraw(CDC* pDC); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void ShowHintWindow(CDocker* pDockTarget, CRect rcHint); + + private: + CDockHint(const CDockHint&); // Disable copy construction + CDockHint& operator = (const CDockHint&); // Disable assignment operator + + CBitmap m_bmBlueTint; + UINT m_uDockSideOld; + }; + + class CTarget : public CWnd + { + public: + CTarget() {} + virtual ~CTarget(); + virtual void OnDraw(CDC* pDC); + virtual void PreCreate(CREATESTRUCT &cs); + + protected: + CBitmap m_bmImage; + + private: + CTarget(const CTarget&); // Disable copy construction + CTarget& operator = (const CTarget&); // Disable assignment operator + }; + + class CTargetCentre : public CTarget + { + public: + CTargetCentre(); + virtual ~CTargetCentre(); + virtual void OnDraw(CDC* pDC); + virtual void OnCreate(); + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + BOOL IsOverContainer() { return m_bIsOverContainer; } + + private: + CTargetCentre(const CTargetCentre&); // Disable copy construction + CTargetCentre& operator = (const CTargetCentre&); // Disable assignment operator + + BOOL m_bIsOverContainer; + CDocker* m_pOldDockTarget; + }; + + class CTargetLeft : public CTarget + { + public: + CTargetLeft() {m_bmImage.LoadImage(IDW_SDLEFT,0,0,0);} + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + + private: + CTargetLeft(const CTargetLeft&); // Disable copy construction + CTargetLeft& operator = (const CTargetLeft&); // Disable assignment operator + }; + + class CTargetTop : public CTarget + { + public: + CTargetTop() {m_bmImage.LoadImage(IDW_SDTOP,0,0,0);} + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + private: + CTargetTop(const CTargetTop&); // Disable copy construction + CTargetTop& operator = (const CTargetTop&); // Disable assignment operator + }; + + class CTargetRight : public CTarget + { + public: + CTargetRight() {m_bmImage.LoadImage(IDW_SDRIGHT,0,0,0);} + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + + private: + CTargetRight(const CTargetRight&); // Disable copy construction + CTargetRight& operator = (const CTargetRight&); // Disable assignment operator + }; + + class CTargetBottom : public CTarget + { + public: + CTargetBottom() {m_bmImage.LoadImage(IDW_SDBOTTOM,0,0,0);} + virtual BOOL CheckTarget(LPDRAGPOS pDragPos); + }; + + friend class CTargetCentre; + friend class CTargetLeft; + friend class CTargetTop; + friend class CTargetRight; + friend class CTargetBottom; + friend class CDockClient; + friend class CDockContainer; + + public: + // Operations + CDocker(); + virtual ~CDocker(); + virtual CDocker* AddDockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, int nDockID = 0); + virtual CDocker* AddUndockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, RECT rc, int nDockID = 0); + virtual void Close(); + virtual void CloseAllDockers(); + virtual void Dock(CDocker* pDocker, UINT uDockSide); + virtual void DockInContainer(CDocker* pDock, DWORD dwDockStyle); + virtual CDockContainer* GetContainer() const; + virtual CDocker* GetActiveDocker() const; + virtual CDocker* GetDockAncestor() const; + virtual CDocker* GetDockFromID(int n_DockID) const; + virtual CDocker* GetDockFromPoint(POINT pt) const; + virtual CDocker* GetDockFromView(CWnd* pView) const; + virtual CDocker* GetTopmostDocker() const; + virtual int GetDockSize() const; + virtual CTabbedMDI* GetTabbedMDI() const; + virtual int GetTextHeight(); + virtual void Hide(); + virtual BOOL LoadRegistrySettings(tString tsRegistryKeyName); + virtual void RecalcDockLayout(); + virtual BOOL SaveRegistrySettings(tString tsRegistryKeyName); + virtual void Undock(CPoint pt, BOOL bShowUndocked = TRUE); + virtual void UndockContainer(CDockContainer* pContainer, CPoint pt, BOOL bShowUndocked); + virtual BOOL VerifyDockers(); + + // Attributes + virtual CDockBar& GetDockBar() const {return (CDockBar&)m_DockBar;} + virtual CDockClient& GetDockClient() const {return (CDockClient&)m_DockClient;} + virtual CDockHint& GetDockHint() const {return m_pDockAncestor->m_DockHint;} + + + std::vector <DockPtr> & GetAllDockers() const {return GetDockAncestor()->m_vAllDockers;} + int GetBarWidth() const {return GetDockBar().GetWidth();} + CString& GetCaption() const {return GetDockClient().GetCaption();} + std::vector <CDocker*> & GetDockChildren() const {return (std::vector <CDocker*> &)m_vDockChildren;} + int GetDockID() const {return m_nDockID;} + CDocker* GetDockParent() const {return m_pDockParent;} + DWORD GetDockStyle() const {return m_DockStyle;} + CWnd* GetView() const {return GetDockClient().GetView();} + BOOL IsChildOfDocker(CWnd* pWnd) const; + BOOL IsDocked() const; + BOOL IsDragAutoResize(); + BOOL IsRelated(CWnd* pWnd) const; + BOOL IsUndocked() const; + void SetBarColor(COLORREF color) {GetDockBar().SetColor(color);} + void SetBarWidth(int nWidth) {GetDockBar().SetWidth(nWidth);} + void SetCaption(LPCTSTR szCaption); + void SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2); + void SetCaptionHeight(int nHeight); + void SetDockStyle(DWORD dwDockStyle); + void SetDockSize(int DockSize); + void SetDragAutoResize(BOOL bAutoResize); + void SetView(CWnd& wndView); + + protected: + virtual CDocker* NewDockerFromID(int idDock); + virtual void OnActivate(WPARAM wParam, LPARAM lParam); + virtual void OnCaptionTimer(WPARAM wParam, LPARAM lParam); + virtual void OnCreate(); + virtual void OnDestroy(WPARAM wParam, LPARAM lParam); + virtual void OnDockDestroyed(WPARAM wParam, LPARAM lParam); + virtual void OnExitSizeMove(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual void OnSetFocus(WPARAM wParam, LPARAM lParam); + virtual void OnSysColorChange(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnSysCommand(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnWindowPosChanging(WPARAM wParam, LPARAM lParam); + virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CDocker(const CDocker&); // Disable copy construction + CDocker& operator = (const CDocker&); // Disable assignment operator + void CheckAllTargets(LPDRAGPOS pDragPos); + void CloseAllTargets(); + void DockOuter(CDocker* pDocker, DWORD dwDockStyle); + void DrawAllCaptions(); + void DrawHashBar(HWND hBar, POINT Pos); + void ConvertToChild(HWND hWndParent); + void ConvertToPopup(RECT rc); + void MoveDockChildren(CDocker* pDockTarget); + void PromoteFirstChild(); + void RecalcDockChildLayout(CRect rc); + void ResizeDockers(LPDRAGPOS pdp); + CDocker* SeparateFromDock(); + void SendNotify(UINT nMessageID); + void SetUndockPosition(CPoint pt); + std::vector<CDocker*> SortDockers(); + + CDockBar m_DockBar; + CDockHint m_DockHint; + CDockClient m_DockClient; + CTargetCentre m_TargetCentre; + CTargetLeft m_TargetLeft; + CTargetTop m_TargetTop; + CTargetRight m_TargetRight; + CPoint m_OldPoint; + CTargetBottom m_TargetBottom; + CDocker* m_pDockParent; + CDocker* m_pDockAncestor; + CDocker* m_pDockActive; + + std::vector <CDocker*> m_vDockChildren; + std::vector <DockPtr> m_vAllDockers; // Only used in DockAncestor + + CRect m_rcBar; + CRect m_rcChild; + + BOOL m_BlockMove; + BOOL m_Undocking; + BOOL m_bIsClosing; + BOOL m_bIsDragging; + BOOL m_bDragAutoResize; + int m_DockStartSize; + int m_nDockID; + int m_nTimerCount; + int m_NCHeight; + DWORD m_dwDockZone; + double m_DockSizeRatio; + DWORD m_DockStyle; + HWND m_hOldFocus; + + }; // class CDocker + + struct DockInfo + { + DWORD DockStyle; + int DockSize; + int DockID; + int DockParentID; + RECT Rect; + }; + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +namespace Win32xx +{ + + ///////////////////////////////////////////////////////////// + // Definitions for the CDockBar class nested within CDocker + // + inline CDocker::CDockBar::CDockBar() : m_pDock(NULL), m_DockBarWidth(4) + { + m_brBackground.CreateSolidBrush(RGB(192,192,192)); + } + + inline CDocker::CDockBar::~CDockBar() + { + } + + inline void CDocker::CDockBar::OnDraw(CDC* pDC) + { + CRect rcClient = GetClientRect(); + pDC->SelectObject(&m_brBackground); + pDC->PatBlt(0, 0, rcClient.Width(), rcClient.Height(), PATCOPY); + } + + inline void CDocker::CDockBar::PreCreate(CREATESTRUCT &cs) + { + // Create a child window, initially hidden + cs.style = WS_CHILD; + } + + inline void CDocker::CDockBar::PreRegisterClass(WNDCLASS& wc) + { + wc.lpszClassName = _T("Win32++ Bar"); + wc.hbrBackground = m_brBackground; + } + + inline void CDocker::CDockBar::SendNotify(UINT nMessageID) + { + // Send a splitter bar notification to the parent + m_DragPos.hdr.code = nMessageID; + m_DragPos.hdr.hwndFrom = m_hWnd; + m_DragPos.ptPos = GetCursorPos(); + m_DragPos.ptPos.x += 1; + GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&m_DragPos); + } + + inline void CDocker::CDockBar::SetColor(COLORREF color) + { + // Useful colors: + // GetSysColor(COLOR_BTNFACE) // Default Grey + // RGB(196, 215, 250) // Default Blue + + m_brBackground.CreateSolidBrush(color); + } + + inline LRESULT CDocker::CDockBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + { + switch (uMsg) + { + case WM_SETCURSOR: + { + if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE)) + { + HCURSOR hCursor; + DWORD dwSide = GetDock()->GetDockStyle() & 0xF; + if ((dwSide == DS_DOCKED_LEFT) || (dwSide == DS_DOCKED_RIGHT)) + hCursor = LoadCursor(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_SPLITH)); + else + hCursor = LoadCursor(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_SPLITV)); + + if (hCursor) SetCursor(hCursor); + else TRACE(_T("**WARNING** Missing cursor resource for slider bar\n")); + + return TRUE; + } + else + SetCursor(LoadCursor(NULL, IDC_ARROW)); + } + break; + + case WM_ERASEBKGND: + return 0; + + case WM_LBUTTONDOWN: + { + if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE)) + { + SendNotify(UWM_BAR_START); + SetCapture(); + } + } + break; + + case WM_LBUTTONUP: + if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE) && (GetCapture() == this)) + { + SendNotify(UWM_BAR_END); + ReleaseCapture(); + } + break; + + case WM_MOUSEMOVE: + if (!(m_pDock->GetDockStyle() & DS_NO_RESIZE) && (GetCapture() == this)) + { + SendNotify(UWM_BAR_MOVE); + } + break; + } + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CDockClient class nested within CDocker + // + inline CDocker::CDockClient::CDockClient() : m_pView(0), m_IsClosePressed(FALSE), + m_bOldFocus(FALSE), m_bCaptionPressed(FALSE), m_IsTracking(FALSE) + { + m_Foregnd1 = RGB(32,32,32); + m_Backgnd1 = RGB(190,207,227); + m_Foregnd2 = GetSysColor(COLOR_BTNTEXT); + m_Backgnd2 = GetSysColor(COLOR_BTNFACE); + } + + inline void CDocker::CDockClient::Draw3DBorder(RECT& Rect) + { + // Imitates the drawing of the WS_EX_CLIENTEDGE extended style + // This draws a 2 pixel border around the specified Rect + CWindowDC dc(this); + CRect rcw = Rect; + dc.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_3DSHADOW)); + dc.MoveTo(0, rcw.Height()); + dc.LineTo(0, 0); + dc.LineTo(rcw.Width(), 0); + dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DDKSHADOW)); + dc.MoveTo(1, rcw.Height()-2); + dc.LineTo(1, 1); + dc.LineTo(rcw.Width()-2, 1); + dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DHILIGHT)); + dc.MoveTo(rcw.Width()-1, 0); + dc.LineTo(rcw.Width()-1, rcw.Height()-1); + dc.LineTo(0, rcw.Height()-1); + dc.CreatePen(PS_SOLID,1, GetSysColor(COLOR_3DLIGHT)); + dc.MoveTo(rcw.Width()-2, 1); + dc.LineTo(rcw.Width()-2, rcw.Height()-2); + dc.LineTo(1, rcw.Height()-2); + } + + inline CRect CDocker::CDockClient::GetCloseRect() + { + // Calculate the close rect position in screen co-ordinates + CRect rcClose; + + int gap = 4; + CRect rc = GetWindowRect(); + int cx = GetSystemMetrics(SM_CXSMICON); + int cy = GetSystemMetrics(SM_CYSMICON); + + rcClose.top = 2 + rc.top + m_pDock->m_NCHeight/2 - cy/2; + rcClose.bottom = 2 + rc.top + m_pDock->m_NCHeight/2 + cy/2; + rcClose.right = rc.right - gap; + rcClose.left = rcClose.right - cx; + +#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500) + if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL) + { + rcClose.left = rc.left + gap; + rcClose.right = rcClose.left + cx; + } +#endif + + + return rcClose; + } + + inline void CDocker::CDockClient::DrawCaption(WPARAM wParam) + { + if (IsWindow() && m_pDock->IsDocked() && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + BOOL bFocus = m_pDock->IsChildOfDocker(GetFocus()); + m_bOldFocus = FALSE; + + // Acquire the DC for our NonClient painting + CDC* pDC; + if ((wParam != 1) && (bFocus == m_bOldFocus)) + pDC = GetDCEx((HRGN)wParam, DCX_WINDOW|DCX_INTERSECTRGN|DCX_PARENTCLIP); + else + pDC = GetWindowDC(); + + // Create and set up our memory DC + CRect rc = GetWindowRect(); + CMemDC dcMem(pDC); + int rcAdjust = (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE)? 2 : 0; + int Width = MAX(rc.Width() -rcAdjust, 0); + int Height = m_pDock->m_NCHeight + rcAdjust; + dcMem.CreateCompatibleBitmap(pDC, Width, Height); + m_bOldFocus = bFocus; + + // Set the font for the title + NONCLIENTMETRICS info = {0}; + info.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); + dcMem.CreateFontIndirect(&info.lfStatusFont); + + // Set the Colours + if (bFocus) + { + dcMem.SetTextColor(m_Foregnd1); + dcMem.CreateSolidBrush(m_Backgnd1); + dcMem.SetBkColor(m_Backgnd1); + } + else + { + dcMem.SetTextColor(m_Foregnd2); + dcMem.CreateSolidBrush(m_Backgnd2); + dcMem.SetBkColor(m_Backgnd2); + } + + // Draw the rectangle + dcMem.CreatePen(PS_SOLID, 1, RGB(160, 150, 140)); + dcMem.Rectangle(rcAdjust, rcAdjust, rc.Width() -rcAdjust, m_pDock->m_NCHeight +rcAdjust); + + // Display the caption + int cx = (m_pDock->GetDockStyle() & DS_NO_CLOSE)? 0 : GetSystemMetrics(SM_CXSMICON); + CRect rcText(4 +rcAdjust, rcAdjust, rc.Width() -4 - cx -rcAdjust, m_pDock->m_NCHeight +rcAdjust); + dcMem.DrawText(m_csCaption, m_csCaption.GetLength(), rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS); + + // Draw the close button + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + DrawCloseButton(dcMem, bFocus); + + // Draw the 3D border + if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + Draw3DBorder(rc); + + // Copy the Memory DC to the window's DC + pDC->BitBlt(rcAdjust, rcAdjust, Width, Height, &dcMem, rcAdjust, rcAdjust, SRCCOPY); + + // Required for Win98/WinME + pDC->Destroy(); + } + } + + inline void CDocker::CDockClient::DrawCloseButton(CDC& DrawDC, BOOL bFocus) + { + // The close button isn't displayed on Win95 + if (GetWinVersion() == 1400) return; + + if (m_pDock->IsDocked() && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + // Determine the close button's drawing position relative to the window + CRect rcClose = GetCloseRect(); + UINT uState = GetCloseRect().PtInRect(GetCursorPos())? m_IsClosePressed && IsLeftButtonDown()? 2 : 1 : 0; + ScreenToClient(rcClose); + + if (GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + { + rcClose.OffsetRect(2, m_pDock->m_NCHeight+2); + if (GetWindowRect().Height() < (m_pDock->m_NCHeight+4)) + rcClose.OffsetRect(-2, -2); + } + else + rcClose.OffsetRect(0, m_pDock->m_NCHeight-2); + + // Draw the outer highlight for the close button + if (!IsRectEmpty(&rcClose)) + { + switch (uState) + { + case 0: + { + // Normal button + DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + break; + } + + case 1: + { + // Popped up button + // Draw outline, white at top, black on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + } + + break; + case 2: + { + // Pressed button + // Draw outline, black on top, white on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + } + break; + } + + // Manually Draw Close Button + if (bFocus) + DrawDC.CreatePen(PS_SOLID, 1, m_Foregnd1); + else + DrawDC.CreatePen(PS_SOLID, 1, m_Foregnd2); + + DrawDC.MoveTo(rcClose.left + 3, rcClose.top +3); + DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.left + 4, rcClose.top +3); + DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -3); + + DrawDC.MoveTo(rcClose.left + 3, rcClose.top +4); + DrawDC.LineTo(rcClose.right - 3, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -3, rcClose.top +3); + DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -3, rcClose.top +4); + DrawDC.LineTo(rcClose.left + 3, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -4, rcClose.top +3); + DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -3); + } + } + } + + inline void CDocker::CDockClient::OnNCCalcSize(WPARAM& wParam, LPARAM& lParam) + { + // Sets the non-client area (and hence sets the client area) + // This function modifies lParam + + UNREFERENCED_PARAMETER(wParam); + + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if (m_pDock->IsDocked()) + { + LPRECT rc = (LPRECT)lParam; + rc->top += m_pDock->m_NCHeight; + } + } + } + + inline LRESULT CDocker::CDockClient::OnNCHitTest(WPARAM wParam, LPARAM lParam) + { + // Identify which part of the non-client area the cursor is over + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if (m_pDock->IsDocked()) + { + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + + // Indicate if the point is in the close button (except for Win95) + if ((GetWinVersion() > 1400) && (GetCloseRect().PtInRect(pt))) + return HTCLOSE; + + ScreenToClient(pt); + + // Indicate if the point is in the caption + if (pt.y < 0) + return HTCAPTION; + } + } + return CWnd::WndProcDefault(WM_NCHITTEST, wParam, lParam); + } + + inline LRESULT CDocker::CDockClient::OnNCLButtonDown(WPARAM wParam, LPARAM lParam) + { + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if ((HTCLOSE == wParam) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + { + m_IsClosePressed = TRUE; + SetCapture(); + } + + m_bCaptionPressed = TRUE; + m_Oldpt.x = GET_X_LPARAM(lParam); + m_Oldpt.y = GET_Y_LPARAM(lParam); + if (m_pDock->IsDocked()) + { + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + ScreenToClient(pt); + m_pView->SetFocus(); + + // Update the close button + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + { + CWindowDC dc(this); + DrawCloseButton(dc, m_bOldFocus); + } + + return 0L; + } + } + return CWnd::WndProcDefault(WM_NCLBUTTONDOWN, wParam, lParam); + } + + inline void CDocker::CDockClient::OnLButtonUp(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & (DS_NO_CAPTION|DS_NO_CLOSE))) + { + m_bCaptionPressed = FALSE; + if (m_IsClosePressed && GetCloseRect().PtInRect(GetCursorPos())) + { + // Destroy the docker + if (dynamic_cast<CDockContainer*>(m_pDock->GetView())) + { + CDockContainer* pContainer = ((CDockContainer*)m_pDock->GetView())->GetActiveContainer(); + CDocker* pDock = m_pDock->GetDockFromView(pContainer); + pDock->GetDockClient().SetClosePressed(); + m_pDock->UndockContainer(pContainer, GetCursorPos(), FALSE); + pDock->Destroy(); + } + else + { + m_pDock->Hide(); + m_pDock->Destroy(); + } + } + } + } + + inline void CDocker::CDockClient::OnLButtonDown(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + m_IsClosePressed = FALSE; + ReleaseCapture(); + CWindowDC dc(this); + DrawCloseButton(dc, m_bOldFocus); + } + + inline void CDocker::CDockClient::OnMouseActivate(WPARAM wParam, LPARAM lParam) + // Focus changed, so redraw the captions + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + m_pDock->GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0); + } + } + + inline void CDocker::CDockClient::OnMouseMove(WPARAM wParam, LPARAM lParam) + { + OnNCMouseMove(wParam, lParam); + } + + inline void CDocker::CDockClient::OnNCMouseLeave(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + m_IsTracking = FALSE; + CWindowDC dc(this); + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & (DS_NO_CAPTION|DS_NO_CLOSE)) && m_pDock->IsDocked()) + DrawCloseButton(dc, m_bOldFocus); + + m_IsTracking = FALSE; + } + + inline LRESULT CDocker::CDockClient::OnNCMouseMove(WPARAM wParam, LPARAM lParam) + { + if (!m_IsTracking) + { + TRACKMOUSEEVENT TrackMouseEventStruct = {0}; + TrackMouseEventStruct.cbSize = sizeof(TrackMouseEventStruct); + TrackMouseEventStruct.dwFlags = TME_LEAVE|TME_NONCLIENT; + TrackMouseEventStruct.hwndTrack = m_hWnd; + _TrackMouseEvent(&TrackMouseEventStruct); + m_IsTracking = TRUE; + } + + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if (m_pDock->IsDocked()) + { + // Discard phantom mouse move messages + if ( (m_Oldpt.x == GET_X_LPARAM(lParam) ) && (m_Oldpt.y == GET_Y_LPARAM(lParam))) + return 0L; + + if (IsLeftButtonDown() && (wParam == HTCAPTION) && (m_bCaptionPressed)) + { + CDocker* pDock = (CDocker*)GetParent(); + if (pDock) + pDock->Undock(GetCursorPos()); + } + + // Update the close button + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + { + CWindowDC dc(this); + DrawCloseButton(dc, m_bOldFocus); + } + } + + m_bCaptionPressed = FALSE; + } + return CWnd::WndProcDefault(WM_MOUSEMOVE, wParam, lParam); + } + + inline LRESULT CDocker::CDockClient::OnNCPaint(WPARAM wParam, LPARAM lParam) + { + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CAPTION)) + { + if (m_pDock->IsDocked()) + { + DefWindowProc(WM_NCPAINT, wParam, lParam); + DrawCaption(wParam); + return 0; + } + } + return CWnd::WndProcDefault(WM_NCPAINT, wParam, lParam); + } + + inline void CDocker::CDockClient::OnWindowPosChanged(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + // Reposition the View window to cover the DockClient's client area + CRect rc = GetClientRect(); + m_pView->SetWindowPos(NULL, rc, SWP_SHOWWINDOW); + } + + inline void CDocker::CDockClient::PreRegisterClass(WNDCLASS& wc) + { + wc.lpszClassName = _T("Win32++ DockClient"); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + } + + inline void CDocker::CDockClient::PreCreate(CREATESTRUCT& cs) + { + DWORD dwStyle = m_pDock->GetDockStyle(); + if (dwStyle & DS_CLIENTEDGE) + cs.dwExStyle = WS_EX_CLIENTEDGE; + +#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500) + if (m_pDock->GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL) + cs.dwExStyle |= WS_EX_LAYOUTRTL; +#endif + + } + + inline void CDocker::CDockClient::SendNotify(UINT nMessageID) + { + // Fill the DragPos structure with data + DRAGPOS DragPos; + DragPos.hdr.code = nMessageID; + DragPos.hdr.hwndFrom = m_hWnd; + DragPos.ptPos = GetCursorPos(); + + // Send a DragPos notification to the docker + GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&DragPos); + } + + inline void CDocker::CDockClient::SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF Foregnd2, COLORREF Backgnd2) + { + // Set the colors used when drawing the caption + // m_Foregnd1 Foreground colour (focused). m_Backgnd1 Background colour (focused) + // m_Foregnd2 Foreground colour (not focused). m_Backgnd2 Foreground colour (not focused) + m_Foregnd1 = Foregnd1; + m_Backgnd1 = Backgnd1; + m_Foregnd2 = Foregnd2; + m_Backgnd2 = Backgnd2; + } + + inline LRESULT CDocker::CDockClient::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_LBUTTONUP: + { + ReleaseCapture(); + if ((0 != m_pDock) && !(m_pDock->GetDockStyle() & DS_NO_CLOSE)) + { + CWindowDC dc(this); + DrawCloseButton(dc, m_bOldFocus); + OnLButtonUp(wParam, lParam); + } + } + break; + + case WM_MOUSEACTIVATE: + OnMouseActivate(wParam, lParam); + break; + + case WM_MOUSEMOVE: + OnMouseMove(wParam, lParam); + break; + + case WM_NCCALCSIZE: + OnNCCalcSize(wParam, lParam); + break; + + case WM_NCHITTEST: + return OnNCHitTest(wParam, lParam); + + case WM_NCLBUTTONDOWN: + return OnNCLButtonDown(wParam, lParam); + + case WM_NCMOUSEMOVE: + return OnNCMouseMove(wParam, lParam); + + case WM_NCPAINT: + return OnNCPaint(wParam, lParam); + + case WM_NCMOUSELEAVE: + OnNCMouseLeave(wParam, lParam); + break; + + case WM_WINDOWPOSCHANGED: + OnWindowPosChanged(wParam, lParam); + break; + } + + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + + ////////////////////////////////////////////////////////////// + // Definitions for the CDockHint class nested within CDocker + // + inline CDocker::CDockHint::CDockHint() : m_uDockSideOld(0) + { + } + + inline CDocker::CDockHint::~CDockHint() + { + } + + inline RECT CDocker::CDockHint::CalcHintRectContainer(CDocker* pDockTarget) + { + // Calculate the hint window's position for container docking + CRect rcHint = pDockTarget->GetDockClient().GetWindowRect(); + if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + rcHint.InflateRect(-2, -2); + pDockTarget->ScreenToClient(rcHint); + + return rcHint; + } + + inline RECT CDocker::CDockHint::CalcHintRectInner(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide) + { + // Calculate the hint window's position for inner docking + CRect rcHint = pDockTarget->GetDockClient().GetWindowRect(); + if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + rcHint.InflateRect(-2, -2); + pDockTarget->ScreenToClient(rcHint); + + int Width; + CRect rcDockDrag = pDockDrag->GetWindowRect(); + CRect rcDockTarget = pDockTarget->GetDockClient().GetWindowRect(); + if ((uDockSide == DS_DOCKED_LEFT) || (uDockSide == DS_DOCKED_RIGHT)) + { + Width = rcDockDrag.Width(); + if (Width >= (rcDockTarget.Width() - pDockDrag->GetBarWidth())) + Width = MAX(rcDockTarget.Width()/2 - pDockDrag->GetBarWidth(), pDockDrag->GetBarWidth()); + } + else + { + Width = rcDockDrag.Height(); + if (Width >= (rcDockTarget.Height() - pDockDrag->GetBarWidth())) + Width = MAX(rcDockTarget.Height()/2 - pDockDrag->GetBarWidth(), pDockDrag->GetBarWidth()); + } + switch (uDockSide) + { + case DS_DOCKED_LEFT: + rcHint.right = rcHint.left + Width; + break; + case DS_DOCKED_RIGHT: + rcHint.left = rcHint.right - Width; + break; + case DS_DOCKED_TOP: + rcHint.bottom = rcHint.top + Width; + break; + case DS_DOCKED_BOTTOM: + rcHint.top = rcHint.bottom - Width; + break; + } + + return rcHint; + } + + inline RECT CDocker::CDockHint::CalcHintRectOuter(CDocker* pDockDrag, UINT uDockSide) + { + // Calculate the hint window's position for outer docking + CDocker* pDockTarget = pDockDrag->GetDockAncestor(); + CRect rcHint = pDockTarget->GetClientRect(); + if (pDockTarget->GetDockClient().GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_CLIENTEDGE) + rcHint.InflateRect(-2, -2); + + int Width; + CRect rcDockDrag = pDockDrag->GetWindowRect(); + CRect rcDockTarget = pDockTarget->GetDockClient().GetWindowRect(); + + // Limit the docked size to half the parent's size if it won't fit inside parent + if ((uDockSide == DS_DOCKED_LEFTMOST) || (uDockSide == DS_DOCKED_RIGHTMOST)) + { + Width = rcDockDrag.Width(); + int BarWidth = pDockDrag->GetBarWidth(); + if (Width >= pDockTarget->GetDockClient().GetClientRect().Width() - pDockDrag->GetBarWidth()) + Width = MAX(pDockTarget->GetDockClient().GetClientRect().Width()/2 - BarWidth, BarWidth); + } + else + { + Width = rcDockDrag.Height(); + int BarWidth = pDockDrag->GetBarWidth(); + if (Width >= pDockTarget->GetDockClient().GetClientRect().Height() - pDockDrag->GetBarWidth()) + Width = MAX(pDockTarget->GetDockClient().GetClientRect().Height()/2 - BarWidth, BarWidth); + } + switch (uDockSide) + { + case DS_DOCKED_LEFTMOST: + rcHint.right = rcHint.left + Width; + break; + case DS_DOCKED_RIGHTMOST: + rcHint.left = rcHint.right - Width; + break; + case DS_DOCKED_TOPMOST: + rcHint.bottom = rcHint.top + Width; + break; + case DS_DOCKED_BOTTOMMOST: + rcHint.top = rcHint.bottom - Width; + break; + } + + return rcHint; + } + + inline void CDocker::CDockHint::DisplayHint(CDocker* pDockTarget, CDocker* pDockDrag, UINT uDockSide) + { + // Ensure a new hint window is created if dock side changes + if (uDockSide != m_uDockSideOld) + { + Destroy(); + pDockTarget->RedrawWindow( NULL, NULL, RDW_NOERASE | RDW_UPDATENOW ); + pDockDrag->RedrawWindow(); + } + m_uDockSideOld = uDockSide; + + if (!IsWindow()) + { + CRect rcHint; + + if (uDockSide & 0xF) + rcHint = CalcHintRectInner(pDockTarget, pDockDrag, uDockSide); + else if (uDockSide & 0xF0000) + rcHint = CalcHintRectOuter(pDockDrag, uDockSide); + else if (uDockSide & DS_DOCKED_CONTAINER) + rcHint = CalcHintRectContainer(pDockTarget); + else + return; + + ShowHintWindow(pDockTarget, rcHint); + } + } + + inline void CDocker::CDockHint::OnDraw(CDC* pDC) + { + // Display the blue tinted bitmap + CRect rc = GetClientRect(); + CMemDC MemDC(pDC); + MemDC.SelectObject(&m_bmBlueTint); + pDC->BitBlt(0, 0, rc.Width(), rc.Height(), &MemDC, 0, 0, SRCCOPY); + } + + inline void CDocker::CDockHint::PreCreate(CREATESTRUCT &cs) + { + cs.style = WS_POPUP; + + // WS_EX_TOOLWINDOW prevents the window being displayed on the taskbar + cs.dwExStyle = WS_EX_TOOLWINDOW; + + cs.lpszClass = _T("Win32++ DockHint"); + } + + inline void CDocker::CDockHint::ShowHintWindow(CDocker* pDockTarget, CRect rcHint) + { + // Save the Dock window's blue tinted bitmap + CClientDC dcDesktop(NULL); + CMemDC dcMem(&dcDesktop); + CRect rcBitmap = rcHint; + CRect rcTarget = rcHint; + pDockTarget->ClientToScreen(rcTarget); + + m_bmBlueTint.CreateCompatibleBitmap(&dcDesktop, rcBitmap.Width(), rcBitmap.Height()); + CBitmap* pOldBitmap = dcMem.SelectObject(&m_bmBlueTint); + dcMem.BitBlt(0, 0, rcBitmap.Width(), rcBitmap.Height(), &dcDesktop, rcTarget.left, rcTarget.top, SRCCOPY); + dcMem.SelectObject(pOldBitmap); + TintBitmap(&m_bmBlueTint, -64, -24, +128); + + // Create the Hint window + if (!IsWindow()) + { + Create(pDockTarget); + } + + pDockTarget->ClientToScreen(rcHint); + SetWindowPos(NULL, rcHint, SWP_SHOWWINDOW|SWP_NOZORDER|SWP_NOACTIVATE); + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetCentre class nested within CDocker + // + inline CDocker::CTargetCentre::CTargetCentre() : m_bIsOverContainer(FALSE), m_pOldDockTarget(0) + { + } + + inline CDocker::CTargetCentre::~CTargetCentre() + { + } + + inline void CDocker::CTargetCentre::OnDraw(CDC* pDC) + { + CBitmap bmCentre(IDW_SDCENTER); + CBitmap bmLeft(IDW_SDLEFT); + CBitmap bmRight(IDW_SDRIGHT); + CBitmap bmTop(IDW_SDTOP); + CBitmap bmBottom(IDW_SDBOTTOM); + + if (bmCentre.GetHandle()) pDC->DrawBitmap(0, 0, 88, 88, bmCentre, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Centre\n")); + + if (bmLeft.GetHandle()) pDC->DrawBitmap(0, 29, 31, 29, bmLeft, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Left\n")); + + if (bmTop.GetHandle()) pDC->DrawBitmap(29, 0, 29, 31, bmTop, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Top\n")); + + if (bmRight.GetHandle()) pDC->DrawBitmap(55, 29, 31, 29, bmRight, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Right\n")); + + if (bmBottom.GetHandle()) pDC->DrawBitmap(29, 55, 29, 31, bmBottom, RGB(255,0,255)); + else TRACE(_T("Missing docking resource: Target Bottom\n")); + + if (IsOverContainer()) + { + CBitmap bmMiddle(IDW_SDMIDDLE); + pDC->DrawBitmap(31, 31, 25, 26, bmMiddle, RGB(255,0,255)); + } + } + + inline void CDocker::CTargetCentre::OnCreate() + { + // Use a region to create an irregularly shapped window + POINT ptArray[16] = { {0,29}, {22, 29}, {29, 22}, {29, 0}, + {58, 0}, {58, 22}, {64, 29}, {87, 29}, + {87, 58}, {64, 58}, {58, 64}, {58, 87}, + {29, 87}, {29, 64}, {23, 58}, {0, 58} }; + + CRgn rgnPoly; + rgnPoly.CreatePolygonRgn(ptArray, 16, WINDING); + SetWindowRgn(&rgnPoly, FALSE); + } + + inline BOOL CDocker::CTargetCentre::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pDragPos->ptPos); + if (NULL == pDockTarget) return FALSE; + + if (!IsWindow()) Create(); + m_bIsOverContainer = (dynamic_cast<CDockContainer*>(pDockTarget->GetView()) != NULL); + + // Redraw the target if the dock target changes + if (m_pOldDockTarget != pDockTarget) Invalidate(); + m_pOldDockTarget = pDockTarget; + + int cxImage = 88; + int cyImage = 88; + + CRect rcTarget = pDockTarget->GetDockClient().GetWindowRect(); + int xMid = rcTarget.left + (rcTarget.Width() - cxImage)/2; + int yMid = rcTarget.top + (rcTarget.Height() - cyImage)/2; + SetWindowPos(HWND_TOPMOST, xMid, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + + // Create the docking zone rectangles + CPoint pt = pDragPos->ptPos; + ScreenToClient(pt); + CRect rcLeft(0, 29, 31, 58); + CRect rcTop(29, 0, 58, 31); + CRect rcRight(55, 29, 87, 58); + CRect rcBottom(29, 55, 58, 87); + CRect rcMiddle(31, 31, 56, 57); + + // Test if our cursor is in one of the docking zones + if ((rcLeft.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_LEFT)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_LEFT); + pDockDrag->m_dwDockZone = DS_DOCKED_LEFT; + return TRUE; + } + else if ((rcTop.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_TOP)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_TOP); + pDockDrag->m_dwDockZone = DS_DOCKED_TOP; + return TRUE; + } + else if ((rcRight.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_RIGHT)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_RIGHT); + pDockDrag->m_dwDockZone = DS_DOCKED_RIGHT; + return TRUE; + } + else if ((rcBottom.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_BOTTOM)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_BOTTOM); + pDockDrag->m_dwDockZone = DS_DOCKED_BOTTOM; + return TRUE; + } + else if ((rcMiddle.PtInRect(pt)) && (IsOverContainer())) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_CONTAINER); + pDockDrag->m_dwDockZone = DS_DOCKED_CONTAINER; + return TRUE; + } + else + return FALSE; + } + + //////////////////////////////////////////////////////////////// + // Definitions for the CTarget class nested within CDocker + // CTarget is the base class for a number of CTargetXXX classes + inline CDocker::CTarget::~CTarget() + { + } + + inline void CDocker::CTarget::OnDraw(CDC* pDC) + { + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (m_bmImage) + pDC->DrawBitmap(0, 0, cxImage, cyImage, m_bmImage, RGB(255,0,255)); + else + TRACE(_T("Missing docking resource\n")); + } + + inline void CDocker::CTarget::PreCreate(CREATESTRUCT &cs) + { + cs.style = WS_POPUP; + cs.dwExStyle = WS_EX_TOPMOST|WS_EX_TOOLWINDOW; + cs.lpszClass = _T("Win32++ DockTargeting"); + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetLeft class nested within CDocker + // + inline BOOL CDocker::CTargetLeft::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CPoint pt = pDragPos->ptPos; + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker(); + if (pDockTarget != pDockDrag->GetDockAncestor()) + { + Destroy(); + return FALSE; + } + + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (!IsWindow()) + { + Create(); + CRect rc = pDockTarget->GetWindowRect(); + int yMid = rc.top + (rc.Height() - cyImage)/2; + SetWindowPos(HWND_TOPMOST, rc.left + 10, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + } + + CRect rcLeft(0, 0, cxImage, cyImage); + ScreenToClient(pt); + + // Test if our cursor is in one of the docking zones + if ((rcLeft.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_LEFT)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_LEFTMOST); + pDockDrag->m_dwDockZone = DS_DOCKED_LEFTMOST; + return TRUE; + } + + return FALSE; + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetTop class nested within CDocker + // + inline BOOL CDocker::CTargetTop::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CPoint pt = pDragPos->ptPos; + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker(); + if (pDockTarget != pDockDrag->GetDockAncestor()) + { + Destroy(); + return FALSE; + } + + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (!IsWindow()) + { + Create(); + CRect rc = pDockTarget->GetWindowRect(); + int xMid = rc.left + (rc.Width() - cxImage)/2; + SetWindowPos(HWND_TOPMOST, xMid, rc.top + 10, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + } + + CRect rcTop(0, 0, cxImage, cyImage); + ScreenToClient(pt); + + // Test if our cursor is in one of the docking zones + if ((rcTop.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_TOP)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_TOPMOST); + pDockDrag->m_dwDockZone = DS_DOCKED_TOPMOST; + return TRUE; + } + + return FALSE; + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetRight class nested within CDocker + // + inline BOOL CDocker::CTargetRight::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CPoint pt = pDragPos->ptPos; + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker(); + if (pDockTarget != pDockDrag->GetDockAncestor()) + { + Destroy(); + return FALSE; + } + + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (!IsWindow()) + { + Create(); + CRect rc = pDockTarget->GetWindowRect(); + int yMid = rc.top + (rc.Height() - cyImage)/2; + SetWindowPos(HWND_TOPMOST, rc.right - 10 - cxImage, yMid, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + } + + CRect rcRight(0, 0, cxImage, cyImage); + ScreenToClient(pt); + + // Test if our cursor is in one of the docking zones + if ((rcRight.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_RIGHT)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_RIGHTMOST); + pDockDrag->m_dwDockZone = DS_DOCKED_RIGHTMOST; + return TRUE; + } + + return FALSE; + } + + + //////////////////////////////////////////////////////////////// + // Definitions for the CTargetBottom class nested within CDocker + // + inline BOOL CDocker::CTargetBottom::CheckTarget(LPDRAGPOS pDragPos) + { + CDocker* pDockDrag = (CDocker*)FromHandle(pDragPos->hdr.hwndFrom); + if (NULL == pDockDrag) return FALSE; + + CPoint pt = pDragPos->ptPos; + CDocker* pDockTarget = pDockDrag->GetDockFromPoint(pt)->GetTopmostDocker(); + if (pDockTarget != pDockDrag->GetDockAncestor()) + { + Destroy(); + return FALSE; + } + + BITMAP bm = m_bmImage.GetBitmapData(); + int cxImage = bm.bmWidth; + int cyImage = bm.bmHeight; + + if (!IsWindow()) + { + Create(); + CRect rc = pDockTarget->GetWindowRect(); + int xMid = rc.left + (rc.Width() - cxImage)/2; + SetWindowPos(HWND_TOPMOST, xMid, rc.bottom - 10 - cyImage, cxImage, cyImage, SWP_NOACTIVATE|SWP_SHOWWINDOW); + } + CRect rcBottom(0, 0, cxImage, cyImage); + ScreenToClient(pt); + + // Test if our cursor is in one of the docking zones + if ((rcBottom.PtInRect(pt)) && !(pDockTarget->GetDockStyle() & DS_NO_DOCKCHILD_BOTTOM)) + { + pDockDrag->m_BlockMove = TRUE; + pDockTarget->GetDockHint().DisplayHint(pDockTarget, pDockDrag, DS_DOCKED_BOTTOMMOST); + pDockDrag->m_dwDockZone = DS_DOCKED_BOTTOMMOST; + return TRUE; + } + + return FALSE; + } + + + ///////////////////////////////////////// + // Definitions for the CDocker class + // + inline CDocker::CDocker() : m_pDockParent(NULL), m_pDockActive(NULL), m_BlockMove(FALSE), m_Undocking(FALSE), + m_bIsClosing(FALSE), m_bIsDragging(FALSE), m_bDragAutoResize(TRUE), m_DockStartSize(0), m_nDockID(0), + m_nTimerCount(0), m_NCHeight(0), m_dwDockZone(0), m_DockSizeRatio(1.0), m_DockStyle(0), m_hOldFocus(0) + { + // Assume this docker is the DockAncestor for now. + m_pDockAncestor = this; + } + + inline CDocker::~CDocker() + { + GetDockBar().Destroy(); + + std::vector <DockPtr>::iterator iter; + if (GetDockAncestor() == this) + { + // Destroy all dock descendants of this dock ancestor + for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter) + { + (*iter)->Destroy(); + } + } + } + + inline CDocker* CDocker::AddDockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, int nDockID /* = 0*/) + // This function creates the docker, and adds it to the docker heirachy as docked + { + // Create the docker window as a child of the frame window. + // This pernamently sets the frame window as the docker window's owner, + // even when its parent is subsequently changed. + + assert(pDocker); + + // Store the Docker's pointer in the DockAncestor's vector for later deletion + GetDockAncestor()->m_vAllDockers.push_back(DockPtr(pDocker)); + + pDocker->SetDockStyle(dwDockStyle); + pDocker->m_nDockID = nDockID; + pDocker->m_pDockAncestor = GetDockAncestor(); + pDocker->m_pDockParent = this; + pDocker->SetDockSize(DockSize); + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + pDocker->Create(pFrame); + pDocker->SetParent(this); + + // Dock the docker window + if (dwDockStyle & DS_DOCKED_CONTAINER) + DockInContainer(pDocker, dwDockStyle); + else + Dock(pDocker, dwDockStyle); + + // Issue TRACE warnings for any missing resources + HMODULE hMod= GetApp()->GetResourceHandle(); + + if (!(dwDockStyle & DS_NO_RESIZE)) + { + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SPLITH), RT_GROUP_CURSOR)) + TRACE(_T("**WARNING** Horizontal cursor resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SPLITV), RT_GROUP_CURSOR)) + TRACE(_T("**WARNING** Vertical cursor resource missing\n")); + } + + if (!(dwDockStyle & DS_NO_UNDOCK)) + { + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDCENTER), RT_BITMAP)) + TRACE(_T("**WARNING** Docking center bitmap resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDLEFT), RT_BITMAP)) + TRACE(_T("**WARNING** Docking left bitmap resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDRIGHT), RT_BITMAP)) + TRACE(_T("**WARNING** Docking right bitmap resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDTOP), RT_BITMAP)) + TRACE(_T("**WARNING** Docking top bitmap resource missing\n")); + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDBOTTOM), RT_BITMAP)) + TRACE(_T("**WARNING** Docking center bottom resource missing\n")); + } + + if (dwDockStyle & DS_DOCKED_CONTAINER) + { + if (!FindResource(hMod, MAKEINTRESOURCE(IDW_SDMIDDLE), RT_BITMAP)) + TRACE(_T("**WARNING** Docking container bitmap resource missing\n")); + } + + return pDocker; + } + + inline CDocker* CDocker::AddUndockedChild(CDocker* pDocker, DWORD dwDockStyle, int DockSize, RECT rc, int nDockID /* = 0*/) + // This function creates the docker, and adds it to the docker heirachy as undocked + { + assert(pDocker); + + // Store the Docker's pointer in the DockAncestor's vector for later deletion + GetDockAncestor()->m_vAllDockers.push_back(DockPtr(pDocker)); + + pDocker->SetDockSize(DockSize); + pDocker->SetDockStyle(dwDockStyle & 0XFFFFFF0); + pDocker->m_nDockID = nDockID; + pDocker->m_pDockAncestor = GetDockAncestor(); + + // Initially create the as a child window of the frame + // This makes the frame window the owner of our docker + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + pDocker->Create(pFrame); + pDocker->SetParent(this); + + // Change the Docker to a POPUP window + DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE; + pDocker->SetWindowLongPtr(GWL_STYLE, dwStyle); + pDocker->SetRedraw(FALSE); + pDocker->SetParent(0); + pDocker->SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED); + pDocker->SetRedraw(TRUE); + pDocker->RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_ALLCHILDREN); + pDocker->SetWindowText(pDocker->GetCaption().c_str()); + + return pDocker; + } + + inline void CDocker::CheckAllTargets(LPDRAGPOS pDragPos) + // Calls CheckTarget for each possible target zone + { + if (!GetDockAncestor()->m_TargetCentre.CheckTarget(pDragPos)) + { + if (!GetDockAncestor()->m_TargetLeft.CheckTarget(pDragPos)) + { + if(!GetDockAncestor()->m_TargetTop.CheckTarget(pDragPos)) + { + if(!GetDockAncestor()->m_TargetRight.CheckTarget(pDragPos)) + { + if(!GetDockAncestor()->m_TargetBottom.CheckTarget(pDragPos)) + { + // Not in a docking zone, so clean up + NMHDR nmhdr = pDragPos->hdr; + CDocker* pDockDrag = (CDocker*)FromHandle(nmhdr.hwndFrom); + if (pDockDrag) + { + if (pDockDrag->m_BlockMove) + pDockDrag->RedrawWindow(0, 0, RDW_FRAME|RDW_INVALIDATE); + + GetDockHint().Destroy(); + pDockDrag->m_dwDockZone = 0; + pDockDrag->m_BlockMove = FALSE; + } + } + } + } + } + } + } + + inline BOOL CDocker::VerifyDockers() + // A diagnostic routine which verifies the integrity of the docking layout + { + BOOL bResult = TRUE; + + // Check dock ancestor + std::vector<DockPtr>::iterator iter; + + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter) + { + if (GetDockAncestor() != (*iter)->m_pDockAncestor) + { + TRACE(_T("Invalid Dock Ancestor\n")); + bResult = FALSE; + } + } + + // Check presence of dock parent + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter) + { + if ((*iter)->IsUndocked() && (*iter)->m_pDockParent != 0) + { + TRACE(_T("Error: Undocked dockers should not have a dock parent\n")); + bResult = FALSE; + } + + if ((*iter)->IsDocked() && (*iter)->m_pDockParent == 0) + { + TRACE(_T("Error: Docked dockers should have a dock parent\n")); + bResult = FALSE; + } + } + + // Check dock parent/child relationship + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter) + { + std::vector<CDocker*>::iterator iterChild; + for (iterChild = (*iter)->GetDockChildren().begin(); iterChild != (*iter)->GetDockChildren().end(); ++iterChild) + { + if ((*iterChild)->m_pDockParent != (*iter).get()) + { + TRACE(_T("Error: Docking parent/Child information mismatch\n")); + bResult = FALSE; + } + if ((*iterChild)->GetParent() != (*iter).get()) + { + TRACE(_T("Error: Incorrect windows child parent relationship\n")); + bResult = FALSE; + } + } + } + + // Check dock parent chain + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); ++iter) + { + CDocker* pDockTopLevel = (*iter)->GetTopmostDocker(); + if (pDockTopLevel->IsDocked()) + TRACE(_T("Error: Top level parent should be undocked\n")); + } + + return bResult; + } + + inline void CDocker::Close() + { + // Destroy the docker + Hide(); + Destroy(); + } + + inline void CDocker::CloseAllDockers() + { + assert(this == GetDockAncestor()); // Must call CloseAllDockers from the DockAncestor + + std::vector <DockPtr>::iterator v; + + SetRedraw(FALSE); + std::vector<DockPtr> AllDockers = GetAllDockers(); + for (v = AllDockers.begin(); v != AllDockers.end(); ++v) + { + // The CDocker is destroyed when the window is destroyed + (*v)->m_bIsClosing = TRUE; + (*v)->Destroy(); // Destroy the window + } + + GetDockChildren().clear(); + SetRedraw(TRUE); + RecalcDockLayout(); + } + + inline void CDocker::CloseAllTargets() + { + GetDockAncestor()->m_TargetCentre.Destroy(); + GetDockAncestor()->m_TargetLeft.Destroy(); + GetDockAncestor()->m_TargetTop.Destroy(); + GetDockAncestor()->m_TargetRight.Destroy(); + GetDockAncestor()->m_TargetBottom.Destroy(); + } + + inline void CDocker::Dock(CDocker* pDocker, UINT DockStyle) + // Docks the specified docker inside this docker + { + assert(pDocker); + + pDocker->m_pDockParent = this; + pDocker->m_BlockMove = FALSE; + pDocker->SetDockStyle(DockStyle); + m_vDockChildren.push_back(pDocker); + pDocker->ConvertToChild(m_hWnd); + + // Limit the docked size to half the parent's size if it won't fit inside parent + if (((DockStyle & 0xF) == DS_DOCKED_LEFT) || ((DockStyle &0xF) == DS_DOCKED_RIGHT)) + { + int Width = GetDockClient().GetWindowRect().Width(); + int BarWidth = pDocker->GetBarWidth(); + if (pDocker->m_DockStartSize >= (Width - BarWidth)) + pDocker->SetDockSize(MAX(Width/2 - BarWidth, BarWidth)); + + pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetWindowRect().Width(); + } + else + { + int Height = GetDockClient().GetWindowRect().Height(); + int BarWidth = pDocker->GetBarWidth(); + if (pDocker->m_DockStartSize >= (Height - BarWidth)) + pDocker->SetDockSize(MAX(Height/2 - BarWidth, BarWidth)); + + pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetWindowRect().Height(); + } + + // Redraw the docked windows + GetAncestor()->SetForegroundWindow(); + GetTopmostDocker()->m_hOldFocus = pDocker->GetView()->GetHwnd(); + pDocker->GetView()->SetFocus(); + + GetTopmostDocker()->SetRedraw(FALSE); + RecalcDockLayout(); + GetTopmostDocker()->SetRedraw(TRUE); + GetTopmostDocker()->RedrawWindow(); + } + + inline void CDocker::DockInContainer(CDocker* pDock, DWORD dwDockStyle) + // Add a container to an existing container + { + if ((dwDockStyle & DS_DOCKED_CONTAINER) && (dynamic_cast<CDockContainer*>(pDock->GetView()))) + { + // Transfer any dock children to this docker + pDock->MoveDockChildren(this); + + // Transfer container children to the target container + CDockContainer* pContainer = (CDockContainer*)GetView(); + CDockContainer* pContainerSource = (CDockContainer*)pDock->GetView(); + + if (pContainerSource->GetAllContainers().size() > 1) + { + // The container we're about to add has children, so transfer those first + std::vector<ContainerInfo>::reverse_iterator riter; + std::vector<ContainerInfo> AllContainers = pContainerSource->GetAllContainers(); + for ( riter = AllContainers.rbegin() ; riter < AllContainers.rend() -1; ++riter ) + { + // Remove child container from pContainerSource + CDockContainer* pContainerChild = (*riter).pContainer; + pContainerChild->ShowWindow(SW_HIDE); + pContainerSource->RemoveContainer(pContainerChild); + + // Add child container to this container + pContainer->AddContainer(pContainerChild); + + CDocker* pDockChild = GetDockFromView(pContainerChild); + pDockChild->SetParent(this); + pDockChild->m_pDockParent = this; + } + } + + pContainer->AddContainer((CDockContainer*)pDock->GetView()); + pDock->m_pDockParent = this; + pDock->m_BlockMove = FALSE; + pDock->ShowWindow(SW_HIDE); + pDock->SetWindowLongPtr(GWL_STYLE, WS_CHILD); + pDock->SetDockStyle(dwDockStyle); + pDock->SetParent(this); + } + } + + inline void CDocker::DockOuter(CDocker* pDocker, DWORD dwDockStyle) + // Docks the specified docker inside the dock ancestor + { + assert(pDocker); + + pDocker->m_pDockParent = GetDockAncestor(); + + DWORD OuterDocking = dwDockStyle & 0xF0000; + DWORD DockSide = OuterDocking / 0x10000; + dwDockStyle &= 0xFFF0FFFF; + dwDockStyle |= DockSide; + + // Set the dock styles + DWORD dwStyle = WS_CHILD | WS_VISIBLE; + pDocker->m_BlockMove = FALSE; + pDocker->SetWindowLongPtr(GWL_STYLE, dwStyle); + pDocker->ShowWindow(SW_HIDE); + pDocker->SetDockStyle(dwDockStyle); + + // Set the docking relationships + std::vector<CDocker*>::iterator iter = GetDockAncestor()->m_vDockChildren.begin(); + GetDockAncestor()->m_vDockChildren.insert(iter, pDocker); + pDocker->SetParent(GetDockAncestor()); + pDocker->GetDockBar().SetParent(GetDockAncestor()); + + // Limit the docked size to half the parent's size if it won't fit inside parent + if (((dwDockStyle & 0xF) == DS_DOCKED_LEFT) || ((dwDockStyle &0xF) == DS_DOCKED_RIGHT)) + { + int Width = GetDockAncestor()->GetDockClient().GetWindowRect().Width(); + int BarWidth = pDocker->GetBarWidth(); + if (pDocker->m_DockStartSize >= (Width - BarWidth)) + pDocker->SetDockSize(MAX(Width/2 - BarWidth, BarWidth)); + + pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetDockAncestor()->GetWindowRect().Width(); + } + else + { + int Height = GetDockAncestor()->GetDockClient().GetWindowRect().Height(); + int BarWidth = pDocker->GetBarWidth(); + if (pDocker->m_DockStartSize >= (Height - BarWidth)) + pDocker->SetDockSize(MAX(Height/2 - BarWidth, BarWidth)); + + pDocker->m_DockSizeRatio = ((double)pDocker->m_DockStartSize) / (double)GetDockAncestor()->GetWindowRect().Height(); + } + + // Redraw the docked windows + GetAncestor()->SetFocus(); + pDocker->GetView()->SetFocus(); + RecalcDockLayout(); + } + + inline void CDocker::DrawAllCaptions() + { + std::vector<DockPtr>::iterator iter; + for (iter = GetAllDockers().begin(); iter != GetAllDockers().end(); iter++) + { + if ((*iter)->IsDocked()) + (*iter)->GetDockClient().DrawCaption((WPARAM)1); + } + } + + inline void CDocker::DrawHashBar(HWND hBar, POINT Pos) + // Draws a hashed bar while the splitter bar is being dragged + { + CDocker* pDock = ((CDockBar*)FromHandle(hBar))->GetDock(); + if (NULL == pDock) return; + + BOOL bVertical = ((pDock->GetDockStyle() & 0xF) == DS_DOCKED_LEFT) || ((pDock->GetDockStyle() & 0xF) == DS_DOCKED_RIGHT); + + CClientDC dcBar(this); + + WORD HashPattern[] = {0x55,0xAA,0x55,0xAA,0x55,0xAA,0x55,0xAA}; + CBitmap bmHash; + CBrush brDithered; + bmHash.CreateBitmap(8, 8, 1, 1, HashPattern); + brDithered.CreatePatternBrush(&bmHash); + dcBar.SelectObject(&brDithered); + + CRect rc = FromHandle(hBar)->GetWindowRect(); + ScreenToClient(rc); + int cx = rc.Width(); + int cy = rc.Height(); + int BarWidth = pDock->GetDockBar().GetWidth(); + + if (bVertical) + dcBar.PatBlt(Pos.x - BarWidth/2, rc.top, BarWidth, cy, PATINVERT); + else + dcBar.PatBlt(rc.left, Pos.y - BarWidth/2, cx, BarWidth, PATINVERT); + } + + inline CDockContainer* CDocker::GetContainer() const + { + CDockContainer* pContainer = NULL; + if (dynamic_cast<CDockContainer*>(GetView())) + pContainer = (CDockContainer*)GetView(); + + return pContainer; + } + + inline CDocker* CDocker::GetActiveDocker() const + // Returns the docker whose child window has focus + { + CWnd* pWnd = GetFocus(); + CDocker* pDock= NULL; + while (pWnd && (pDock == NULL)) + { + if (IsRelated(pWnd)) + pDock = (CDocker*)pWnd; + + pWnd = pWnd->GetParent(); + } + + return pDock; + } + + inline CDocker* CDocker::GetDockAncestor() const + // The GetDockAncestor function retrieves the pointer to the + // ancestor (root docker parent) of the Docker. + { + return m_pDockAncestor; + } + + inline CDocker* CDocker::GetDockFromPoint(POINT pt) const + // Retrieves the Docker whose view window contains the specified point + { + // Step 1: Find the top level Docker the point is over + CDocker* pDockTop = NULL; + CWnd* pAncestor = GetDockAncestor()->GetAncestor(); + + // Iterate through all top level windows + CWnd* pWnd = GetWindow(GW_HWNDFIRST); + while(pWnd) + { + if (IsRelated(pWnd) || pWnd == pAncestor) + { + CDocker* pDockTest; + if (pWnd == pAncestor) + pDockTest = GetDockAncestor(); + else + pDockTest = (CDocker*)pWnd; + + CRect rc = pDockTest->GetClientRect(); + pDockTest->ClientToScreen(rc); + if ((this != pDockTest) && rc.PtInRect(pt)) + { + pDockTop = pDockTest; + break; + } + } + + pWnd = pWnd->GetWindow(GW_HWNDNEXT); + } + + // Step 2: Find the docker child whose view window has the point + CDocker* pDockTarget = NULL; + if (pDockTop) + { + CDocker* pDockParent = pDockTop; + CDocker* pDockTest = pDockParent; + + while (IsRelated(pDockTest)) + { + pDockParent = pDockTest; + CPoint ptLocal = pt; + pDockParent->ScreenToClient(ptLocal); + pDockTest = (CDocker*)pDockParent->ChildWindowFromPoint(ptLocal); + assert (pDockTest != pDockParent); + } + + CRect rc = pDockParent->GetDockClient().GetWindowRect(); + if (rc.PtInRect(pt)) pDockTarget = pDockParent; + } + + return pDockTarget; + } + + inline CDocker* CDocker::GetDockFromID(int n_DockID) const + { + std::vector <DockPtr>::iterator v; + + if (GetDockAncestor()) + { + for (v = GetDockAncestor()->m_vAllDockers.begin(); v != GetDockAncestor()->m_vAllDockers.end(); v++) + { + if (n_DockID == (*v)->GetDockID()) + return (*v).get(); + } + } + + return 0; + } + + inline CDocker* CDocker::GetDockFromView(CWnd* pView) const + { + CDocker* pDock = 0; + std::vector<DockPtr>::iterator iter; + std::vector<DockPtr> AllDockers = GetAllDockers(); + for (iter = AllDockers.begin(); iter != AllDockers.end(); ++iter) + { + if ((*iter)->GetView() == pView) + pDock = (*iter).get(); + } + + return pDock; + } + + inline int CDocker::GetDockSize() const + { + // Returns the size of the docker to be used if it is redocked + // Note: This function returns 0 if the docker has the DS_DOCKED_CONTAINER style + + CRect rcParent; + if (GetDockParent()) + rcParent = GetDockParent()->GetWindowRect(); + else + rcParent = GetDockAncestor()->GetWindowRect(); + + double DockSize = 0; + if ((GetDockStyle() & DS_DOCKED_LEFT) || (GetDockStyle() & DS_DOCKED_RIGHT)) + DockSize = (double)(rcParent.Width()*m_DockSizeRatio); + else if ((GetDockStyle() & DS_DOCKED_TOP) || (GetDockStyle() & DS_DOCKED_BOTTOM)) + DockSize = (double)(rcParent.Height()*m_DockSizeRatio); + else if ((GetDockStyle() & DS_DOCKED_CONTAINER)) + DockSize = 0; + + return (int)DockSize; + } + + inline CDocker* CDocker::GetTopmostDocker() const + // Returns the docker's parent at the top of the Z order. + // Could be the dock ancestor or an undocked docker. + { + CDocker* pDockTopLevel = (CDocker* const)this; + + while(pDockTopLevel->GetDockParent()) + { + assert (pDockTopLevel != pDockTopLevel->GetDockParent()); + pDockTopLevel = pDockTopLevel->GetDockParent(); + } + + return pDockTopLevel; + } + + inline CTabbedMDI* CDocker::GetTabbedMDI() const + { + CTabbedMDI* pTabbedMDI = NULL; + if (dynamic_cast<CTabbedMDI*>(GetView())) + pTabbedMDI = (CTabbedMDI*)GetView(); + + return pTabbedMDI; + } + + inline int CDocker::GetTextHeight() + { + NONCLIENTMETRICS nm = {0}; + nm.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo (SPI_GETNONCLIENTMETRICS, 0, &nm, 0); + LOGFONT lf = nm.lfStatusFont; + + CClientDC dc(this); + dc.CreateFontIndirect(&lf); + CSize szText = dc.GetTextExtentPoint32(_T("Text"), lstrlen(_T("Text"))); + return szText.cy; + } + + inline void CDocker::Hide() + { + // Undocks a docker (if needed) and hides it. + // Do unhide the docker, dock it. + + if (IsDocked()) + { + if (dynamic_cast<CDockContainer*>(GetView())) + { + CDockContainer* pContainer = GetContainer(); + CDocker* pDock = GetDockFromView(pContainer->GetContainerParent()); + pDock->UndockContainer(pContainer, GetCursorPos(), FALSE); + } + else + { + CDocker* pDockUndockedFrom = SeparateFromDock(); + pDockUndockedFrom->RecalcDockLayout(); + } + } + + ShowWindow(SW_HIDE); + } + + inline BOOL CDocker::IsChildOfDocker(CWnd* pWnd) const + // returns true if the specified window is a child of this docker + { + while ((pWnd != NULL) && (pWnd != GetDockAncestor())) + { + if (pWnd == (CWnd*)this) return TRUE; + if (IsRelated(pWnd)) break; + pWnd = pWnd->GetParent(); + } + + return FALSE; + } + + inline BOOL CDocker::IsDocked() const + { + return (((m_DockStyle&0xF) || (m_DockStyle & DS_DOCKED_CONTAINER)) && !m_Undocking); // Boolean expression + } + + inline BOOL CDocker::IsDragAutoResize() + { + return m_bDragAutoResize; + } + + inline BOOL CDocker::IsRelated(CWnd* pWnd) const + // Returns TRUE if the hWnd is a docker within this dock family + { + if (GetDockAncestor() == pWnd) return TRUE; + + std::vector<DockPtr>::iterator iter; + for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter) + { + if ((*iter).get() == pWnd) return TRUE; + } + + return FALSE; + } + + inline BOOL CDocker::IsUndocked() const + { + return (!((m_DockStyle&0xF)|| (m_DockStyle & DS_DOCKED_CONTAINER)) && !m_Undocking); // Boolean expression + } + + inline BOOL CDocker::LoadRegistrySettings(tString tsRegistryKeyName) + // Recreates the docker layout based on information stored in the registry. + // Assumes the DockAncestor window is already created. + { + BOOL bResult = FALSE; + + if (0 != tsRegistryKeyName.size()) + { + std::vector<DockInfo> vDockList; + std::vector<int> vActiveContainers; + tString tsKey = _T("Software\\") + tsRegistryKeyName + _T("\\Dock Windows"); + HKEY hKey = 0; + RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey); + if (hKey) + { + DWORD dwType = REG_BINARY; + DWORD BufferSize = sizeof(DockInfo); + DockInfo di; + int i = 0; + TCHAR szNumber[20]; + tString tsSubKey = _T("DockChild"); + tsSubKey += _itot(i, szNumber, 10); + + // Fill the DockList vector from the registry + while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&di, &BufferSize)) + { + vDockList.push_back(di); + i++; + tsSubKey = _T("DockChild"); + tsSubKey += _itot(i, szNumber, 10); + } + + dwType = REG_DWORD; + BufferSize = sizeof(int); + int nID; + i = 0; + tsSubKey = _T("ActiveContainer"); + tsSubKey += _itot(i, szNumber, 10); + // Fill the DockList vector from the registry + while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&nID, &BufferSize)) + { + vActiveContainers.push_back(nID); + i++; + tsSubKey = _T("ActiveContainer"); + tsSubKey += _itot(i, szNumber, 10); + } + + RegCloseKey(hKey); + if (vDockList.size() > 0) bResult = TRUE; + } + + // Add dockers without parents first + std::vector<DockInfo>::iterator iter; + for (iter = vDockList.begin(); iter < vDockList.end() ; ++iter) + { + DockInfo di = (*iter); + if (di.DockParentID == 0) + { + CDocker* pDocker = NewDockerFromID(di.DockID); + if (pDocker) + { + if (di.DockStyle & 0xF) + AddDockedChild(pDocker, di.DockStyle, di.DockSize, di.DockID); + else + AddUndockedChild(pDocker, di.DockStyle, di.DockSize, di.Rect, di.DockID); + } + else + { + TRACE(_T("Failed to add dockers without parents from registry")); + bResult = FALSE; + } + } + } + + // Remove dockers without parents from vDockList + for (UINT n = (UINT)vDockList.size(); n > 0; --n) + { + iter = vDockList.begin() + n-1; + if ((*iter).DockParentID == 0) + vDockList.erase(iter); + } + + // Add remaining dockers + while (vDockList.size() > 0) + { + bool bFound = false; + std::vector<DockInfo>::iterator iter; + for (iter = vDockList.begin(); iter < vDockList.end(); ++iter) + { + DockInfo di = *iter; + CDocker* pDockParent = GetDockFromID(di.DockParentID); + + if (pDockParent != 0) + { + CDocker* pDock = NewDockerFromID(di.DockID); + if(pDock) + { + pDockParent->AddDockedChild(pDock, di.DockStyle, di.DockSize, di.DockID); + bFound = true; + } + else + { + TRACE(_T("Failed to add dockers with parents from registry")); + bResult = FALSE; + } + + vDockList.erase(iter); + break; + } + } + + if (!bFound) + { + TRACE(_T("Orphaned dockers stored in registry ")); + bResult = FALSE; + break; + } + } + + std::vector<int>::iterator iterID; + for (iterID = vActiveContainers.begin(); iterID < vActiveContainers.end(); ++iterID) + { + CDocker* pDocker = GetDockFromID(*iterID); + if (pDocker) + { + CDockContainer* pContainer = pDocker->GetContainer(); + if (pContainer) + { + int nPage = pContainer->GetContainerIndex(pContainer); + if (nPage >= 0) + pContainer->SelectPage(nPage); + } + } + } + } + + if (!bResult) CloseAllDockers(); + return bResult; + } + + inline void CDocker::MoveDockChildren(CDocker* pDockTarget) + // Used internally by Dock and Undock + { + assert(pDockTarget); + + // Transfer any dock children from the current docker to the target docker + std::vector<CDocker*>::iterator iter; + for (iter = GetDockChildren().begin(); iter < GetDockChildren().end(); ++iter) + { + pDockTarget->GetDockChildren().push_back(*iter); + (*iter)->m_pDockParent = pDockTarget; + (*iter)->SetParent(pDockTarget); + (*iter)->GetDockBar().SetParent(pDockTarget); + } + GetDockChildren().clear(); + } + + inline CDocker* CDocker::NewDockerFromID(int nID) + // Used in LoadRegistrySettings. Creates a new Docker from the specified ID + { + UNREFERENCED_PARAMETER(nID); + + // Override this function to create the Docker objects as shown below + + CDocker* pDock = NULL; + /* switch(nID) + { + case ID_CLASSES: + pDock = new CDockClasses; + break; + case ID_FILES: + pDock = new CDockFiles; + break; + default: + TRACE(_T("Unknown Dock ID\n")); + break; + } */ + + return pDock; + } + + inline void CDocker::OnActivate(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + + // Only top level undocked dockers get this message + if (LOWORD(wParam) == WA_INACTIVE) + { + GetTopmostDocker()->m_hOldFocus = ::GetFocus(); + + // Send a notification of focus lost + int idCtrl = ::GetDlgCtrlID(m_hOldFocus); + NMHDR nhdr={0}; + nhdr.hwndFrom = m_hOldFocus; + nhdr.idFrom = idCtrl; + nhdr.code = UWM_FRAMELOSTFOCUS; + SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr); + } + } + + inline void CDocker::OnCaptionTimer(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + + if (this == GetDockAncestor()) + { + if (wParam == 1) + { + DrawAllCaptions(); + m_nTimerCount++; + if (m_nTimerCount == 10) + { + KillTimer(wParam); + m_nTimerCount = 0; + } + } + } + } + + inline void CDocker::OnCreate() + { + +#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500) + if (GetParent()->GetWindowLongPtr(GWL_EXSTYLE) & WS_EX_LAYOUTRTL) + { + DWORD dwExStyle = GetWindowLongPtr(GWL_EXSTYLE); + SetWindowLongPtr(GWL_EXSTYLE, dwExStyle | WS_EX_LAYOUTRTL); + } +#endif + + // Create the various child windows + GetDockClient().SetDock(this); + GetDockClient().Create(this); + + assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window + GetView()->Create(&GetDockClient()); + + // Create the slider bar belonging to this docker + GetDockBar().SetDock(this); + if (GetDockAncestor() != this) + GetDockBar().Create(GetParent()); + + // Now remove the WS_POPUP style. It was required to allow this window + // to be owned by the frame window. + SetWindowLongPtr(GWL_STYLE, WS_CHILD); + SetParent(GetParent()); // Reinstate the window's parent + + // Set the default colour for the splitter bar + COLORREF rgbColour = GetSysColor(COLOR_BTNFACE); + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + ReBarTheme* pTheme = (ReBarTheme*)pFrame->SendMessage(UWM_GETREBARTHEME, 0, 0); + + if (pTheme && pTheme->UseThemes && pTheme->clrBkgnd2 != 0) + rgbColour =pTheme->clrBkgnd2; + + SetBarColor(rgbColour); + + // Set the caption height based on text height + m_NCHeight = MAX(20, GetTextHeight() + 5); + } + + inline void CDocker::OnDestroy(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + // Destroy any dock children first + std::vector<CDocker*>::iterator iter; + for (iter = GetDockChildren().begin(); iter < GetDockChildren().end(); ++iter) + { + (*iter)->Destroy(); + } + + if (dynamic_cast<CDockContainer*>(GetView()) && IsUndocked()) + { + CDockContainer* pContainer = (CDockContainer*)GetView(); + if (pContainer->GetAllContainers().size() > 1) + { + // This container has children, so destroy them now + std::vector<ContainerInfo> AllContainers = pContainer->GetAllContainers(); + std::vector<ContainerInfo>::iterator iter; + for (iter = AllContainers.begin(); iter < AllContainers.end(); ++iter) + { + if ((*iter).pContainer != pContainer) + { + // Reset container parent before destroying the dock window + CDocker* pDock = GetDockFromView((*iter).pContainer); + if (pContainer->IsWindow()) + pContainer->SetParent(&pDock->GetDockClient()); + + pDock->Destroy(); + } + } + } + } + + GetDockBar().Destroy(); + + // Post a destroy docker message + if ( GetDockAncestor()->IsWindow() ) + GetDockAncestor()->PostMessage(UWM_DOCK_DESTROYED, (WPARAM)this, 0L); + } + + inline void CDocker::OnDockDestroyed(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + + CDocker* pDock = (CDocker*)wParam; + + assert( this == GetDockAncestor() ); + std::vector<DockPtr>::iterator iter; + for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter) + { + if ((*iter).get() == pDock) + { + GetAllDockers().erase(iter); + break; + } + } + } + + inline void CDocker::OnExitSizeMove(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + m_BlockMove = FALSE; + m_bIsDragging = FALSE; + SendNotify(UWM_DOCK_END); + } + + inline LRESULT CDocker::OnNotify(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + LPDRAGPOS pdp = (LPDRAGPOS)lParam; + + switch (((LPNMHDR)lParam)->code) + { + case UWM_DOCK_START: + { + if (IsDocked()) + { + Undock(GetCursorPos()); + SendMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(pdp->ptPos.x, pdp->ptPos.y)); + } + } + break; + + case UWM_DOCK_MOVE: + { + CheckAllTargets((LPDRAGPOS)lParam); + } + break; + + case UWM_DOCK_END: + { + CDocker* pDock = (CDocker*)FromHandle(pdp->hdr.hwndFrom); + if (NULL == pDock) break; + + UINT DockZone = pdp->DockZone; + CRect rc = pDock->GetWindowRect(); + + switch(DockZone) + { + case DS_DOCKED_LEFT: + case DS_DOCKED_RIGHT: + pDock->SetDockSize(rc.Width()); + Dock(pDock, pDock->GetDockStyle() | DockZone); + break; + case DS_DOCKED_TOP: + case DS_DOCKED_BOTTOM: + pDock->SetDockSize(rc.Height()); + Dock(pDock, pDock->GetDockStyle() | DockZone); + break; + case DS_DOCKED_CONTAINER: + { + DockInContainer(pDock, pDock->GetDockStyle() | DockZone); + CDockContainer* pContainer = (CDockContainer*)GetView(); + int nPage = pContainer->GetContainerIndex((CDockContainer*)pDock->GetView()); + pContainer->SelectPage(nPage); + } + break; + case DS_DOCKED_LEFTMOST: + case DS_DOCKED_RIGHTMOST: + pDock->SetDockSize(rc.Width()); + DockOuter(pDock, pDock->GetDockStyle() | DockZone); + break; + case DS_DOCKED_TOPMOST: + case DS_DOCKED_BOTTOMMOST: + pDock->SetDockSize(rc.Height()); + DockOuter(pDock, pDock->GetDockStyle() | DockZone); + break; + } + + GetDockHint().Destroy(); + CloseAllTargets(); + } + break; + + case UWM_BAR_START: + { + CPoint pt = pdp->ptPos; + ScreenToClient(pt); + if (!IsDragAutoResize()) + DrawHashBar(pdp->hdr.hwndFrom, pt); + m_OldPoint = pt; + } + break; + + case UWM_BAR_MOVE: + { + CPoint pt = pdp->ptPos; + ScreenToClient(pt); + + if (pt != m_OldPoint) + { + if (IsDragAutoResize()) + ResizeDockers(pdp); + else + { + DrawHashBar(pdp->hdr.hwndFrom, m_OldPoint); + DrawHashBar(pdp->hdr.hwndFrom, pt); + } + + m_OldPoint = pt; + } + } + break; + + case UWM_BAR_END: + { + POINT pt = pdp->ptPos; + ScreenToClient(pt); + + if (!IsDragAutoResize()) + DrawHashBar(pdp->hdr.hwndFrom, pt); + + ResizeDockers(pdp); + } + break; + case NM_SETFOCUS: + if (GetDockAncestor()->IsWindow()) + GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0); + break; + case UWM_FRAMEGOTFOCUS: + if (GetDockAncestor()->IsWindow()) + GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0); + if (GetView()->IsWindow()) + GetView()->SendMessage(WM_NOTIFY, wParam, lParam); + break; + case UWM_FRAMELOSTFOCUS: + if (GetDockAncestor()->IsWindow()) + GetDockAncestor()->PostMessage(UWM_DOCK_ACTIVATED, 0, 0); + if (GetView()->IsWindow()) + GetView()->SendMessage(WM_NOTIFY, wParam, lParam); + break; + } + return 0L; + } + + inline void CDocker::ResizeDockers(LPDRAGPOS pdp) + // Called when the docker's splitter bar is dragged + { + assert(pdp); + + POINT pt = pdp->ptPos; + ScreenToClient(pt); + + CDocker* pDock = ((CDockBar*)FromHandle(pdp->hdr.hwndFrom))->GetDock(); + if (NULL == pDock) return; + + RECT rcDock = pDock->GetWindowRect(); + ScreenToClient(rcDock); + + double dBarWidth = pDock->GetDockBar().GetWidth(); + int iBarWidth = pDock->GetDockBar().GetWidth(); + int DockSize; + + switch (pDock->GetDockStyle() & 0xF) + { + case DS_DOCKED_LEFT: + DockSize = MAX(pt.x, iBarWidth/2) - rcDock.left - (int)(.5* dBarWidth); + DockSize = MAX(-iBarWidth, DockSize); + pDock->SetDockSize(DockSize); + pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Width()); + break; + case DS_DOCKED_RIGHT: + DockSize = rcDock.right - MAX(pt.x, iBarWidth/2) - (int)(.5* dBarWidth); + DockSize = MAX(-iBarWidth, DockSize); + pDock->SetDockSize(DockSize); + pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Width()); + break; + case DS_DOCKED_TOP: + DockSize = MAX(pt.y, iBarWidth/2) - rcDock.top - (int)(.5* dBarWidth); + DockSize = MAX(-iBarWidth, DockSize); + pDock->SetDockSize(DockSize); + pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Height()); + break; + case DS_DOCKED_BOTTOM: + DockSize = rcDock.bottom - MAX(pt.y, iBarWidth/2) - (int)(.5* dBarWidth); + DockSize = MAX(-iBarWidth, DockSize); + pDock->SetDockSize(DockSize); + pDock->m_DockSizeRatio = ((double)pDock->m_DockStartSize)/((double)pDock->m_pDockParent->GetWindowRect().Height()); + break; + } + + RecalcDockLayout(); + } + + inline void CDocker::OnSetFocus(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if (IsUndocked() && m_hOldFocus) + ::SetFocus(m_hOldFocus); + else + // Pass focus on the the view window + GetView()->SetFocus(); + + if ((this == GetTopmostDocker()) && (this != GetDockAncestor())) + { + // Send a notification to top level window + int idCtrl = ::GetDlgCtrlID(m_hOldFocus); + NMHDR nhdr={0}; + nhdr.hwndFrom = m_hOldFocus; + nhdr.idFrom = idCtrl; + nhdr.code = NM_SETFOCUS; + SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr); + } + } + + inline void CDocker::OnSysColorChange(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + if (this == GetDockAncestor()) + { + COLORREF rgbColour = GetSysColor(COLOR_BTNFACE); + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + ReBarTheme* pTheme = (ReBarTheme*)pFrame->SendMessage(UWM_GETREBARTHEME, 0, 0); + + if (pTheme && pTheme->UseThemes && pTheme->clrBand2 != 0) + rgbColour = pTheme->clrBkgnd2; + else + rgbColour = GetSysColor(COLOR_BTNFACE); + + // Set the splitter bar colour for each docker decendant + std::vector<DockPtr>::iterator iter; + for (iter = GetAllDockers().begin(); iter < GetAllDockers().end(); ++iter) + (*iter)->SetBarColor(rgbColour); + + // Set the splitter bar colour for the docker ancestor + SetBarColor(rgbColour); + } + } + + inline LRESULT CDocker::OnSysCommand(WPARAM wParam, LPARAM lParam) + { + switch(wParam&0xFFF0) + { + case SC_MOVE: + // An undocked docker is being moved + { + BOOL bResult = FALSE; + m_bIsDragging = TRUE; + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + if (SystemParametersInfo(SPI_GETDRAGFULLWINDOWS, 0, &bResult, 0)) + { + // Turn on DragFullWindows for this move + SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, TRUE, 0, 0); + + // Process this message + DefWindowProc(WM_SYSCOMMAND, wParam, lParam); + + // Return DragFullWindows to its previous state + SystemParametersInfo(SPI_SETDRAGFULLWINDOWS, bResult, 0, 0); + return 0L; + } + } + break; + case SC_CLOSE: + // The close button is pressed on an undocked docker + m_bIsClosing = TRUE; + break; + } + return CWnd::WndProcDefault(WM_SYSCOMMAND, wParam, lParam); + } + + inline LRESULT CDocker::OnWindowPosChanging(WPARAM wParam, LPARAM lParam) + { + // Suspend dock drag moving while over dock zone + if (m_BlockMove) + { + LPWINDOWPOS pWndPos = (LPWINDOWPOS)lParam; + pWndPos->flags |= SWP_NOMOVE|SWP_FRAMECHANGED; + return 0; + } + + return CWnd::WndProcDefault(WM_WINDOWPOSCHANGING, wParam, lParam); + } + + inline void CDocker::OnWindowPosChanged(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + if (m_bIsDragging) + { + // Send a Move notification to the parent + if ( IsLeftButtonDown() ) + { + LPWINDOWPOS wPos = (LPWINDOWPOS)lParam; + if ((!(wPos->flags & SWP_NOMOVE)) || m_BlockMove) + SendNotify(UWM_DOCK_MOVE); + } + else + { + CloseAllTargets(); + m_BlockMove = FALSE; + } + } + else if (this == GetTopmostDocker()) + { + // Reposition the dock children + if (IsUndocked() && IsWindowVisible() && !m_bIsClosing) RecalcDockLayout(); + } + } + + inline void CDocker::PreCreate(CREATESTRUCT &cs) + { + // Specify the WS_POPUP style to have this window owned + if (this != GetDockAncestor()) + cs.style = WS_POPUP; + + cs.dwExStyle = WS_EX_TOOLWINDOW; + } + + inline void CDocker::PreRegisterClass(WNDCLASS &wc) + { + wc.lpszClassName = _T("Win32++ Docker"); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + } + + inline void CDocker::RecalcDockChildLayout(CRect rc) + { + // This function positions the Docker's dock children, the Dockers client area + // and draws the dockbar bars. + + // Notes: + // 1) This function is called recursively. + // 2) The client area and child dockers are positioned simultaneously with + // DeferWindowPos to avoid drawing errors in complex docker arrangements. + // 3) The docker's client area contains the docker's caption (if any) and the docker's view window. + + // Note: All top level dockers are undocked, including the dock ancestor. + if (IsDocked()) + { + rc.OffsetRect(-rc.left, -rc.top); + } + + HDWP hdwp = BeginDeferWindowPos((int)m_vDockChildren.size() +2); + + // Step 1: Calculate the position of each Docker child, DockBar, and Client window. + // The Client area = the docker rect minus the area of dock children and the dock bar (splitter bar). + for (UINT u = 0; u < m_vDockChildren.size(); ++u) + { + CRect rcChild = rc; + double DockSize = m_vDockChildren[u]->m_DockStartSize;; + + // Calculate the size of the Docker children + switch (m_vDockChildren[u]->GetDockStyle() & 0xF) + { + case DS_DOCKED_LEFT: + if (!(GetDockStyle() & DS_FIXED_RESIZE)) + DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Width()), rcChild.Width()); + rcChild.right = rcChild.left + (int)DockSize; + break; + case DS_DOCKED_RIGHT: + if (!(GetDockStyle() & DS_FIXED_RESIZE)) + DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Width()), rcChild.Width()); + rcChild.left = rcChild.right - (int)DockSize; + break; + case DS_DOCKED_TOP: + if (!(GetDockStyle() & DS_FIXED_RESIZE)) + DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Height()), rcChild.Height()); + rcChild.bottom = rcChild.top + (int)DockSize; + break; + case DS_DOCKED_BOTTOM: + if (!(GetDockStyle() & DS_FIXED_RESIZE)) + DockSize = MIN(m_vDockChildren[u]->m_DockSizeRatio*(GetWindowRect().Height()), rcChild.Height()); + rcChild.top = rcChild.bottom - (int)DockSize; + break; + } + + if (m_vDockChildren[u]->IsDocked()) + { + // Position this docker's children + hdwp = m_vDockChildren[u]->DeferWindowPos(hdwp, NULL, rcChild, SWP_SHOWWINDOW|SWP_FRAMECHANGED); + m_vDockChildren[u]->m_rcChild = rcChild; + + rc.SubtractRect(rc, rcChild); + + // Calculate the dimensions of the splitter bar + CRect rcBar = rc; + DWORD DockSide = m_vDockChildren[u]->GetDockStyle() & 0xF; + + if (DS_DOCKED_LEFT == DockSide) rcBar.right = rcBar.left + m_vDockChildren[u]->GetBarWidth(); + if (DS_DOCKED_RIGHT == DockSide) rcBar.left = rcBar.right - m_vDockChildren[u]->GetBarWidth(); + if (DS_DOCKED_TOP == DockSide) rcBar.bottom = rcBar.top + m_vDockChildren[u]->GetBarWidth(); + if (DS_DOCKED_BOTTOM == DockSide) rcBar.top = rcBar.bottom - m_vDockChildren[u]->GetBarWidth(); + + // Save the splitter bar position. We will reposition it later. + m_vDockChildren[u]->m_rcBar = rcBar; + rc.SubtractRect(rc, rcBar); + } + } + + // Step 2: Position the Dock client and dock bar + hdwp = GetDockClient().DeferWindowPos(hdwp, NULL, rc, SWP_SHOWWINDOW |SWP_FRAMECHANGED); + EndDeferWindowPos(hdwp); + + // Position the dockbar. Only docked dockers have a dock bar. + if (IsDocked()) + { + // The SWP_NOCOPYBITS forces a redraw of the dock bar. + GetDockBar().SetWindowPos(NULL, m_rcBar, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOCOPYBITS ); + } + + // Step 3: Now recurse through the docker's children. They might have children of their own. + for (UINT v = 0; v < m_vDockChildren.size(); ++v) + { + m_vDockChildren[v]->RecalcDockChildLayout(m_vDockChildren[v]->m_rcChild); + } + } + + inline void CDocker::RecalcDockLayout() + // Repositions the dock children of a top level docker + { + if (GetDockAncestor()->IsWindow()) + { + CRect rc = GetTopmostDocker()->GetClientRect(); + GetTopmostDocker()->RecalcDockChildLayout(rc); + GetTopmostDocker()->UpdateWindow(); + } + } + + inline std::vector<CDocker*> CDocker::SortDockers() + // Returns a vector of sorted dockers, used by SaveRegistrySettings. + { + std::vector<CDocker*> vSorted; + std::vector<CDocker*>::iterator itSort; + std::vector<DockPtr>::iterator itAll; + + // Add undocked top level dockers + for (itAll = GetAllDockers().begin(); itAll < GetAllDockers().end(); ++itAll) + { + if (!(*itAll)->GetDockParent()) + vSorted.push_back((*itAll).get()); + } + + // Add dock ancestor's children + vSorted.insert(vSorted.end(), GetDockAncestor()->GetDockChildren().begin(), GetDockAncestor()->GetDockChildren().end()); + + // Add other dock children + int index = 0; + itSort = vSorted.begin(); + while (itSort < vSorted.end()) + { + vSorted.insert(vSorted.end(), (*itSort)->GetDockChildren().begin(), (*itSort)->GetDockChildren().end()); + itSort = vSorted.begin() + (++index); + } + + // Add dockers docked in containers + std::vector<CDocker*> vDockContainers; + for (itSort = vSorted.begin(); itSort< vSorted.end(); ++itSort) + { + if ((*itSort)->GetContainer()) + vDockContainers.push_back(*itSort); + } + + for (itSort = vDockContainers.begin(); itSort < vDockContainers.end(); ++itSort) + { + CDockContainer* pContainer = (*itSort)->GetContainer(); + + for (UINT i = 1; i < pContainer->GetAllContainers().size(); ++i) + { + CDockContainer* pChild = pContainer->GetContainerFromIndex(i); + CDocker* pDock = GetDockFromView(pChild); + vSorted.push_back(pDock); + } + } + + return vSorted; + } + + inline BOOL CDocker::SaveRegistrySettings(tString tsRegistryKeyName) + // Stores the docking configuration in the registry + // NOTE: This function assumes that each docker has a unique DockID + { + assert(VerifyDockers()); + + std::vector<CDocker*> vSorted = SortDockers(); + std::vector<CDocker*>::iterator iter; + std::vector<DockInfo> vDockInfo; + + if (0 != tsRegistryKeyName.size()) + { + // Fill the DockInfo vector with the docking information + for (iter = vSorted.begin(); iter < vSorted.end(); ++iter) + { + DockInfo di = {0}; + di.DockID = (*iter)->GetDockID(); + di.DockStyle = (*iter)->GetDockStyle(); + di.DockSize = (*iter)->GetDockSize(); + di.Rect = (*iter)->GetWindowRect(); + if ((*iter)->GetDockParent()) + di.DockParentID = (*iter)->GetDockParent()->GetDockID(); + + vDockInfo.push_back(di); + } + + tString tsKeyName = _T("Software\\") + tsRegistryKeyName; + HKEY hKey = NULL; + HKEY hKeyDock = NULL; + + try + { + if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) + throw (CWinException(_T("RegCreateKeyEx Failed"))); + + RegDeleteKey(hKey, _T("Dock Windows")); + if (ERROR_SUCCESS != RegCreateKeyEx(hKey, _T("Dock Windows"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyDock, NULL)) + throw (CWinException(_T("RegCreateKeyEx Failed"))); + + // Add the Dock windows information to the registry + for (UINT u = 0; u < vDockInfo.size(); ++u) + { + DockInfo di = vDockInfo[u]; + TCHAR szNumber[16]; + tString tsSubKey = _T("DockChild"); + tsSubKey += _itot((int)u, szNumber, 10); + if(ERROR_SUCCESS != RegSetValueEx(hKeyDock, tsSubKey.c_str(), 0, REG_BINARY, (LPBYTE)&di, sizeof(DockInfo))) + throw (CWinException(_T("RegSetValueEx failed"))); + } + + // Add Active Container to the registry + int i = 0; + for (iter = vSorted.begin(); iter < vSorted.end(); ++iter) + { + CDockContainer* pContainer = (*iter)->GetContainer(); + + if (pContainer && (pContainer == pContainer->GetActiveContainer())) + { + TCHAR szNumber[20]; + tString tsSubKey = _T("ActiveContainer"); + tsSubKey += _itot(i++, szNumber, 10); + int nID = GetDockFromView(pContainer)->GetDockID(); + if(ERROR_SUCCESS != RegSetValueEx(hKeyDock, tsSubKey.c_str(), 0, REG_DWORD, (LPBYTE)&nID, sizeof(int))) + throw (CWinException(_T("RegSetValueEx failed"))); + } + } + + RegCloseKey(hKeyDock); + RegCloseKey(hKey); + } + + catch (const CWinException& e) + { + // Roll back the registry changes by deleting the subkeys + if (hKey) + { + if (hKeyDock) + { + RegDeleteKey(hKeyDock, _T("Dock Windows")); + RegCloseKey(hKeyDock); + } + + RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str()); + RegCloseKey(hKey); + } + + e.what(); + return FALSE; + } + } + + return TRUE; + } + + inline void CDocker::SendNotify(UINT nMessageID) + // Sends a docking notification to the docker below the cursor + { + DRAGPOS DragPos; + DragPos.hdr.code = nMessageID; + DragPos.hdr.hwndFrom = m_hWnd; + DragPos.ptPos = GetCursorPos(); + DragPos.DockZone = m_dwDockZone; + m_dwDockZone = 0; + + CDocker* pDock = GetDockFromPoint(DragPos.ptPos); + + if (pDock) + pDock->SendMessage(WM_NOTIFY, 0L, (LPARAM)&DragPos); + else + { + if (GetDockHint().IsWindow()) GetDockHint().Destroy(); + CloseAllTargets(); + m_BlockMove = FALSE; + } + } + + inline void CDocker::SetDockStyle(DWORD dwDockStyle) + { + if (IsWindow()) + { + if ((dwDockStyle & DS_CLIENTEDGE) != (m_DockStyle & DS_CLIENTEDGE)) + { + if (dwDockStyle & DS_CLIENTEDGE) + { + DWORD dwExStyle = (DWORD)GetDockClient().GetWindowLongPtr(GWL_EXSTYLE)|WS_EX_CLIENTEDGE; + GetDockClient().SetWindowLongPtr(GWL_EXSTYLE, dwExStyle); + GetDockClient().RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_FRAME); + } + else + { + DWORD dwExStyle = (DWORD)GetDockClient().GetWindowLongPtr(GWL_EXSTYLE); + dwExStyle &= ~WS_EX_CLIENTEDGE; + GetDockClient().SetWindowLongPtr(GWL_EXSTYLE, dwExStyle); + GetDockClient().RedrawWindow(0, 0, RDW_INVALIDATE|RDW_UPDATENOW|RDW_ERASE|RDW_FRAME); + } + } + + RecalcDockLayout(); + } + + m_DockStyle = dwDockStyle; + } + + inline void CDocker::SetCaption(LPCTSTR szCaption) + // Sets the caption text + { + GetDockClient().SetCaption(szCaption); + + if (IsWindow()) + SetWindowText(szCaption); + } + + inline void CDocker::SetCaptionColors(COLORREF Foregnd1, COLORREF Backgnd1, COLORREF ForeGnd2, COLORREF BackGnd2) + { + GetDockClient().SetCaptionColors(Foregnd1, Backgnd1, ForeGnd2, BackGnd2); + } + + inline void CDocker::SetCaptionHeight(int nHeight) + // Sets the height of the caption + { + m_NCHeight = nHeight; + RedrawWindow(); + RecalcDockLayout(); + } + + inline void CDocker::SetDockSize(int DockSize) + // Sets the size of a docked docker + { + if (IsDocked()) + { + assert (m_pDockParent); + switch (GetDockStyle() & 0xF) + { + case DS_DOCKED_LEFT: + m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Width()); + m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Width()); + break; + case DS_DOCKED_RIGHT: + m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Width()); + m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Width()); + break; + case DS_DOCKED_TOP: + m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Height()); + m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Height()); + break; + case DS_DOCKED_BOTTOM: + m_DockStartSize = MIN(DockSize,m_pDockParent->GetWindowRect().Height()); + m_DockSizeRatio = ((double)m_DockStartSize)/((double)m_pDockParent->GetWindowRect().Height()); + break; + } + + RecalcDockLayout(); + } + else + { + m_DockStartSize = DockSize; + m_DockSizeRatio = 1.0; + } + } + + inline void CDocker::SetDragAutoResize(BOOL bAutoResize) + { + m_bDragAutoResize = bAutoResize; + } + + inline void CDocker::SetView(CWnd& wndView) + // Assigns the view window to the docker + { + CWnd* pWnd = &wndView; + GetDockClient().SetView(wndView); + if (dynamic_cast<CDockContainer*>(pWnd)) + { + CDockContainer* pContainer = (CDockContainer*)&wndView; + SetCaption(pContainer->GetDockCaption().c_str()); + } + } + + inline void CDocker::PromoteFirstChild() + // One of the steps required for undocking + { + // Promote our first child to replace ourself + if (m_pDockParent) + { + for (UINT u = 0 ; u < m_pDockParent->m_vDockChildren.size(); ++u) + { + if (m_pDockParent->m_vDockChildren[u] == this) + { + if (m_vDockChildren.size() > 0) + // swap our first child for ourself as a child of the parent + m_pDockParent->m_vDockChildren[u] = m_vDockChildren[0]; + else + // remove ourself as a child of the parent + m_pDockParent->m_vDockChildren.erase(m_pDockParent->m_vDockChildren.begin() + u); + break; + } + } + } + + // Transfer styles and data and children to the child docker + CDocker* pDockFirstChild = NULL; + if (m_vDockChildren.size() > 0) + { + pDockFirstChild = m_vDockChildren[0]; + pDockFirstChild->m_DockStyle = (pDockFirstChild->m_DockStyle & 0xFFFFFFF0 ) | (m_DockStyle & 0xF); + pDockFirstChild->m_DockStartSize = m_DockStartSize; + pDockFirstChild->m_DockSizeRatio = m_DockSizeRatio; + + if (m_pDockParent) + { + pDockFirstChild->m_pDockParent = m_pDockParent; + pDockFirstChild->SetParent(m_pDockParent); + pDockFirstChild->GetDockBar().SetParent(m_pDockParent); + } + else + { + std::vector<CDocker*>::iterator iter; + for (iter = GetDockChildren().begin() + 1; iter < GetDockChildren().end(); ++iter) + (*iter)->ShowWindow(SW_HIDE); + + pDockFirstChild->ConvertToPopup(GetWindowRect()); + pDockFirstChild->GetDockBar().ShowWindow(SW_HIDE); + } + + m_vDockChildren.erase(m_vDockChildren.begin()); + MoveDockChildren(pDockFirstChild); + } + } + + inline void CDocker::ConvertToChild(HWND hWndParent) + { + DWORD dwStyle = WS_CHILD | WS_VISIBLE; + SetWindowLongPtr(GWL_STYLE, dwStyle); + SetParent(FromHandle(hWndParent)); + GetDockBar().SetParent(FromHandle(hWndParent)); + } + + inline void CDocker::ConvertToPopup(RECT rc) + { + // Change the window to an "undocked" style + ShowWindow(SW_HIDE); + DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME; + SetWindowLongPtr(GWL_STYLE, dwStyle); + + // Change the window's parent and reposition it + GetDockBar().ShowWindow(SW_HIDE); + SetWindowPos(0, 0, 0, 0, 0, SWP_NOSENDCHANGING|SWP_HIDEWINDOW|SWP_NOREDRAW); + m_pDockParent = 0; + SetParent(0); + SetWindowPos(NULL, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED|SWP_NOOWNERZORDER); + GetDockClient().SetWindowPos(NULL, GetClientRect(), SWP_SHOWWINDOW); + + SetWindowText(GetCaption().c_str()); + } + + inline CDocker* CDocker::SeparateFromDock() + { + // This performs some of the tasks required for undocking. + // It is also used when a docker is hidden. + CDocker* pDockUndockedFrom = GetDockParent(); + if (!pDockUndockedFrom && (GetDockChildren().size() > 0)) + pDockUndockedFrom = GetDockChildren()[0]; + + GetTopmostDocker()->m_hOldFocus = 0; + PromoteFirstChild(); + m_pDockParent = 0; + + GetDockBar().ShowWindow(SW_HIDE); + m_DockStyle = m_DockStyle & 0xFFFFFFF0; + m_DockStyle &= ~DS_DOCKED_CONTAINER; + + return pDockUndockedFrom; + } + + inline void CDocker::SetUndockPosition(CPoint pt) + { + m_Undocking = TRUE; + CRect rc; + rc = GetDockClient().GetWindowRect(); + CRect rcTest = rc; + rcTest.bottom = MIN(rcTest.bottom, rcTest.top + m_NCHeight); + if ( !rcTest.PtInRect(pt)) + rc.SetRect(pt.x - rc.Width()/2, pt.y - m_NCHeight/2, pt.x + rc.Width()/2, pt.y - m_NCHeight/2 + rc.Height()); + + ConvertToPopup(rc); + m_Undocking = FALSE; + + // Send the undock notification to the frame + NMHDR nmhdr = {0}; + nmhdr.hwndFrom = m_hWnd; + nmhdr.code = UWM_UNDOCKED; + nmhdr.idFrom = m_nDockID; + CWnd* pFrame = GetDockAncestor()->GetAncestor(); + pFrame->SendMessage(WM_NOTIFY, m_nDockID, (LPARAM)&nmhdr); + + // Initiate the window move + SetCursorPos(pt.x, pt.y); + ScreenToClient(pt); + PostMessage(WM_SYSCOMMAND, (WPARAM)(SC_MOVE|0x0002), MAKELPARAM(pt.x, pt.y)); + } + + inline void CDocker::Undock(CPoint pt, BOOL bShowUndocked) + { + // Return if we shouldn't undock + if (GetDockStyle() & DS_NO_UNDOCK) return; + + // Undocking isn't supported on Win95 + if (1400 == GetWinVersion()) return; + + CDocker* pDockUndockedFrom = SeparateFromDock(); + + // Position and draw the undocked window, unless it is about to be closed + if (bShowUndocked) + { + SetUndockPosition(pt); + } + + RecalcDockLayout(); + if ((pDockUndockedFrom) && (pDockUndockedFrom->GetTopmostDocker() != GetTopmostDocker())) + pDockUndockedFrom->RecalcDockLayout(); + } + + inline void CDocker::UndockContainer(CDockContainer* pContainer, CPoint pt, BOOL bShowUndocked) + { + assert(pContainer); + assert(this == GetDockFromView(pContainer->GetContainerParent())); + + // Return if we shouldn't undock + if (GetDockFromView(pContainer)->GetDockStyle() & DS_NO_UNDOCK) return; + + // Undocking isn't supported on Win95 + if (1400 == GetWinVersion()) return; + + GetTopmostDocker()->m_hOldFocus = 0; + CDocker* pDockUndockedFrom = GetDockFromView(pContainer->GetContainerParent()); + pDockUndockedFrom->ShowWindow(SW_HIDE); + if (GetView() == pContainer) + { + // The parent container is being undocked, so we need + // to transfer our child containers to a different docker + + // Choose a new docker from among the dockers for child containers + CDocker* pDockNew = 0; + CDocker* pDockOld = GetDockFromView(pContainer); + std::vector<ContainerInfo> AllContainers = pContainer->GetAllContainers(); + std::vector<ContainerInfo>::iterator iter = AllContainers.begin(); + while ((0 == pDockNew) && (iter < AllContainers.end())) + { + if ((*iter).pContainer != pContainer) + pDockNew = (CDocker*)FromHandle(::GetParent((*iter).pContainer->GetParent()->GetHwnd())); + + ++iter; + } + + if (pDockNew) + { + // Move containers from the old docker to the new docker + CDockContainer* pContainerNew = (CDockContainer*)pDockNew->GetView(); + for (iter = AllContainers.begin(); iter != AllContainers.end(); ++iter) + { + if ((*iter).pContainer != pContainer) + { + CDockContainer* pChildContainer = (CDockContainer*)(*iter).pContainer; + pContainer->RemoveContainer(pChildContainer); + if (pContainerNew != pChildContainer) + { + pContainerNew->AddContainer(pChildContainer); + CDocker* pDock = GetDockFromView(pChildContainer); + pDock->SetParent(pDockNew); + pDock->m_pDockParent = pDockNew; + } + } + } + + // Now transfer the old docker's settings to the new one. + pDockUndockedFrom = pDockNew; + pDockNew->m_DockStyle = pDockOld->m_DockStyle; + pDockNew->m_DockStartSize = pDockOld->m_DockStartSize; + pDockNew->m_DockSizeRatio = pDockOld->m_DockSizeRatio; + if (pDockOld->IsDocked()) + { + pDockNew->m_pDockParent = pDockOld->m_pDockParent; + pDockNew->SetParent(pDockOld->GetParent()); + } + else + { + CRect rc = pDockOld->GetWindowRect(); + pDockNew->ShowWindow(SW_HIDE); + DWORD dwStyle = WS_POPUP| WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_VISIBLE; + pDockNew->SetWindowLongPtr(GWL_STYLE, dwStyle); + pDockNew->m_pDockParent = 0; + pDockNew->SetParent(0); + pDockNew->SetWindowPos(NULL, rc, SWP_SHOWWINDOW|SWP_FRAMECHANGED| SWP_NOOWNERZORDER); + } + pDockNew->GetDockBar().SetParent(pDockOld->GetParent()); + pDockNew->GetView()->SetFocus(); + + // Transfer the Dock children to the new docker + pDockOld->MoveDockChildren(pDockNew); + + // insert pDockNew into its DockParent's DockChildren vector + if (pDockNew->m_pDockParent) + { + std::vector<CDocker*>::iterator p; + for (p = pDockNew->m_pDockParent->m_vDockChildren.begin(); p != pDockNew->m_pDockParent->m_vDockChildren.end(); ++p) + { + if (*p == this) + { + pDockNew->m_pDockParent->m_vDockChildren.insert(p, pDockNew); + break; + } + } + } + } + } + else + { + // This is a child container, so simply remove it from the parent + CDockContainer* pContainerParent = (CDockContainer*)GetView(); + pContainerParent->RemoveContainer(pContainer); + pContainerParent->SetTabSize(); + pContainerParent->SetFocus(); + pContainerParent->GetViewPage().SetParent(pContainerParent); + } + + // Finally do the actual undocking + CDocker* pDock = GetDockFromView(pContainer); + CRect rc = GetDockClient().GetWindowRect(); + ScreenToClient(rc); + pDock->GetDockClient().SetWindowPos(NULL, rc, SWP_SHOWWINDOW); + pDock->Undock(pt, bShowUndocked); + pDockUndockedFrom->ShowWindow(); + pDockUndockedFrom->RecalcDockLayout(); + pDock->BringWindowToTop(); + } + + inline LRESULT CDocker::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_ACTIVATE: + OnActivate(wParam, lParam); + break; + case WM_SYSCOMMAND: + return OnSysCommand(wParam, lParam); + + case WM_EXITSIZEMOVE: + OnExitSizeMove(wParam, lParam); + break; + case WM_WINDOWPOSCHANGING: + return OnWindowPosChanging(wParam, lParam); + + case WM_WINDOWPOSCHANGED: + OnWindowPosChanged(wParam, lParam); + break; + case WM_DESTROY: + OnDestroy(wParam, lParam); + break; + case WM_SETFOCUS: + OnSetFocus(wParam, lParam); + break; + case WM_TIMER: + OnCaptionTimer(wParam, lParam); + break; + case UWM_DOCK_DESTROYED: + OnDockDestroyed(wParam, lParam); + break; + case UWM_DOCK_ACTIVATED: + DrawAllCaptions(); + SetTimer(1, 100, NULL); + break; + case WM_SYSCOLORCHANGE: + OnSysColorChange(wParam, lParam); + break; + case WM_NCLBUTTONDBLCLK : + m_bIsDragging = FALSE; + break; + } + + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + + ////////////////////////////////////// + // Declaration of the CDockContainer class + inline CDockContainer::CDockContainer() : m_iCurrentPage(0), m_hTabIcon(0), m_nTabPressed(-1) + { + m_pContainerParent = this; + } + + inline CDockContainer::~CDockContainer() + { + if (m_hTabIcon) + DestroyIcon(m_hTabIcon); + } + + inline void CDockContainer::AddContainer(CDockContainer* pContainer) + { + assert(pContainer); + + if (this == m_pContainerParent) + { + ContainerInfo ci = {0}; + ci.pContainer = pContainer; + lstrcpy(ci.szTitle, pContainer->GetTabText()); + ci.iImage = ImageList_AddIcon(GetImageList(), pContainer->GetTabIcon()); + int iNewPage = (int)m_vContainerInfo.size(); + m_vContainerInfo.push_back(ci); + + if (m_hWnd) + { + TCITEM tie = {0}; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = ci.iImage; + tie.pszText = m_vContainerInfo[iNewPage].szTitle; + TabCtrl_InsertItem(m_hWnd, iNewPage, &tie); + + SetTabSize(); + } + + pContainer->m_pContainerParent = this; + if (pContainer->IsWindow()) + { + // Set the parent container relationships + pContainer->GetViewPage().SetParent(this); + pContainer->GetViewPage().ShowWindow(SW_HIDE); + } + } + } + + inline void CDockContainer::AddToolBarButton(UINT nID, BOOL bEnabled /* = TRUE */) + // Adds Resource IDs to toolbar buttons. + // A resource ID of 0 is a separator + { + GetToolBar().AddButton(nID, bEnabled); + } + + inline CDockContainer* CDockContainer::GetContainerFromIndex(UINT nPage) + { + CDockContainer* pContainer = NULL; + if (nPage < m_vContainerInfo.size()) + pContainer = (CDockContainer*)m_vContainerInfo[nPage].pContainer; + + return pContainer; + } + + inline CWnd* CDockContainer::GetActiveView() const + // Returns a pointer to the active view window, or NULL if there is no active veiw. + { + CWnd* pWnd = NULL; + if (m_pContainerParent->m_vContainerInfo.size() > 0) + { + CDockContainer* pActiveContainer = m_pContainerParent->m_vContainerInfo[m_pContainerParent->m_iCurrentPage].pContainer; + if (pActiveContainer->GetViewPage().GetView()->IsWindow()) + pWnd = pActiveContainer->GetViewPage().GetView(); + } + + return pWnd; + } + + inline CDockContainer* CDockContainer::GetContainerFromView(CWnd* pView) const + { + assert(pView); + + std::vector<ContainerInfo>::iterator iter; + CDockContainer* pViewContainer = 0; + for (iter = GetAllContainers().begin(); iter != GetAllContainers().end(); ++iter) + { + CDockContainer* pContainer = (*iter).pContainer; + if (pContainer->GetView() == pView) + pViewContainer = pContainer; + } + + return pViewContainer; + } + + inline int CDockContainer::GetContainerIndex(CDockContainer* pContainer) + { + assert(pContainer); + int iReturn = -1; + + for (int i = 0; i < (int)m_pContainerParent->m_vContainerInfo.size(); ++i) + { + if (m_pContainerParent->m_vContainerInfo[i].pContainer == pContainer) + iReturn = i; + } + + return iReturn; + } + + inline SIZE CDockContainer::GetMaxTabTextSize() + { + CSize Size; + + // Allocate an iterator for the ContainerInfo vector + std::vector<ContainerInfo>::iterator iter; + + for (iter = m_vContainerInfo.begin(); iter != m_vContainerInfo.end(); ++iter) + { + CSize TempSize; + CClientDC dc(this); + NONCLIENTMETRICS info = {0}; + info.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); + dc.CreateFontIndirect(&info.lfStatusFont); + TempSize = dc.GetTextExtentPoint32(iter->szTitle, lstrlen(iter->szTitle)); + if (TempSize.cx > Size.cx) + Size = TempSize; + } + + return Size; + } + + inline void CDockContainer::SetupToolBar() + { + // Use this function to set the Resource IDs for the toolbar(s). + +/* // Set the Resource IDs for the toolbar buttons + AddToolBarButton( IDM_FILE_NEW ); + AddToolBarButton( IDM_FILE_OPEN ); + AddToolBarButton( IDM_FILE_SAVE ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_EDIT_CUT ); + AddToolBarButton( IDM_EDIT_COPY ); + AddToolBarButton( IDM_EDIT_PASTE ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_FILE_PRINT ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_HELP_ABOUT ); +*/ + } + + inline void CDockContainer::OnCreate() + { + assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window + + ContainerInfo ci = {0}; + ci.pContainer = this; + lstrcpy(ci.szTitle, GetTabText()); + ci.iImage = ImageList_AddIcon(GetImageList(), GetTabIcon()); + m_vContainerInfo.push_back(ci); + + // Create the page window + GetViewPage().Create(this); + + // Create the toolbar + GetToolBar().Create(&GetViewPage()); + DWORD style = (DWORD)GetToolBar().GetWindowLongPtr(GWL_STYLE); + style |= CCS_NODIVIDER ; + GetToolBar().SetWindowLongPtr(GWL_STYLE, style); + SetupToolBar(); + if (GetToolBar().GetToolBarData().size() > 0) + { + // Set the toolbar images + // A mask of 192,192,192 is compatible with AddBitmap (for Win95) + if (!GetToolBar().SendMessage(TB_GETIMAGELIST, 0L, 0L)) + GetToolBar().SetImages(RGB(192,192,192), IDW_MAIN, 0, 0); + + GetToolBar().SendMessage(TB_AUTOSIZE, 0L, 0L); + } + else + GetToolBar().Destroy(); + + SetFixedWidth(TRUE); + SetOwnerDraw(TRUE); + + // Add tabs for each container. + for (int i = 0; i < (int)m_vContainerInfo.size(); ++i) + { + // Add tabs for each view. + TCITEM tie = {0}; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = i; + tie.pszText = m_vContainerInfo[i].szTitle; + TabCtrl_InsertItem(m_hWnd, i, &tie); + } + } + + inline void CDockContainer::OnLButtonDown(WPARAM wParam, LPARAM lParam) + { + // Overrides CTab::OnLButtonDown + + UNREFERENCED_PARAMETER(wParam); + + CPoint pt((DWORD)lParam); + TCHITTESTINFO info = {0}; + info.pt = pt; + m_nTabPressed = HitTest(info); + } + + inline void CDockContainer::OnLButtonUp(WPARAM wParam, LPARAM lParam) + { + // Overrides CTab::OnLButtonUp and takes no action + + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + } + + inline void CDockContainer::OnMouseLeave(WPARAM wParam, LPARAM lParam) + { + // Overrides CTab::OnMouseLeave + + if (IsLeftButtonDown() && (m_nTabPressed >= 0)) + { + CDocker* pDock = (CDocker*)FromHandle(::GetParent(GetParent()->GetHwnd())); + if (dynamic_cast<CDocker*>(pDock)) + { + CDockContainer* pContainer = GetContainerFromIndex(m_iCurrentPage); + pDock->UndockContainer(pContainer, GetCursorPos(), TRUE); + } + } + + m_nTabPressed = -1; + CTab::OnMouseLeave(wParam, lParam); + } + + inline LRESULT CDockContainer::OnNotifyReflect(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + { + // Display the newly selected tab page + int nPage = GetCurSel(); + SelectPage(nPage); + } + break; + } + + return 0L; + } + + inline void CDockContainer::PreCreate(CREATESTRUCT &cs) + { + // For Tabs on the bottom, add the TCS_BOTTOM style + CTab::PreCreate(cs); + cs.style |= TCS_BOTTOM; + } + + inline void CDockContainer::RecalcLayout() + { + if (GetContainerParent() == this) + { + // Set the tab sizes + SetTabSize(); + + // Position the View over the tab control's display area + CRect rc = GetClientRect(); + AdjustRect(FALSE, &rc); + CDockContainer* pContainer = m_vContainerInfo[m_iCurrentPage].pContainer; + pContainer->GetViewPage().SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW); + } + } + + inline void CDockContainer::RemoveContainer(CDockContainer* pWnd) + { + assert(pWnd); + + // Remove the tab + int iTab = GetContainerIndex(pWnd); + if (iTab > 0) + { + // DeleteItem(iTab); + TabCtrl_DeleteItem(m_hWnd, iTab); + } + + // Remove the ContainerInfo entry + std::vector<ContainerInfo>::iterator iter; + int iImage = -1; + for (iter = m_vContainerInfo.begin(); iter != m_vContainerInfo.end(); ++iter) + { + if (iter->pContainer == pWnd) + { + iImage = (*iter).iImage; + if (iImage >= 0) + TabCtrl_RemoveImage(m_hWnd, iImage); + + m_vContainerInfo.erase(iter); + break; + } + } + + // Set the parent container relationships + pWnd->GetViewPage().SetParent(pWnd); + pWnd->m_pContainerParent = pWnd; + + // Display the first page + m_iCurrentPage = 0; + if (IsWindow()) + SelectPage(0); + } + + inline void CDockContainer::SelectPage(int nPage) + { + if (this != m_pContainerParent) + m_pContainerParent->SelectPage(nPage); + else + { + if ((nPage >= 0) && (nPage < (int)m_vContainerInfo.size() )) + { + if (GetCurSel() != nPage) + SetCurSel(nPage); + + // Create the new container window if required + if (!m_vContainerInfo[nPage].pContainer->IsWindow()) + { + CDockContainer* pContainer = m_vContainerInfo[nPage].pContainer; + pContainer->Create(GetParent()); + pContainer->GetViewPage().SetParent(this); + } + + // Determine the size of the tab page's view area + CRect rc = GetClientRect(); + AdjustRect(FALSE, &rc); + + // Swap the pages over + CDockContainer* pOldContainer = m_vContainerInfo[m_iCurrentPage].pContainer; + CDockContainer* pNewContainer = m_vContainerInfo[nPage].pContainer; + pOldContainer->GetViewPage().ShowWindow(SW_HIDE); + pNewContainer->GetViewPage().SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW); + pNewContainer->GetViewPage().GetView()->SetFocus(); + + // Adjust the docking caption + CDocker* pDock = (CDocker*)FromHandle(::GetParent(::GetParent(m_hWnd))); + if (dynamic_cast<CDocker*>(pDock)) + { + pDock->SetCaption(pNewContainer->GetDockCaption().c_str()); + pDock->RedrawWindow(); + } + + m_iCurrentPage = nPage; + } + } + } + + inline void CDockContainer::SetActiveContainer(CDockContainer* pContainer) + { + int nPage = GetContainerIndex(pContainer); + assert (0 <= nPage); + SelectPage(nPage); + } + + inline void CDockContainer::SetTabIcon(UINT nID_Icon) + { + HICON hIcon = (HICON)LoadImage(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(nID_Icon), IMAGE_ICON, 0, 0, LR_SHARED); + SetTabIcon(hIcon); + } + + inline void CDockContainer::SetTabSize() + { + CRect rc = GetClientRect(); + AdjustRect(FALSE, &rc); + if (rc.Width() < 0 ) + rc.SetRectEmpty(); + + int nItemWidth = MIN(25 + GetMaxTabTextSize().cx, (rc.Width()-2)/(int)m_vContainerInfo.size()); + int nItemHeight = MAX(20, GetTextHeight() + 5); + SendMessage(TCM_SETITEMSIZE, 0L, MAKELPARAM(nItemWidth, nItemHeight)); + } + + inline void CDockContainer::SetTabText(UINT nTab, LPCTSTR szText) + { + CDockContainer* pContainer = GetContainerParent()->GetContainerFromIndex(nTab); + pContainer->SetTabText(szText); + + CTab::SetTabText(nTab, szText); + } + + inline void CDockContainer::SetView(CWnd& Wnd) + { + GetViewPage().SetView(Wnd); + } + + inline LRESULT CDockContainer::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_SIZE: + RecalcLayout(); + return 0; + + // The following are called in CTab::WndProcDefault + // case WM_LBUTTONDOWN: + // OnLButtonDown(wParam, lParam); + // break; + // case WM_LBUTTONUP: + // OnLButtonUp(wParam, lParam); + // break; + // case WM_MOUSELEAVE: + // OnMouseLeave(wParam, lParam); + // break; + + case WM_SETFOCUS: + { + // Pass focus on to the current view + GetActiveView()->SetFocus(); + } + break; + } + + // pass unhandled messages on to CTab for processing + return CTab::WndProcDefault(uMsg, wParam, lParam); + } + + + /////////////////////////////////////////// + // Declaration of the nested CViewPage class + inline BOOL CDockContainer::CViewPage::OnCommand(WPARAM wParam, LPARAM lParam) + { + CDockContainer* pContainer = (CDockContainer*)GetParent(); + BOOL bResult = FALSE; + if (pContainer && pContainer->GetActiveContainer()) + bResult = (BOOL)pContainer->GetActiveContainer()->SendMessage(WM_COMMAND, wParam, lParam); + + return bResult; + } + + inline void CDockContainer::CViewPage::OnCreate() + { + if (m_pView) + m_pView->Create(this); + } + + inline LRESULT CDockContainer::CViewPage::OnNotify(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + + // Display tooltips for the toolbar + case TTN_GETDISPINFO: + { + int iIndex = GetToolBar().HitTest(); + LPNMTTDISPINFO lpDispInfo = (LPNMTTDISPINFO)lParam; + if (iIndex >= 0) + { + int nID = GetToolBar().GetCommandID(iIndex); + if (nID > 0) + { + m_tsTooltip = LoadString(nID); + lpDispInfo->lpszText = (LPTSTR)m_tsTooltip.c_str(); + } + else + m_tsTooltip = _T(""); + } + } + break; + } // switch LPNMHDR + + return 0L; + } + + inline void CDockContainer::CViewPage::PreRegisterClass(WNDCLASS &wc) + { + wc.lpszClassName = _T("Win32++ TabPage"); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + } + + inline void CDockContainer::CViewPage::RecalcLayout() + { + CRect rc = GetClientRect(); + if (GetToolBar().IsWindow()) + { + GetToolBar().SendMessage(TB_AUTOSIZE, 0L, 0L); + CRect rcToolBar = m_ToolBar.GetClientRect(); + rc.top += rcToolBar.Height(); + } + + GetView()->SetWindowPos(NULL, rc, SWP_SHOWWINDOW); + } + + inline void CDockContainer::CViewPage::SetView(CWnd& wndView) + // Sets or changes the View window displayed within the frame + { + // Assign the view window + m_pView = &wndView; + + if (m_hWnd) + { + if (!m_pView->IsWindow()) + { + // The container is already created, so create and position the new view too + GetView()->Create(this); + } + + RecalcLayout(); + } + } + + inline LRESULT CDockContainer::CViewPage::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_SIZE: + RecalcLayout(); + break; + case WM_NOTIFY: + switch (((LPNMHDR)lParam)->code) + { + // Send the focus change notifications to the grandparent + case NM_KILLFOCUS: + case NM_SETFOCUS: + case UWM_FRAMELOSTFOCUS: + ::SendMessage(::GetParent(::GetParent(m_hWnd)), WM_NOTIFY, wParam, lParam); + break; + } + + break; + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + +} // namespace Win32xx + +#endif // _WIN32XX_DOCKING_H_ + diff --git a/mmc_updater/depends/win32cpp/file.h b/mmc_updater/depends/win32cpp/file.h new file mode 100644 index 00000000..4316dc94 --- /dev/null +++ b/mmc_updater/depends/win32cpp/file.h @@ -0,0 +1,392 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +#ifndef _WIN32XX_FILE_H_ +#define _WIN32XX_FILE_H_ + + +#include "wincore.h" + +namespace Win32xx +{ + + class CFile + { + public: + CFile(); + CFile(HANDLE hFile); + CFile(LPCTSTR pszFileName, UINT nOpenFlags); + ~CFile(); + operator HANDLE() const; + + BOOL Close(); + BOOL Flush(); + HANDLE GetHandle() const; + ULONGLONG GetLength() const; + const CString& GetFileName() const; + const CString& GetFilePath() const; + const CString& GetFileTitle() const; + ULONGLONG GetPosition() const; + BOOL LockRange(ULONGLONG Pos, ULONGLONG Count); + BOOL Open(LPCTSTR pszFileName, UINT nOpenFlags); + CString OpenFileDialog(LPCTSTR pszFilePathName = NULL, + DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR pszFilter = NULL, + CWnd* pOwnerWnd = NULL); + UINT Read(void* pBuf, UINT nCount); + static BOOL Remove(LPCTSTR pszFileName); + static BOOL Rename(LPCTSTR pszOldName, LPCTSTR pszNewName); + CString SaveFileDialog(LPCTSTR pszFilePathName = NULL, + DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR pszFilter = NULL, + LPCTSTR pszDefExt = NULL, CWnd* pOwnerWnd = NULL); + ULONGLONG Seek(LONGLONG lOff, UINT nFrom); + void SeekToBegin(); + ULONGLONG SeekToEnd(); + void SetFilePath(LPCTSTR pszNewName); + BOOL SetLength(ULONGLONG NewLen); + BOOL UnlockRange(ULONGLONG Pos, ULONGLONG Count); + BOOL Write(const void* pBuf, UINT nCount); + + private: + CFile(const CFile&); // Disable copy construction + CFile& operator = (const CFile&); // Disable assignment operator + CString m_FileName; + CString m_FilePath; + CString m_FileTitle; + HANDLE m_hFile; + }; + +} + + + +namespace Win32xx +{ + inline CFile::CFile() : m_hFile(0) + { + } + + inline CFile::CFile(HANDLE hFile) : m_hFile(hFile) + { + } + + inline CFile::CFile(LPCTSTR pszFileName, UINT nOpenFlags) : m_hFile(0) + { + assert(pszFileName); + Open(pszFileName, nOpenFlags); + assert(m_hFile); + } + + inline CFile::~CFile() + { + Close(); + } + + inline CFile::operator HANDLE() const + { + return m_hFile; + } + + inline BOOL CFile::Close() + // Closes the file associated with this object. Closed file can no longer be read or written to. + { + BOOL bResult = TRUE; + if (m_hFile) + bResult = CloseHandle(m_hFile); + + m_hFile = 0; + return bResult; + } + + inline BOOL CFile::Flush() + // Causes any remaining data in the file buffer to be written to the file. + { + assert(m_hFile); + return FlushFileBuffers(m_hFile); + } + + inline HANDLE CFile::GetHandle() const + { + return m_hFile; + } + + inline ULONGLONG CFile::GetLength( ) const + // Returns the length of the file in bytes. + { + assert(m_hFile); + + LONG High = 0; + DWORD LowPos = SetFilePointer(m_hFile, 0, &High, FILE_END); + + ULONGLONG Result = ((ULONGLONG)High << 32) + LowPos; + return Result; + } + + inline const CString& CFile::GetFileName() const + // Returns the filename of the file associated with this object. + { + return (const CString&)m_FileName; + } + + inline const CString& CFile::GetFilePath() const + // Returns the full filename including the directory of the file associated with this object. + { + return (const CString&)m_FilePath; + } + + inline const CString& CFile::GetFileTitle() const + // Returns the filename of the file associated with this object, excluding the path and the file extension + { + return (const CString&)m_FileTitle; + } + + inline ULONGLONG CFile::GetPosition() const + // Returns the current value of the file pointer, which can be used in subsequent calls to Seek. + { + assert(m_hFile); + LONG High = 0; + DWORD LowPos = SetFilePointer(m_hFile, 0, &High, FILE_CURRENT); + + ULONGLONG Result = ((ULONGLONG)High << 32) + LowPos; + return Result; + } + + inline BOOL CFile::LockRange(ULONGLONG Pos, ULONGLONG Count) + // Locks a range of bytes in and open file. + { + assert(m_hFile); + + DWORD dwPosHigh = (DWORD)(Pos >> 32); + DWORD dwPosLow = (DWORD)(Pos & 0xFFFFFFFF); + DWORD dwCountHigh = (DWORD)(Count >> 32); + DWORD dwCountLow = (DWORD)(Count & 0xFFFFFFFF); + + return ::LockFile(m_hFile, dwPosLow, dwPosHigh, dwCountLow, dwCountHigh); + } + + inline BOOL CFile::Open(LPCTSTR pszFileName, UINT nOpenFlags) + // Prepares a file to be written to or read from. + { + if (m_hFile) Close(); + + m_hFile = ::CreateFile(pszFileName, GENERIC_READ | GENERIC_WRITE, 0, NULL, nOpenFlags, FILE_ATTRIBUTE_NORMAL, NULL); + + if (INVALID_HANDLE_VALUE == m_hFile) + { + TRACE(_T("Failed\n")); + m_hFile = 0; + } + + if (m_hFile) + { + SetFilePath(pszFileName); + } + + return (m_hFile != 0); + } + + inline CString CFile::OpenFileDialog(LPCTSTR pszFilePathName, DWORD dwFlags, LPCTSTR pszFilter, CWnd* pOwnerWnd) + // Displays the file open dialog. + // Returns a CString containing either the selected file name or an empty CString. + { + CString str; + if (pszFilePathName) + str = pszFilePathName; + + OPENFILENAME ofn = {0}; + ofn.lStructSize = sizeof(OPENFILENAME); + +#if defined OPENFILENAME_SIZE_VERSION_400 + if (GetWinVersion() < 2500) + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; +#endif + + ofn.hwndOwner = pOwnerWnd? pOwnerWnd->GetHwnd() : NULL; + ofn.hInstance = GetApp()->GetInstanceHandle(); + ofn.lpstrFilter = pszFilter; + ofn.lpstrTitle = _T("Open File"); + ofn.Flags = dwFlags; + ofn.nMaxFile = _MAX_PATH; + + ofn.lpstrFile = (LPTSTR)str.GetBuffer(_MAX_PATH); + ::GetOpenFileName(&ofn); + str.ReleaseBuffer(); + + return str; + } + + inline UINT CFile::Read(void* pBuf, UINT nCount) + // Reads from the file, storing the contents in the specified buffer. + { + assert(m_hFile); + DWORD dwRead = 0; + + if (!::ReadFile(m_hFile, pBuf, nCount, &dwRead, NULL)) + dwRead = 0; + + return dwRead; + } + + inline BOOL CFile::Rename(LPCTSTR pszOldName, LPCTSTR pszNewName) + // Renames the specified file. + { + return ::MoveFile(pszOldName, pszNewName); + } + + inline BOOL CFile::Remove(LPCTSTR pszFileName) + // Deletes the specified file. + { + return ::DeleteFile(pszFileName); + } + + inline CString CFile::SaveFileDialog(LPCTSTR pszFilePathName, DWORD dwFlags, LPCTSTR pszFilter, LPCTSTR pszDefExt, CWnd* pOwnerWnd) + // Displays the SaveFileDialog. + // Returns a CString containing either the selected file name or an empty CString + { + CString str; + if (pszFilePathName) + str = pszFilePathName; + + OPENFILENAME ofn = {0}; + ofn.lStructSize = sizeof(OPENFILENAME); + +#if defined OPENFILENAME_SIZE_VERSION_400 + if (GetWinVersion() < 2500) + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; +#endif + + ofn.hwndOwner = pOwnerWnd? pOwnerWnd->GetHwnd() : NULL; + ofn.hInstance = GetApp()->GetInstanceHandle(); + ofn.lpstrFilter = pszFilter; + ofn.lpstrFile = (LPTSTR)pszFilePathName; + ofn.lpstrFileTitle = (LPTSTR)pszFilePathName; + ofn.lpstrDefExt = pszDefExt; + ofn.nMaxFile = lstrlen(pszFilePathName); + ofn.lpstrTitle = _T("Save File"); + ofn.Flags = dwFlags; + ofn.nMaxFile = _MAX_PATH; + ofn.lpstrFile = (LPTSTR)str.GetBuffer(_MAX_PATH); + ::GetSaveFileName(&ofn); + str.ReleaseBuffer(); + + return str; + } + + inline ULONGLONG CFile::Seek(LONGLONG lOff, UINT nFrom) + // Positions the current file pointer. + // Permitted values for nFrom are: FILE_BEGIN, FILE_CURRENT, or FILE_END. + { + assert(m_hFile); + assert(nFrom == FILE_BEGIN || nFrom == FILE_CURRENT || nFrom == FILE_END); + + LONG High = LONG(lOff >> 32); + LONG Low = (LONG)(lOff & 0xFFFFFFFF); + + DWORD LowPos = SetFilePointer(m_hFile, Low, &High, nFrom); + + ULONGLONG Result = ((ULONGLONG)High << 32) + LowPos; + return Result; + } + + inline void CFile::SeekToBegin() + // Sets the current file pointer to the beginning of the file. + { + assert(m_hFile); + Seek(0, FILE_BEGIN); + } + + inline ULONGLONG CFile::SeekToEnd() + // Sets the current file pointer to the end of the file. + { + assert(m_hFile); + return Seek(0, FILE_END); + } + + inline void CFile::SetFilePath(LPCTSTR pszFileName) + // Specifies the full file name, including its path + { + TCHAR* pFileName = NULL; + int nBuffSize = ::GetFullPathName(pszFileName, 0, 0, 0); + if (nBuffSize > 0) + { + TCHAR* pBuff = m_FilePath.GetBuffer(nBuffSize); + ::GetFullPathName(pszFileName, nBuffSize, pBuff, &pFileName); + m_FilePath.ReleaseBuffer(); + m_FileName = pFileName; + int nPos = m_FileName.ReverseFind(_T(".")); + if (nPos >= 0) + m_FileTitle = m_FileName.Left(nPos); + } + } + + inline BOOL CFile::SetLength(ULONGLONG NewLen) + // Changes the length of the file to the specified value. + { + assert(m_hFile); + + Seek(NewLen, FILE_BEGIN); + return ::SetEndOfFile(m_hFile); + } + + inline BOOL CFile::UnlockRange(ULONGLONG Pos, ULONGLONG Count) + // Unlocks a range of bytes in an open file. + { + assert(m_hFile); + + DWORD dwPosHigh = (DWORD)(Pos >> 32); + DWORD dwPosLow = (DWORD)(Pos & 0xFFFFFFFF); + DWORD dwCountHigh = (DWORD)(Count >> 32); + DWORD dwCountLow = (DWORD)(Count & 0xFFFFFFFF); + + return ::UnlockFile(m_hFile, dwPosLow, dwPosHigh, dwCountLow, dwCountHigh); + } + + inline BOOL CFile::Write(const void* pBuf, UINT nCount) + // Writes the specified buffer to the file. + { + assert(m_hFile); + DWORD dwWritten = 0; + BOOL bResult = ::WriteFile(m_hFile, pBuf, nCount, &dwWritten, NULL); + if (dwWritten != nCount) + bResult = FALSE; + + return bResult; + } + + +} // namespace Win32xx + +#endif diff --git a/mmc_updater/depends/win32cpp/frame.h b/mmc_updater/depends/win32cpp/frame.h new file mode 100644 index 00000000..1f439fd7 --- /dev/null +++ b/mmc_updater/depends/win32cpp/frame.h @@ -0,0 +1,3303 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// frame.h +// Declaration of the CFrame and CMenuBar classes + +// The classes declared in this file support SDI (Single Document Interface) +// frames on Win32/Win64 operating systems (not Windows CE). For Windows CE, +// use wceframe.h instead. SDI frames are a simple frame which supports a +// single view window. Refer to mdi.h for frames that support several +// child windows. + +// CFrame also includes each of the following classes as members: +// * CReBar for managing the frame's rebar control. +// * CMenuBar for managing the menu inside the rebar. +// * CToolBar for managing the frame's toolbar. +// * CStatusBar for managing the frame's status bar. +// In each case these members are exposed by a GetXXX function, allowing +// them to be accessed or sent messages. + +// CFrame is responsible for creating a "frame" window. This window has a +// menu and and several child windows, including a toolbar (usualy hosted +// within a rebar), a status bar, and a view positioned over the frame +// window's non-client area. The "view" window is a seperate CWnd object +// assigned to the frame with the SetView function. + +// When compiling an application with these classes, it will need to be linked +// with Comctl32.lib. + +// To create a SDI frame application, inherit a CMainFrame class from CFrame. +// Use the Frame sample application as the starting point for your own frame +// applications. +// Refer to the Notepad and Scribble samples for examples on how to use these +// classes to create a frame application. + + +#ifndef _WIN32XX_FRAME_H_ +#define _WIN32XX_FRAME_H_ + +#include "wincore.h" +#include "dialog.h" +#include "gdi.h" +#include "statusbar.h" +#include "toolbar.h" +#include "rebar.h" +#include "default_resource.h" + +#ifndef RBN_MINMAX + #define RBN_MINMAX (RBN_FIRST - 21) +#endif + + +namespace Win32xx +{ + + //////////////////////////////////////////////// + // Declarations for structures for themes + // + struct MenuTheme + { + BOOL UseThemes; // TRUE if themes are used + COLORREF clrHot1; // Colour 1 for top menu. Color of selected menu item + COLORREF clrHot2; // Colour 2 for top menu. Color of checkbox + COLORREF clrPressed1; // Colour 1 for pressed top menu and side bar + COLORREF clrPressed2; // Colour 2 for pressed top menu and side bar + COLORREF clrOutline; // Colour for border outline + }; + + + // Forward declaration of CFrame. Its defined later. + class CFrame; + + + //////////////////////////////////// + // Declaration of the CMenuBar class + // + class CMenuBar : public CToolBar + { + friend class CFrame; + + public: + CMenuBar(); + virtual ~CMenuBar(); + virtual void SetMenu(HMENU hMenu); + virtual void SetMenuBarTheme(MenuTheme& Theme); + + HMENU GetMenu() const {return m_hTopMenu;} + MenuTheme& GetMenuBarTheme() {return m_ThemeMenu;} + + protected: + //Overridables + virtual void OnCreate(); + virtual LRESULT OnCustomDraw(NMHDR* pNMHDR); + virtual void OnKeyDown(WPARAM wParam, LPARAM lParam); + virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam); + virtual void OnMenuChar(WPARAM wParam, LPARAM lParam); + virtual BOOL OnMenuInput(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void OnMouseLeave(); + virtual void OnMouseMove(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam); + virtual void OnSysCommand(WPARAM wParam, LPARAM lParam); + virtual void OnWindowPosChanged(); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CMenuBar(const CMenuBar&); // Disable copy construction + CMenuBar& operator = (const CMenuBar&); // Disable assignment operator + void DoAltKey(WORD KeyCode); + void DoPopupMenu(); + void DrawAllMDIButtons(CDC& DrawDC); + void DrawMDIButton(CDC& DrawDC, int iButton, UINT uState); + void ExitMenu(); + HWND GetActiveMDIChild(); + void GrabFocus(); + BOOL IsMDIChildMaxed() const; + BOOL IsMDIFrame() const; + void ReleaseFocus(); + void SetHotItem(int nHot); + static LRESULT CALLBACK StaticMsgHook(int nCode, WPARAM wParam, LPARAM lParam); + + enum MDIButtonType + { + MDI_MIN = 0, + MDI_RESTORE = 1, + MDI_CLOSE = 2, + }; + + BOOL m_bExitAfter; // Exit after Popup menu ends + BOOL m_bKeyMode; // keyboard navigation mode + BOOL m_bMenuActive; // popup menu active + BOOL m_bSelPopup; // a popup (cascade) menu is selected + HMENU m_hPopupMenu; // handle to the popup menu + HMENU m_hSelMenu; // handle to the casceded popup menu + HMENU m_hTopMenu; // handle to the top level menu + HWND m_hPrevFocus; // handle to window which had focus + CRect m_MDIRect[3]; // array of CRect for MDI buttons + int m_nHotItem; // hot item + int m_nMDIButton; // the MDI button (MDIButtonType) pressed + CPoint m_OldMousePos; // old Mouse position + MenuTheme m_ThemeMenu; // Theme structure + CFrame* m_pFrame; // Pointer to the frame + + }; // class CMenuBar + + + + ////////////////////////////////// + // Declaration of the CFrame class + // + class CFrame : public CWnd + { + friend class CMenuBar; + + struct ItemData + // Each Dropdown menu item has this data + { + HMENU hMenu; + UINT nPos; + UINT fType; + std::vector<TCHAR> vItemText; + HMENU hSubMenu; + + ItemData() : hMenu(0), nPos(0), fType(0), hSubMenu(0) { vItemText.assign(MAX_MENU_STRING, _T('\0')); } + LPTSTR GetItemText() {return &vItemText[0];} + }; + + typedef Shared_Ptr<ItemData> ItemDataPtr; + + public: + CFrame(); + virtual ~CFrame(); + + // Override these functions as required + virtual void AdjustFrameRect(RECT rcView) const; + virtual CRect GetViewRect() const; + virtual BOOL IsMDIFrame() const { return FALSE; } + virtual void SetStatusIndicators(); + virtual void SetStatusText(); + virtual void RecalcLayout(); + virtual MenuTheme& GetMenuTheme() const { return (MenuTheme&) m_ThemeMenu; } + virtual ReBarTheme& GetReBarTheme() const { return (ReBarTheme&)GetReBar().GetReBarTheme(); } + virtual ToolBarTheme& GetToolBarTheme() const { return (ToolBarTheme&)GetToolBar().GetToolBarTheme(); } + + // Virtual Attributes + // If you need to modify the default behaviour of the menubar, rebar, + // statusbar or toolbar, inherit from those classes, and override + // the following attribute functions. + virtual CMenuBar& GetMenuBar() const { return (CMenuBar&)m_MenuBar; } + virtual CReBar& GetReBar() const { return (CReBar&)m_ReBar; } + virtual CStatusBar& GetStatusBar() const { return (CStatusBar&)m_StatusBar; } + virtual CToolBar& GetToolBar() const { return (CToolBar&)m_ToolBar; } + + // These functions aren't virtual, and shouldn't be overridden + HACCEL GetFrameAccel() const { return m_hAccel; } + CMenu& GetFrameMenu() const { return (CMenu&)m_Menu; } + std::vector<tString> GetMRUEntries() const { return m_vMRUEntries; } + tString GetRegistryKeyName() const { return m_tsKeyName; } + CWnd* GetView() const { return m_pView; } + tString GetMRUEntry(UINT nIndex); + void SetFrameMenu(INT ID_MENU); + void SetFrameMenu(HMENU hMenu); + void SetMenuTheme(MenuTheme& Theme); + void SetView(CWnd& wndView); + BOOL IsMenuBarUsed() const { return (GetMenuBar() != 0); } + BOOL IsReBarSupported() const { return (GetComCtlVersion() >= 470); } + BOOL IsReBarUsed() const { return (GetReBar() != 0); } + + protected: + // Override these functions as required + virtual BOOL AddMenuIcon(int nID_MenuItem, HICON hIcon, int cx = 16, int cy = 16); + virtual UINT AddMenuIcons(const std::vector<UINT>& MenuData, COLORREF crMask, UINT ToolBarID, UINT ToolBarDisabledID); + virtual void AddMenuBarBand(); + virtual void AddMRUEntry(LPCTSTR szMRUEntry); + virtual void AddToolBarBand(CToolBar& TB, DWORD dwStyle, UINT nID); + virtual void AddToolBarButton(UINT nID, BOOL bEnabled = TRUE, LPCTSTR szText = 0); + virtual void CreateToolBar(); + virtual void DrawCheckmark(LPDRAWITEMSTRUCT pdis, CDC& DrawDC); + virtual void DrawMenuIcon(LPDRAWITEMSTRUCT pdis, CDC& DrawDC, BOOL bDisabled); + virtual void DrawMenuText(CDC& DrawDC, LPCTSTR ItemText, CRect& rc, COLORREF colorText); + virtual int GetMenuItemPos(HMENU hMenu, LPCTSTR szItem); + virtual BOOL LoadRegistrySettings(LPCTSTR szKeyName); + virtual BOOL LoadRegistryMRUSettings(UINT nMaxMRU = 0); + virtual void OnActivate(WPARAM wParam, LPARAM lParam); + virtual void OnClose(); + virtual void OnCreate(); + virtual void OnDestroy(); + virtual LRESULT OnDrawItem(WPARAM wParam, LPARAM lParam); + virtual void OnExitMenuLoop(); + virtual void OnHelp(); + virtual void OnInitMenuPopup(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnMeasureItem(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnMenuChar(WPARAM wParam, LPARAM lParam); + virtual void OnMenuSelect(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual void OnSetFocus(); + virtual void OnSysColorChange(); + virtual LRESULT OnSysCommand(WPARAM wParam, LPARAM lParam); + virtual void OnTimer(WPARAM wParam); + virtual void OnViewStatusBar(); + virtual void OnViewToolBar(); + virtual void PreCreate(CREATESTRUCT& cs); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual void RemoveMRUEntry(LPCTSTR szMRUEntry); + virtual BOOL SaveRegistrySettings(); + virtual void SetMenuBarBandSize(); + virtual UINT SetMenuIcons(const std::vector<UINT>& MenuData, COLORREF crMask, UINT ToolBarID, UINT ToolBarDisabledID); + virtual void SetupToolBar(); + virtual void SetTheme(); + virtual void SetToolBarImages(COLORREF crMask, UINT ToolBarID, UINT ToolBarHotID, UINT ToolBarDisabledID); + virtual void ShowMenu(BOOL bShow); + virtual void ShowStatusBar(BOOL bShow); + virtual void ShowToolBar(BOOL bShow); + virtual void UpdateMRUMenu(); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + enum Constants + { + ID_STATUS_TIMER = 1, + POST_TEXT_GAP = 16, // for owner draw menu item + }; + + tString m_tsStatusText; // TCHAR std::string for status text + BOOL m_bShowIndicatorStatus; // set to TRUE to see indicators in status bar + BOOL m_bShowMenuStatus; // set to TRUE to see menu and toolbar updates in status bar + BOOL m_bUseReBar; // set to TRUE if ReBars are to be used + BOOL m_bUseThemes; // set to TRUE if themes are to be used + BOOL m_bUpdateTheme; // set to TRUE to run SetThemes when theme changes + BOOL m_bUseToolBar; // set to TRUE if the toolbar is used + BOOL m_bUseCustomDraw; // set to TRUE to perform custom drawing on menu items + BOOL m_bShowStatusBar; // A flag to indicate if the StatusBar should be displayed + BOOL m_bShowToolBar; // A flag to indicate if the ToolBar should be displayed + MenuTheme m_ThemeMenu; // Theme structure for popup menus + HIMAGELIST m_himlMenu; // Imagelist of menu icons + HIMAGELIST m_himlMenuDis; // Imagelist of disabled menu icons + + private: + CFrame(const CFrame&); // Disable copy construction + CFrame& operator = (const CFrame&); // Disable assignment operator + void LoadCommonControls(); + + std::vector<ItemDataPtr> m_vMenuItemData;// vector of ItemData pointers + std::vector<UINT> m_vMenuIcons; // vector of menu icon resource IDs + std::vector<tString> m_vMRUEntries; // Vector of tStrings for MRU entires + CDialog m_AboutDialog; // Help about dialog + CMenuBar m_MenuBar; // CMenuBar object + CReBar m_ReBar; // CReBar object + CStatusBar m_StatusBar; // CStatusBar object + CToolBar m_ToolBar; // CToolBar object + CMenu m_Menu; // handle to the frame menu + HACCEL m_hAccel; // handle to the frame's accelerator table + CWnd* m_pView; // pointer to the View CWnd object + LPCTSTR m_OldStatus[3]; // Array of TCHAR pointers; + tString m_tsKeyName; // TCHAR std::string for Registry key name + tString m_tsTooltip; // TCHAR std::string for tool tips + UINT m_nMaxMRU; // maximum number of MRU entries + CRect m_rcPosition; // CRect of the starting window position + HWND m_hOldFocus; // The window which had focus prior to the app'a deactivation + int m_nOldID; // The previous ToolBar ID displayed in the statusbar + + }; // class CFrame + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + ///////////////////////////////////// + // Definitions for the CMenuBar class + // + inline CMenuBar::CMenuBar() + { + m_bExitAfter = FALSE; + m_hTopMenu = NULL; + m_nHotItem = -1; + m_bSelPopup = FALSE; + m_hSelMenu = NULL; + m_bMenuActive = FALSE; + m_bKeyMode = FALSE; + m_hPrevFocus = NULL; + m_nMDIButton = 0; + m_hPopupMenu = 0; + + ZeroMemory(&m_ThemeMenu, sizeof(MenuTheme)); + } + + inline CMenuBar::~CMenuBar() + { + } + + inline void CMenuBar::DoAltKey(WORD KeyCode) + { + //Handle key pressed with Alt held down + UINT ID; + if (SendMessage(TB_MAPACCELERATOR, KeyCode, (LPARAM) &ID)) + { + GrabFocus(); + m_bKeyMode = TRUE; + SetHotItem(ID); + m_bMenuActive = TRUE; + PostMessage(UWM_POPUPMENU, 0L, 0L); + } + else + ::MessageBeep(MB_OK); + } + + inline void CMenuBar::DoPopupMenu() + { + if (m_bKeyMode) + // Simulate a down arrow key press + PostMessage(WM_KEYDOWN, VK_DOWN, 0L); + + m_bKeyMode = FALSE; + m_bExitAfter = FALSE; + m_OldMousePos = GetCursorPos(); + + HWND hMaxMDIChild = NULL; + if (IsMDIChildMaxed()) + hMaxMDIChild = GetActiveMDIChild(); + + // Load the submenu + int nMaxedOffset = IsMDIChildMaxed()? 1:0; + m_hPopupMenu = ::GetSubMenu(m_hTopMenu, m_nHotItem - nMaxedOffset); + if (IsMDIChildMaxed() && (0 == m_nHotItem) ) + m_hPopupMenu = ::GetSystemMenu(hMaxMDIChild, FALSE); + + // Retrieve the bounding rectangle for the toolbar button + CRect rc = GetItemRect(m_nHotItem); + + // convert rectangle to desktop coordinates + ClientToScreen(rc); + + // Position popup above toolbar if it won't fit below + TPMPARAMS tpm; + tpm.cbSize = sizeof(TPMPARAMS); + tpm.rcExclude = rc; + + // Set the hot button + SendMessage(TB_SETHOTITEM, m_nHotItem, 0L); + SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(TRUE, 0)); + + m_bSelPopup = FALSE; + m_hSelMenu = NULL; + m_bMenuActive = TRUE; + + // We hook mouse input to process mouse and keyboard input during + // the popup menu. Messages are sent to StaticMsgHook. + + // Remove any remaining hook first + TLSData* pTLSData = (TLSData*)::TlsGetValue(GetApp()->GetTlsIndex()); + pTLSData->pMenuBar = this; + if (pTLSData->hHook != NULL) + ::UnhookWindowsHookEx(pTLSData->hHook); + + // Hook messages about to be processed by the shortcut menu + pTLSData->hHook = ::SetWindowsHookEx(WH_MSGFILTER, (HOOKPROC)StaticMsgHook, NULL, ::GetCurrentThreadId()); + + // Display the shortcut menu + BOOL bRightToLeft = FALSE; + +#if defined(WINVER) && defined (WS_EX_LAYOUTRTL) && (WINVER >= 0x0500) + bRightToLeft = ((GetAncestor()->GetWindowLongPtr(GWL_EXSTYLE)) & WS_EX_LAYOUTRTL); +#endif + + int xPos = bRightToLeft? rc.right : rc.left; + UINT nID = ::TrackPopupMenuEx(m_hPopupMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL, + xPos, rc.bottom, m_hWnd, &tpm); + + // We get here once the TrackPopupMenuEx has ended + m_bMenuActive = FALSE; + + // Remove the message hook + ::UnhookWindowsHookEx(pTLSData->hHook); + pTLSData->hHook = NULL; + + // Process MDI Child system menu + if (IsMDIChildMaxed()) + { + if (::GetSystemMenu(hMaxMDIChild, FALSE) == m_hPopupMenu ) + { + if (nID) + ::SendMessage(hMaxMDIChild, WM_SYSCOMMAND, nID, 0L); + } + } + + // Resestablish Focus + if (m_bKeyMode) + GrabFocus(); + } + + inline void CMenuBar::DrawAllMDIButtons(CDC& DrawDC) + { + if (!IsMDIFrame()) + return; + + if (IsMDIChildMaxed()) + { + int cx = GetSystemMetrics(SM_CXSMICON); + int cy = GetSystemMetrics(SM_CYSMICON); + CRect rc = GetClientRect(); + int gap = 4; + rc.right -= gap; + + // Assign values to each element of the CRect array + for (int i = 0 ; i < 3 ; ++i) + { + int left = rc.right - (i+1)*cx - gap*(i+1); + int top = rc.bottom/2 - cy/2; + int right = rc.right - i*cx - gap*(i+1); + int bottom = rc.bottom/2 + cy/2; + ::SetRect(&m_MDIRect[2 - i], left, top, right, bottom); + } + + // Hide the MDI button if it won't fit + for (int k = 0 ; k <= 2 ; ++k) + { + + if (m_MDIRect[k].left < GetMaxSize().cx) + { + ::SetRectEmpty(&m_MDIRect[k]); + } + } + + DrawMDIButton(DrawDC, MDI_MIN, 0); + DrawMDIButton(DrawDC, MDI_RESTORE, 0); + DrawMDIButton(DrawDC, MDI_CLOSE, 0); + } + } + + inline void CMenuBar::DrawMDIButton(CDC& DrawDC, int iButton, UINT uState) + { + if (!IsRectEmpty(&m_MDIRect[iButton])) + { + switch (uState) + { + case 0: + { + // Draw a grey outline + DrawDC.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNFACE)); + DrawDC.MoveTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom); + DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].bottom); + DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].top); + DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].top); + DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom); + } + break; + case 1: + { + // Draw outline, white at top, black on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.MoveTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom); + DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].bottom); + DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].top); + DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom); + } + + break; + case 2: + { + // Draw outline, black on top, white on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.MoveTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom); + DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].bottom); + DrawDC.LineTo(m_MDIRect[iButton].right, m_MDIRect[iButton].top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].top); + DrawDC.LineTo(m_MDIRect[iButton].left, m_MDIRect[iButton].bottom); + } + break; + } + + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + + switch (iButton) + { + case MDI_MIN: + // Manually Draw Minimise button + DrawDC.MoveTo(m_MDIRect[0].left + 4, m_MDIRect[0].bottom -4); + DrawDC.LineTo(m_MDIRect[0].right - 4, m_MDIRect[0].bottom - 4); + + DrawDC.MoveTo(m_MDIRect[0].left + 4, m_MDIRect[0].bottom -5); + DrawDC.LineTo(m_MDIRect[0].right - 4, m_MDIRect[0].bottom - 5); + break; + case MDI_RESTORE: + // Manually Draw Restore Button + DrawDC.MoveTo(m_MDIRect[1].left + 3, m_MDIRect[1].top + 7); + DrawDC.LineTo(m_MDIRect[1].left + 3, m_MDIRect[1].bottom -4); + DrawDC.LineTo(m_MDIRect[1].right - 6, m_MDIRect[1].bottom -4); + DrawDC.LineTo(m_MDIRect[1].right - 6, m_MDIRect[1].top + 7); + DrawDC.LineTo(m_MDIRect[1].left + 3, m_MDIRect[1].top + 7); + + DrawDC.MoveTo(m_MDIRect[1].left + 3, m_MDIRect[1].top + 8); + DrawDC.LineTo(m_MDIRect[1].right - 6, m_MDIRect[1].top + 8); + + DrawDC.MoveTo(m_MDIRect[1].left + 5, m_MDIRect[1].top + 7); + DrawDC.LineTo(m_MDIRect[1].left + 5, m_MDIRect[1].top + 4); + DrawDC.LineTo(m_MDIRect[1].right - 4, m_MDIRect[1].top + 4); + DrawDC.LineTo(m_MDIRect[1].right - 4, m_MDIRect[1].bottom -6); + DrawDC.LineTo(m_MDIRect[1].right - 6, m_MDIRect[1].bottom -6); + + DrawDC.MoveTo(m_MDIRect[1].left + 5, m_MDIRect[1].top + 5); + DrawDC.LineTo(m_MDIRect[1].right - 4, m_MDIRect[1].top + 5); + break; + case MDI_CLOSE: + // Manually Draw Close Button + DrawDC.MoveTo(m_MDIRect[2].left + 4, m_MDIRect[2].top +5); + DrawDC.LineTo(m_MDIRect[2].right - 4, m_MDIRect[2].bottom -3); + + DrawDC.MoveTo(m_MDIRect[2].left + 5, m_MDIRect[2].top +5); + DrawDC.LineTo(m_MDIRect[2].right - 4, m_MDIRect[2].bottom -4); + + DrawDC.MoveTo(m_MDIRect[2].left + 4, m_MDIRect[2].top +6); + DrawDC.LineTo(m_MDIRect[2].right - 5, m_MDIRect[2].bottom -3); + + DrawDC.MoveTo(m_MDIRect[2].right -5, m_MDIRect[2].top +5); + DrawDC.LineTo(m_MDIRect[2].left + 3, m_MDIRect[2].bottom -3); + + DrawDC.MoveTo(m_MDIRect[2].right -5, m_MDIRect[2].top +6); + DrawDC.LineTo(m_MDIRect[2].left + 4, m_MDIRect[2].bottom -3); + + DrawDC.MoveTo(m_MDIRect[2].right -6, m_MDIRect[2].top +5); + DrawDC.LineTo(m_MDIRect[2].left + 3, m_MDIRect[2].bottom -4); + break; + } + } + } + + inline void CMenuBar::ExitMenu() + { + ReleaseFocus(); + m_bKeyMode = FALSE; + m_bMenuActive = FALSE; + SendMessage(TB_PRESSBUTTON, m_nHotItem, (LPARAM) MAKELONG (FALSE, 0)); + SetHotItem(-1); + + CPoint pt = GetCursorPos(); + ScreenToClient(pt); + + // Update mouse mouse position for hot tracking + SendMessage(WM_MOUSEMOVE, 0L, MAKELONG(pt.x, pt.y)); + } + + inline HWND CMenuBar::GetActiveMDIChild() + { + HWND hwndMDIChild = NULL; + if (IsMDIFrame()) + { + hwndMDIChild = (HWND)::SendMessage(m_pFrame->GetView()->GetHwnd(), WM_MDIGETACTIVE, 0L, 0L); + } + + return hwndMDIChild; + } + + inline void CMenuBar::GrabFocus() + { + if (::GetFocus() != m_hWnd) + m_hPrevFocus = ::SetFocus(m_hWnd); + ::SetCapture(m_hWnd); + ::SetCursor(::LoadCursor(NULL, IDC_ARROW)); + } + + inline BOOL CMenuBar::IsMDIChildMaxed() const + { + BOOL bMaxed = FALSE; + + if (IsMDIFrame() && m_pFrame->GetView()->IsWindow()) + { + m_pFrame->GetView()->SendMessage(WM_MDIGETACTIVE, 0L, (LPARAM)&bMaxed); + } + + return bMaxed; + } + + inline BOOL CMenuBar::IsMDIFrame() const + { + return (m_pFrame->IsMDIFrame()); + } + + inline void CMenuBar::OnMenuChar(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + + if (!m_bMenuActive) + DoAltKey(LOWORD(wParam)); + } + + inline void CMenuBar::OnCreate() + { + // We must send this message before sending the TB_ADDBITMAP or TB_ADDBUTTONS message + SendMessage(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0L); + + m_pFrame = (CFrame*)GetAncestor(); + assert(m_pFrame); + } + + inline LRESULT CMenuBar::OnCustomDraw(NMHDR* pNMHDR) + // CustomDraw is used to render the MenuBar's toolbar buttons + { + if (m_ThemeMenu.UseThemes) + { + LPNMTBCUSTOMDRAW lpNMCustomDraw = (LPNMTBCUSTOMDRAW)pNMHDR; + + switch (lpNMCustomDraw->nmcd.dwDrawStage) + { + // Begin paint cycle + case CDDS_PREPAINT: + // Send NM_CUSTOMDRAW item draw, and post-paint notification messages. + return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT ; + + // An item is about to be drawn + case CDDS_ITEMPREPAINT: + { + CDC* pDrawDC = FromHandle(lpNMCustomDraw->nmcd.hdc); + CRect rcRect = lpNMCustomDraw->nmcd.rc; + int nState = lpNMCustomDraw->nmcd.uItemState; + DWORD dwItem = (DWORD)lpNMCustomDraw->nmcd.dwItemSpec; + + // Leave a pixel gap above and below the drawn rectangle + if (IsAeroThemed()) + rcRect.InflateRect(0, -2); + else + rcRect.InflateRect(0, -1); + + if (IsMDIChildMaxed() && (0 == dwItem)) + // Draw over MDI Max button + { + HICON hIcon = (HICON)::SendMessage(GetActiveMDIChild(), WM_GETICON, ICON_SMALL, 0L); + if (NULL == hIcon) + hIcon = ::LoadIcon(NULL, IDI_APPLICATION); + + int cx = ::GetSystemMetrics (SM_CXSMICON); + int cy = ::GetSystemMetrics (SM_CYSMICON); + int y = 1 + (GetWindowRect().Height() - cy)/2; + int x = (rcRect.Width() - cx)/2; + pDrawDC->DrawIconEx(x, y, hIcon, cx, cy, 0, NULL, DI_NORMAL); + + pDrawDC->Detach(); // Optional, deletes GDI objects sooner + return CDRF_SKIPDEFAULT; // No further drawing + } + + else if (nState & (CDIS_HOT | CDIS_SELECTED)) + { + if ((nState & CDIS_SELECTED) || (GetButtonState(dwItem) & TBSTATE_PRESSED)) + { + pDrawDC->GradientFill(m_ThemeMenu.clrPressed1, m_ThemeMenu.clrPressed2, rcRect, FALSE); + } + else if (nState & CDIS_HOT) + { + pDrawDC->GradientFill(m_ThemeMenu.clrHot1, m_ThemeMenu.clrHot2, rcRect, FALSE); + } + + // Draw border + pDrawDC->CreatePen(PS_SOLID, 1, m_ThemeMenu.clrOutline); + pDrawDC->MoveTo(rcRect.left, rcRect.bottom); + pDrawDC->LineTo(rcRect.left, rcRect.top); + pDrawDC->LineTo(rcRect.right-1, rcRect.top); + pDrawDC->LineTo(rcRect.right-1, rcRect.bottom); + pDrawDC->MoveTo(rcRect.right-1, rcRect.bottom); + pDrawDC->LineTo(rcRect.left, rcRect.bottom); + + TCHAR str[80] = _T(""); + int nLength = (int)SendMessage(TB_GETBUTTONTEXT, lpNMCustomDraw->nmcd.dwItemSpec, 0L); + if ((nLength > 0) && (nLength < 80)) + SendMessage(TB_GETBUTTONTEXT, lpNMCustomDraw->nmcd.dwItemSpec, (LPARAM)str); + + // Draw highlight text + pDrawDC->SelectObject(GetFont()); + rcRect.bottom += 1; + int iMode = pDrawDC->SetBkMode(TRANSPARENT); + pDrawDC->DrawText(str, lstrlen(str), rcRect, DT_VCENTER | DT_CENTER | DT_SINGLELINE); + + pDrawDC->SetBkMode(iMode); + pDrawDC->Detach(); // Optional, deletes GDI objects sooner + return CDRF_SKIPDEFAULT; // No further drawing + } + } + return CDRF_DODEFAULT ; // Do default drawing + + // Painting cycle has completed + case CDDS_POSTPAINT: + // Draw MDI Minimise, Restore and Close buttons + { + CDC* pDrawDC = FromHandle(lpNMCustomDraw->nmcd.hdc); + DrawAllMDIButtons(*pDrawDC); + pDrawDC->Detach(); // Optional, deletes GDI objects sooner + } + break; + } + } + return 0L; + } + + inline void CMenuBar::OnKeyDown(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(lParam); + + switch (wParam) + { + case VK_ESCAPE: + ExitMenu(); + break; + + case VK_SPACE: + ExitMenu(); + // Bring up the system menu + GetAncestor()->PostMessage(WM_SYSCOMMAND, SC_KEYMENU, VK_SPACE); + break; + + // Handle VK_DOWN,VK_UP and VK_RETURN together + case VK_DOWN: + case VK_UP: + case VK_RETURN: + // Always use PostMessage for USER_POPUPMENU (not SendMessage) + PostMessage(UWM_POPUPMENU, 0L, 0L); + break; + + case VK_LEFT: + // Move left to next topmenu item + (m_nHotItem > 0)? SetHotItem(m_nHotItem -1) : SetHotItem(GetButtonCount()-1); + break; + + case VK_RIGHT: + // Move right to next topmenu item + (m_nHotItem < GetButtonCount() -1)? SetHotItem(m_nHotItem +1) : SetHotItem(0); + break; + + default: + // Handle Accelerator keys with Alt toggled down + if (m_bKeyMode) + { + UINT ID; + if (SendMessage(TB_MAPACCELERATOR, wParam, (LPARAM) &ID)) + { + m_nHotItem = ID; + PostMessage(UWM_POPUPMENU, 0L, 0L); + } + else + ::MessageBeep(MB_OK); + } + break; + } // switch (wParam) + } + + inline void CMenuBar::OnLButtonDown(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + GrabFocus(); + m_nMDIButton = 0; + CPoint pt; + + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + if (IsMDIFrame()) + { + if (IsMDIChildMaxed()) + { + CClientDC MenuBarDC(this); + m_nMDIButton = -1; + + if (m_MDIRect[0].PtInRect(pt)) m_nMDIButton = 0; + if (m_MDIRect[1].PtInRect(pt)) m_nMDIButton = 1; + if (m_MDIRect[2].PtInRect(pt)) m_nMDIButton = 2; + + if (m_nMDIButton >= 0) + { + DrawMDIButton(MenuBarDC, MDI_MIN, (0 == m_nMDIButton)? 2 : 0); + DrawMDIButton(MenuBarDC, MDI_RESTORE, (1 == m_nMDIButton)? 2 : 0); + DrawMDIButton(MenuBarDC, MDI_CLOSE, (2 == m_nMDIButton)? 2 : 0); + } + + // Bring up the MDI Child window's system menu when the icon is pressed + if (0 == HitTest()) + { + m_nHotItem = 0; + PostMessage(UWM_POPUPMENU, 0L, 0L); + } + } + } + } + + inline void CMenuBar::OnLButtonUp(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + CPoint pt; + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + if (IsMDIFrame()) + { + HWND MDIClient = m_pFrame->GetView()->GetHwnd(); + HWND MDIChild = GetActiveMDIChild(); + + if (IsMDIChildMaxed()) + { + CPoint pt = GetCursorPos(); + ScreenToClient(pt); + + // Process the MDI button action when the left mouse button is up + if (m_MDIRect[0].PtInRect(pt)) + { + if (MDI_MIN == m_nMDIButton) + ::ShowWindow(MDIChild, SW_MINIMIZE); + } + + if (m_MDIRect[1].PtInRect(pt)) + { + if (MDI_RESTORE == m_nMDIButton) + ::PostMessage(MDIClient, WM_MDIRESTORE, (WPARAM)MDIChild, 0L); + } + + if (m_MDIRect[2].PtInRect(pt)) + { + if (MDI_CLOSE == m_nMDIButton) + ::PostMessage(MDIChild, WM_CLOSE, 0L, 0L); + } + } + } + m_nMDIButton = 0; + ExitMenu(); + } + + inline BOOL CMenuBar::OnMenuInput(UINT uMsg, WPARAM wParam, LPARAM lParam) + // When a popup menu is active, StaticMsgHook directs all menu messages here + { + switch(uMsg) + { + case WM_KEYDOWN: + m_bExitAfter = FALSE; + { + switch (wParam) + { + case VK_ESCAPE: + // Use default processing if inside a Sub Menu + if ((m_hSelMenu) &&(m_hSelMenu != m_hPopupMenu)) + return FALSE; + + m_bMenuActive = FALSE; + m_bKeyMode = TRUE; + SendMessage(WM_CANCELMODE, 0L, 0L); + SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(FALSE, 0)); + SendMessage(TB_SETHOTITEM, m_nHotItem, 0L); + break; + + case VK_LEFT: + // Use default processing if inside a Sub Menu + if ((m_hSelMenu) &&(m_hSelMenu != m_hPopupMenu)) + return FALSE; + + SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(FALSE, 0)); + + // Move left to next topmenu item + (m_nHotItem > 0)? --m_nHotItem : m_nHotItem = GetButtonCount()-1; + SendMessage(WM_CANCELMODE, 0L, 0L); + + // Always use PostMessage for USER_POPUPMENU (not SendMessage) + PostMessage(UWM_POPUPMENU, 0L, 0L); + PostMessage(WM_KEYDOWN, VK_DOWN, 0L); + break; + + case VK_RIGHT: + // Use default processing to open Sub Menu + if (m_bSelPopup) + return FALSE; + + SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(FALSE, 0)); + + // Move right to next topmenu item + (m_nHotItem < GetButtonCount()-1)? ++m_nHotItem : m_nHotItem = 0; + SendMessage(WM_CANCELMODE, 0L, 0L); + + // Always use PostMessage for USER_POPUPMENU (not SendMessage) + PostMessage(UWM_POPUPMENU, 0L, 0L); + PostMessage(WM_KEYDOWN, VK_DOWN, 0L); + break; + + case VK_RETURN: + m_bExitAfter = TRUE; + break; + + } // switch (wParam) + + } // case WM_KEYDOWN + + return FALSE; + + case WM_CHAR: + m_bExitAfter = TRUE; + return FALSE; + + case WM_LBUTTONDOWN: + { + m_bExitAfter = TRUE; + if (HitTest() >= 0) + { + // Cancel popup when we hit a button a second time + SendMessage(WM_CANCELMODE, 0L, 0L); + return TRUE; + } + } + return FALSE; + + case WM_LBUTTONDBLCLK: + // Perform default action for DblClick on MDI Maxed icon + if (IsMDIChildMaxed() && (0 == HitTest())) + { + CWnd* pMDIChild = FromHandle(GetActiveMDIChild()); + CMenu* pChildMenu = pMDIChild->GetSystemMenu(FALSE); + + UINT nID = pChildMenu->GetDefaultItem(FALSE, 0); + if (nID) + pMDIChild->PostMessage(WM_SYSCOMMAND, nID, 0L); + } + + m_bExitAfter = TRUE; + return FALSE; + + case WM_MENUSELECT: + { + // store info about selected item + m_hSelMenu = (HMENU)lParam; + m_bSelPopup = ((HIWORD(wParam) & MF_POPUP) != 0); + + // Reflect message back to the frame window + GetAncestor()->SendMessage(WM_MENUSELECT, wParam, lParam); + } + return TRUE; + + case WM_MOUSEMOVE: + { + CPoint pt; + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + // Skip if mouse hasn't moved + if ((pt.x == m_OldMousePos.x) && (pt.y == m_OldMousePos.y)) + return FALSE; + + m_OldMousePos.x = pt.x; + m_OldMousePos.y = pt.y; + ScreenToClient(pt); + + // Reflect messages back to the MenuBar for hot tracking + SendMessage(WM_MOUSEMOVE, 0L, MAKELPARAM(pt.x, pt.y)); + } + break; + + } + return FALSE; + } + + inline void CMenuBar::OnMouseLeave() + { + if (IsMDIFrame()) + { + if (IsMDIChildMaxed()) + { + CClientDC MenuBarDC(this); + + DrawMDIButton(MenuBarDC, MDI_MIN, 0); + DrawMDIButton(MenuBarDC, MDI_RESTORE, 0); + DrawMDIButton(MenuBarDC, MDI_CLOSE, 0); + } + } + } + + inline void CMenuBar::OnMouseMove(WPARAM wParam, LPARAM lParam) + { + CPoint pt; + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + if (IsMDIFrame()) + { + if (IsMDIChildMaxed()) + { + CClientDC MenuBarDC(this); + int MDIButton = -1; + if (m_MDIRect[0].PtInRect(pt)) MDIButton = 0; + if (m_MDIRect[1].PtInRect(pt)) MDIButton = 1; + if (m_MDIRect[2].PtInRect(pt)) MDIButton = 2; + + if (MK_LBUTTON == wParam) // mouse moved with left mouse button is held down + { + // toggle the MDI button image pressed/unpressed as required + if (MDIButton >= 0) + { + DrawMDIButton(MenuBarDC, MDI_MIN, ((0 == MDIButton) && (0 == m_nMDIButton))? 2 : 0); + DrawMDIButton(MenuBarDC, MDI_RESTORE, ((1 == MDIButton) && (1 == m_nMDIButton))? 2 : 0); + DrawMDIButton(MenuBarDC, MDI_CLOSE, ((2 == MDIButton) && (2 == m_nMDIButton))? 2 : 0); + } + else + { + DrawMDIButton(MenuBarDC, MDI_MIN, 0); + DrawMDIButton(MenuBarDC, MDI_RESTORE, 0); + DrawMDIButton(MenuBarDC, MDI_CLOSE, 0); + } + } + else // mouse moved without left mouse button held down + { + if (MDIButton >= 0) + { + DrawMDIButton(MenuBarDC, MDI_MIN, (0 == MDIButton)? 1 : 0); + DrawMDIButton(MenuBarDC, MDI_RESTORE, (1 == MDIButton)? 1 : 0); + DrawMDIButton(MenuBarDC, MDI_CLOSE, (2 == MDIButton)? 1 : 0); + } + else + { + DrawMDIButton(MenuBarDC, MDI_MIN, 0); + DrawMDIButton(MenuBarDC, MDI_RESTORE, 0); + DrawMDIButton(MenuBarDC, MDI_CLOSE, 0); + } + } + } + } + } + + inline LRESULT CMenuBar::OnNotifyReflect(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + case NM_CUSTOMDRAW: + { + return OnCustomDraw((LPNMHDR) lParam); + } + + case TBN_DROPDOWN: + // Always use PostMessage for USER_POPUPMENU (not SendMessage) + PostMessage(UWM_POPUPMENU, 0L, 0L); + break; + + case TBN_HOTITEMCHANGE: + // This is the notification that a hot item change is about to occur + // This is used to bring up a new popup menu when required + { + CPoint pt = GetCursorPos(); + if (this == WindowFromPoint(pt)) // MenuBar window must be on top + { + DWORD flag = ((LPNMTBHOTITEM)lParam)->dwFlags; + if ((flag & HICF_MOUSE) && !(flag & HICF_LEAVING)) + { + int nButton = HitTest(); + if ((m_bMenuActive) && (nButton != m_nHotItem)) + { + SendMessage(TB_PRESSBUTTON, m_nHotItem, MAKELONG(FALSE, 0)); + m_nHotItem = nButton; + SendMessage(WM_CANCELMODE, 0L, 0L); + + //Always use PostMessage for USER_POPUPMENU (not SendMessage) + PostMessage(UWM_POPUPMENU, 0L, 0L); + } + m_nHotItem = nButton; + } + + // Handle escape from popup menu + if ((flag & HICF_LEAVING) && m_bKeyMode) + { + m_nHotItem = ((LPNMTBHOTITEM)lParam)->idOld; + PostMessage(TB_SETHOTITEM, m_nHotItem, 0L); + } + + } + break; + } //case TBN_HOTITEMCHANGE: + + } // switch(((LPNMHDR)lParam)->code) + return 0L; + } // CMenuBar::OnNotify(...) + + inline void CMenuBar::OnWindowPosChanged() + { + InvalidateRect(&m_MDIRect[0], TRUE); + InvalidateRect(&m_MDIRect[1], TRUE); + InvalidateRect(&m_MDIRect[2], TRUE); + { + CClientDC MenuBarDC(this); + DrawAllMDIButtons(MenuBarDC); + } + } + + inline void CMenuBar::PreCreate(CREATESTRUCT &cs) + { + cs.style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_LIST | TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NORESIZE; + } + + inline void CMenuBar::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = TOOLBARCLASSNAME; + } + + inline void CMenuBar::ReleaseFocus() + { + if (m_hPrevFocus) + ::SetFocus(m_hPrevFocus); + + m_hPrevFocus = NULL; + ::ReleaseCapture(); + } + + inline void CMenuBar::SetHotItem(int nHot) + { + m_nHotItem = nHot; + SendMessage(TB_SETHOTITEM, m_nHotItem, 0L); + } + + inline void CMenuBar::SetMenu(HMENU hMenu) + { + assert(::IsWindow(m_hWnd)); + + m_hTopMenu = hMenu; + int nMaxedOffset = (IsMDIChildMaxed()? 1:0); + + // Remove any existing buttons + while (SendMessage(TB_BUTTONCOUNT, 0L, 0L) > 0) + { + if(!SendMessage(TB_DELETEBUTTON, 0L, 0L)) + break; + } + + // Set the Bitmap size to zero + SendMessage(TB_SETBITMAPSIZE, 0L, MAKELPARAM(0, 0)); + + if (IsMDIChildMaxed()) + { + // Create an extra button for the MDI child system menu + // Later we will custom draw the window icon over this button + TBBUTTON tbb = {0}; + tbb.fsState = TBSTATE_ENABLED; + tbb.fsStyle = TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE ; + tbb.iString = (INT_PTR)_T(" "); + SendMessage(TB_ADDBUTTONS, 1, (WPARAM)&tbb); + SetButtonText(0, _T(" ")); + } + + for (int i = 0 ; i < ::GetMenuItemCount(hMenu); ++i) + { + // Assign the ToolBar Button struct + TBBUTTON tbb = {0}; + tbb.idCommand = i + nMaxedOffset; // Each button needs a unique ID + tbb.fsState = TBSTATE_ENABLED; + tbb.fsStyle = TBSTYLE_BUTTON | TBSTYLE_AUTOSIZE | TBSTYLE_DROPDOWN; + tbb.iString = (INT_PTR)_T(" "); + SendMessage(TB_ADDBUTTONS, 1, (WPARAM)&tbb); + + // Add the menu title to the string table + std::vector<TCHAR> vMenuName( MAX_MENU_STRING+1, _T('\0') ); + TCHAR* szMenuName = &vMenuName[0]; + GetMenuString(hMenu, i, szMenuName, MAX_MENU_STRING, MF_BYPOSITION); + SetButtonText(i + nMaxedOffset, szMenuName); + } + } + + inline void CMenuBar::SetMenuBarTheme(MenuTheme& Theme) + { + m_ThemeMenu.UseThemes = Theme.UseThemes; + m_ThemeMenu.clrHot1 = Theme.clrHot1; + m_ThemeMenu.clrHot2 = Theme.clrHot2; + m_ThemeMenu.clrPressed1 = Theme.clrPressed1; + m_ThemeMenu.clrPressed2 = Theme.clrPressed2; + m_ThemeMenu.clrOutline = Theme.clrOutline; + + if (IsWindow()) + Invalidate(); + } + + inline LRESULT CALLBACK CMenuBar::StaticMsgHook(int nCode, WPARAM wParam, LPARAM lParam) + { + assert(GetApp()); + MSG* pMsg = (MSG*)lParam; + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + assert(pTLSData); + CMenuBar* pMenuBar = (CMenuBar*)pTLSData->pMenuBar; + + if (pMenuBar && (MSGF_MENU == nCode)) + { + // process menu message + if (pMenuBar->OnMenuInput(pMsg->message, pMsg->wParam, pMsg->lParam)) + { + return TRUE; + } + } + + return CallNextHookEx(pTLSData->hHook, nCode, wParam, lParam); + } + + inline void CMenuBar::OnSysCommand(WPARAM wParam, LPARAM lParam) + { + if (SC_KEYMENU == wParam) + { + if (0 == lParam) + { + // Alt/F10 key toggled + GrabFocus(); + m_bKeyMode = TRUE; + int nMaxedOffset = (IsMDIChildMaxed()? 1:0); + SetHotItem(nMaxedOffset); + } + else + // Handle key pressed with Alt held down + DoAltKey((WORD)lParam); + } + } + + inline LRESULT CMenuBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_CHAR: + return 0L; // Discard these messages + case WM_DRAWITEM: + m_pFrame->OnDrawItem(wParam, lParam); + return TRUE; // handled + case WM_EXITMENULOOP: + if (m_bExitAfter) + ExitMenu(); + m_pFrame->OnExitMenuLoop(); + break; + case WM_INITMENUPOPUP: + m_pFrame->OnInitMenuPopup(wParam, lParam); + break; + case WM_KEYDOWN: + OnKeyDown(wParam, lParam); + return 0L; // Discard these messages + case WM_KILLFOCUS: + ExitMenu(); + return 0L; + case WM_LBUTTONDOWN: + // Do default processing first + CallWindowProc(GetPrevWindowProc(), uMsg, wParam, lParam); + + OnLButtonDown(wParam, lParam); + return 0L; + case WM_LBUTTONUP: + OnLButtonUp(wParam, lParam); + break; + case WM_MEASUREITEM: + m_pFrame->OnMeasureItem(wParam, lParam); + return TRUE; // handled + case WM_MOUSELEAVE: + OnMouseLeave(); + break; + case WM_MOUSEMOVE: + OnMouseMove(wParam, lParam); + break; + case UWM_POPUPMENU: + DoPopupMenu(); + return 0L; + case WM_SYSKEYDOWN: + if ((VK_MENU == wParam) || (VK_F10 == wParam)) + return 0L; + break; + case WM_SYSKEYUP: + if ((VK_MENU == wParam) || (VK_F10 == wParam)) + { + ExitMenu(); + return 0L; + } + break; + case UWM_GETMENUTHEME: + { + MenuTheme& tm = GetMenuBarTheme(); + return (LRESULT)&tm; + } + case WM_WINDOWPOSCHANGED: + OnWindowPosChanged(); + break; + case WM_WINDOWPOSCHANGING: + // Bypass CToolBar::WndProcDefault for this message + return CWnd::WndProcDefault(uMsg, wParam, lParam); + + } // switch (uMsg) + + return CToolBar::WndProcDefault(uMsg, wParam, lParam); + } // LRESULT CMenuBar::WndProcDefault(...) + + + + /////////////////////////////////// + // Definitions for the CFrame class + // + inline CFrame::CFrame() : m_tsStatusText(_T("Ready")), m_bShowIndicatorStatus(TRUE), m_bShowMenuStatus(TRUE), + m_bUseReBar(FALSE), m_bUseThemes(TRUE), m_bUpdateTheme(FALSE), m_bUseToolBar(TRUE), m_bUseCustomDraw(TRUE), + m_bShowStatusBar(TRUE), m_bShowToolBar(TRUE), m_himlMenu(NULL), m_himlMenuDis(NULL), + m_AboutDialog(IDW_ABOUT), m_pView(NULL), m_nMaxMRU(0), m_hOldFocus(0), m_nOldID(-1) + { + ZeroMemory(&m_ThemeMenu, sizeof(m_ThemeMenu)); + + // Do either InitCommonControls or InitCommonControlsEx + LoadCommonControls(); + + // By default, we use the rebar if we can + if (GetComCtlVersion() >= 470) + m_bUseReBar = TRUE; + + for (int i = 0 ; i < 3 ; ++i) + m_OldStatus[i] = _T('\0'); + } + + inline CFrame::~CFrame() + { + if (m_himlMenu) ImageList_Destroy(m_himlMenu); + if (m_himlMenuDis) ImageList_Destroy(m_himlMenuDis); + } + + inline BOOL CFrame::AddMenuIcon(int nID_MenuItem, HICON hIcon, int cx /*= 16*/, int cy /*= 16*/) + { + // Get ImageList image size + int cxOld = 0; + int cyOld = 0; + ImageList_GetIconSize(m_himlMenu, &cxOld, &cyOld ); + + // Create a new ImageList if required + if ((cx != cxOld) || (cy != cyOld) || (NULL == m_himlMenu)) + { + if (m_himlMenu) ImageList_Destroy(m_himlMenu); + m_himlMenu = ImageList_Create(cx, cy, ILC_COLOR32 | ILC_MASK, 1, 0); + m_vMenuIcons.clear(); + } + + if (ImageList_AddIcon(m_himlMenu, hIcon) != -1) + { + m_vMenuIcons.push_back(nID_MenuItem); + + // Recreate the Disabled imagelist + if (m_himlMenuDis) ImageList_Destroy(m_himlMenuDis); + m_himlMenuDis = NULL; + m_himlMenuDis = CreateDisabledImageList(m_himlMenu); + + return TRUE; + } + + return FALSE; + } + + inline UINT CFrame::AddMenuIcons(const std::vector<UINT>& MenuData, COLORREF crMask, UINT ToolBarID, UINT ToolBarDisabledID) + // Adds the icons from a bitmap resouce to an internal ImageList for use with popup menu items. + // Note: If existing are a different size to the new ones, the old ones will be removed! + // The ToolBarDisabledID is ignored unless ToolBarID and ToolBarDisabledID bitmaps are the same size. + { + // Count the MenuData entries excluding seperators + int iImages = 0; + for (UINT i = 0 ; i < MenuData.size(); ++i) + { + if (MenuData[i] != 0) // Don't count seperators + { + ++iImages; + } + } + + // Load the button images from Resouce ID + CBitmap Bitmap(ToolBarID); + + if ((0 == iImages) || (!Bitmap)) + return (UINT)m_vMenuIcons.size(); // No valid images, so nothing to do! + + BITMAP bm = Bitmap.GetBitmapData(); + int iImageWidth = bm.bmWidth / iImages; + int iImageHeight = bm.bmHeight; + + // Create the ImageList if required + if (NULL == m_himlMenu) + { + m_himlMenu = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iImages, 0); + m_vMenuIcons.clear(); + } + else + { + int Oldcx; + int Oldcy; + + ImageList_GetIconSize(m_himlMenu, &Oldcx, &Oldcy); + if (iImageHeight != Oldcy) + { + TRACE(_T("Unable to add icons. The new icons are a different size to the old ones\n")); + return (UINT)m_vMenuIcons.size(); + } + } + + // Add the resource IDs to the m_vMenuIcons vector + for (UINT j = 0 ; j < MenuData.size(); ++j) + { + if (MenuData[j] != 0) + { + m_vMenuIcons.push_back(MenuData[j]); + } + } + + // Add the images to the ImageList + ImageList_AddMasked(m_himlMenu, Bitmap, crMask); + + // Create the Disabled imagelist + if (ToolBarDisabledID) + { + if (0 != m_himlMenuDis) + m_himlMenuDis = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iImages, 0); + + CBitmap BitmapDisabled(ToolBarDisabledID); + BITMAP bmDis = BitmapDisabled.GetBitmapData(); + + int iImageWidthDis = bmDis.bmWidth / iImages; + int iImageHeightDis = bmDis.bmHeight; + + // Normal and Disabled icons must be the same size + if ((iImageWidthDis == iImageWidth) && (iImageHeightDis == iImageHeight)) + { + ImageList_AddMasked(m_himlMenu, BitmapDisabled, crMask); + } + else + { + ImageList_Destroy(m_himlMenuDis); + m_himlMenuDis = CreateDisabledImageList(m_himlMenu); + } + } + else + { + if (m_himlMenuDis) ImageList_Destroy(m_himlMenuDis); + m_himlMenuDis = CreateDisabledImageList(m_himlMenu); + } + + // return the number of menu icons + return (UINT)m_vMenuIcons.size(); + } + + inline void CFrame::AddMenuBarBand() + { + // Adds a MenuBar to the rebar control + REBARBANDINFO rbbi = {0}; + CSize sz = GetMenuBar().GetMaxSize(); + + // Calculate the MenuBar height from the menu font + CSize csMenuBar; + CClientDC dcMenuBar(&GetMenuBar()); + dcMenuBar.SelectObject(GetMenuBar().GetFont()); + csMenuBar = dcMenuBar.GetTextExtentPoint32(_T("\tSomeText"), lstrlen(_T("\tSomeText"))); + int MenuBar_Height = csMenuBar.cy + 6; + + rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_SIZE | RBBIM_ID; + rbbi.cxMinChild = sz.cx; + rbbi.cx = sz.cx; + rbbi.cyMinChild = MenuBar_Height; + rbbi.cyMaxChild = MenuBar_Height; + rbbi.fStyle = RBBS_BREAK | RBBS_VARIABLEHEIGHT | RBBS_GRIPPERALWAYS ; + rbbi.hwndChild = GetMenuBar(); + rbbi.wID = IDW_MENUBAR; + + // Note: rbbi.cbSize is set inside the InsertBand function + GetReBar().InsertBand(-1, rbbi); + SetMenuBarBandSize(); + GetReBar().SetMenuBar(GetMenuBar()); + + if (GetReBar().GetReBarTheme().LockMenuBand) + GetReBar().ShowGripper(GetReBar().GetBand(GetMenuBar()), FALSE); + } + + inline void CFrame::AddMRUEntry(LPCTSTR szMRUEntry) + { + // Erase possible duplicate entries from vector + RemoveMRUEntry(szMRUEntry); + + // Insert the entry at the beginning of the vector + m_vMRUEntries.insert(m_vMRUEntries.begin(), szMRUEntry); + + // Delete excessive MRU entries + if (m_vMRUEntries.size() > m_nMaxMRU) + m_vMRUEntries.erase(m_vMRUEntries.begin() + m_nMaxMRU, m_vMRUEntries.end()); + + UpdateMRUMenu(); + } + + inline void CFrame::AddToolBarBand(CToolBar& TB, DWORD dwStyle, UINT nID) + { + // Adds a ToolBar to the rebar control + + // Create the ToolBar Window + TB.Create(&GetReBar()); + + // Fill the REBARBAND structure + REBARBANDINFO rbbi = {0}; + CSize sz = TB.GetMaxSize(); + + rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_STYLE | RBBIM_CHILD | RBBIM_SIZE | RBBIM_ID; + rbbi.cyMinChild = sz.cy; + rbbi.cyMaxChild = sz.cy; + rbbi.cx = sz.cx +2; + rbbi.cxMinChild = sz.cx +2; + + rbbi.fStyle = dwStyle; + rbbi.hwndChild = TB; + rbbi.wID = nID; + + // Note: rbbi.cbSize is set inside the InsertBand function + GetReBar().InsertBand(-1, rbbi); + } + + inline void CFrame::AddToolBarButton(UINT nID, BOOL bEnabled /* = TRUE*/, LPCTSTR szText) + // Adds Resource IDs to toolbar buttons. + // A resource ID of 0 is a separator + { + GetToolBar().AddButton(nID, bEnabled); + + if(0 != szText) + GetToolBar().SetButtonText(nID, szText); + + if (!IsWindow()) TRACE(_T("Warning ... Resource IDs for toolbars should be added in SetupToolBar\n")); + } + + inline void CFrame::AdjustFrameRect(RECT rcView) const + // Adjust the size of the frame to accommodate the View window's dimensions + { + // Adjust for the view styles + CRect rc = rcView; + DWORD dwStyle = (DWORD)GetView()->GetWindowLongPtr(GWL_STYLE); + DWORD dwExStyle = (DWORD)GetView()->GetWindowLongPtr(GWL_EXSTYLE); + AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle); + + // Calculate the new frame height + CRect rcFrameBefore = GetWindowRect(); + CRect rcViewBefore = GetViewRect(); + int Height = rc.Height() + rcFrameBefore.Height() - rcViewBefore.Height(); + + // Adjust for the frame styles + dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + dwExStyle = (DWORD)GetWindowLongPtr(GWL_EXSTYLE); + AdjustWindowRectEx(&rc, dwStyle, FALSE, dwExStyle); + + // Calculate final rect size, and reposition frame + SetWindowPos(NULL, 0, 0, rc.Width(), Height, SWP_NOMOVE); + } + + inline void CFrame::CreateToolBar() + { + if (IsReBarSupported() && m_bUseReBar) + AddToolBarBand(GetToolBar(), RBBS_BREAK, IDW_TOOLBAR); // Create the toolbar inside rebar + else + GetToolBar().Create(this); // Create the toolbar without a rebar + + SetupToolBar(); + + if (IsReBarSupported() && m_bUseReBar) + { + if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().LockMenuBand) + { + // Hide gripper for single toolbar + if (GetReBar().GetBandCount() <= 2) + GetReBar().ShowGripper(GetReBar().GetBand(GetToolBar()), FALSE); + } + } + + if (GetToolBar().GetToolBarData().size() > 0) + { + // Set the toolbar images (if not already set in SetupToolBar) + // A mask of 192,192,192 is compatible with AddBitmap (for Win95) + if (!GetToolBar().SendMessage(TB_GETIMAGELIST, 0L, 0L)) + SetToolBarImages(RGB(192,192,192), IDW_MAIN, 0, 0); + + // Add the icons for popup menu + AddMenuIcons(GetToolBar().GetToolBarData(), RGB(192, 192, 192), IDW_MAIN, 0); + } + else + { + TRACE(_T("Warning ... No resource IDs assigned to the toolbar\n")); + } + } + + inline void CFrame::DrawCheckmark(LPDRAWITEMSTRUCT pdis, CDC& DrawDC) + // Draws the checkmark or radiocheck transparently + { + CRect rc = pdis->rcItem; + UINT fType = ((ItemData*)pdis->itemData)->fType; + MenuTheme tm = GetMenuTheme(); + CRect rcBk; + + // Draw the checkmark's background rectangle first + int Iconx = 16, Icony = 16; + if (m_himlMenu) ImageList_GetIconSize(m_himlMenu, &Iconx, &Icony); + int BarWidth = Iconx + 8; + int left = (BarWidth - Iconx)/2; + int top = rc.top + (rc.Height() - Icony)/2; + rcBk.SetRect(left, top, left + Iconx, top + Icony); + + if (tm.UseThemes) + { + DrawDC.CreateSolidBrush(tm.clrHot2); + DrawDC.CreatePen(PS_SOLID, 1, tm.clrOutline); + + // Draw the checkmark's background rectangle + DrawDC.Rectangle(rcBk.left, rcBk.top, rcBk.right, rcBk.bottom); + } + + CMemDC MemDC(FromHandle(pdis->hDC)); + int cxCheck = ::GetSystemMetrics(SM_CXMENUCHECK); + int cyCheck = ::GetSystemMetrics(SM_CYMENUCHECK); + MemDC.CreateBitmap(cxCheck, cyCheck, 1, 1, NULL); + CRect rcCheck( 0, 0, cxCheck, cyCheck); + + // Copy the check mark bitmap to hdcMem + if (MFT_RADIOCHECK == fType) + MemDC.DrawFrameControl(rcCheck, DFC_MENU, DFCS_MENUBULLET); + else + MemDC.DrawFrameControl(rcCheck, DFC_MENU, DFCS_MENUCHECK); + + int xoffset = (rcBk.Width() - rcCheck.Width()-1)/2; + int yoffset = (rcBk.Height() - rcCheck.Height()-1)/2; + + if (tm.UseThemes) + xoffset += 2; + + // Draw a white or black check mark as required + // Unfortunately MaskBlt isn't supported on Win95, 98 or ME, so we do it the hard way + CMemDC MaskDC(FromHandle(pdis->hDC)); + MaskDC.CreateCompatibleBitmap(FromHandle(pdis->hDC), cxCheck, cyCheck); + MaskDC.BitBlt(0, 0, cxCheck, cyCheck, &MaskDC, 0, 0, WHITENESS); + + if ((pdis->itemState & ODS_SELECTED) && (!tm.UseThemes)) + { + // Draw a white checkmark + MemDC.BitBlt(0, 0, cxCheck, cyCheck, &MemDC, 0, 0, DSTINVERT); + MaskDC.BitBlt(0, 0, cxCheck, cyCheck, &MemDC, 0, 0, SRCAND); + DrawDC.BitBlt(rcBk.left + xoffset, rcBk.top + yoffset, cxCheck, cyCheck, &MaskDC, 0, 0, SRCPAINT); + } + else + { + // Draw a black checkmark + int BullitOffset = ((MFT_RADIOCHECK == fType) && tm.UseThemes)? 1 : 0; + MaskDC.BitBlt( -BullitOffset, BullitOffset, cxCheck, cyCheck, &MemDC, 0, 0, SRCAND); + DrawDC.BitBlt(rcBk.left + xoffset, rcBk.top + yoffset, cxCheck, cyCheck, &MaskDC, 0, 0, SRCAND); + } + } + + inline void CFrame::DrawMenuIcon(LPDRAWITEMSTRUCT pdis, CDC& DrawDC, BOOL bDisabled) + { + if (!m_himlMenu) + return; + // Get icon size + int Iconx; + int Icony; + ImageList_GetIconSize(m_himlMenu, &Iconx, &Icony); + int BarWidth = Iconx + 8; + + // get the drawing rectangle + CRect rc = pdis->rcItem; + int left = (BarWidth - Iconx)/2; + int top = rc.top + (rc.Height() - Icony)/2; + rc.SetRect(left, top, left + Iconx, top + Icony); + + // get the icon's location in the imagelist + int iImage = -1; + for (int i = 0 ; i < (int)m_vMenuIcons.size(); ++i) + { + if (pdis->itemID == m_vMenuIcons[i]) + iImage = i; + } + + // draw the image + if (iImage >= 0 ) + { + if ((bDisabled) && (m_himlMenuDis)) + ImageList_Draw(m_himlMenuDis, iImage, DrawDC, rc.left, rc.top, ILD_TRANSPARENT); + else + ImageList_Draw(m_himlMenu, iImage, DrawDC, rc.left, rc.top, ILD_TRANSPARENT); + } + } + + inline void CFrame::DrawMenuText(CDC& DrawDC, LPCTSTR ItemText, CRect& rc, COLORREF colorText) + { + // find the position of tab character + int nTab = -1; + for(int i = 0; i < lstrlen(ItemText); ++i) + { + if(_T('\t') == ItemText[i]) + { + nTab = i; + break; + } + } + + // Draw the item text + DrawDC.SetTextColor(colorText); + DrawDC.DrawText(ItemText, nTab, rc, DT_SINGLELINE | DT_LEFT | DT_VCENTER); + + // Draw text after tab, right aligned + if(nTab != -1) + DrawDC.DrawText( &ItemText[nTab + 1], -1, rc, DT_SINGLELINE | DT_RIGHT | DT_VCENTER); + } + + inline int CFrame::GetMenuItemPos(HMENU hMenu, LPCTSTR szItem) + // Returns the position of the menu item, given it's name + { + int nMenuItemCount = GetMenuItemCount(hMenu); + MENUITEMINFO mii = {0}; + mii.cbSize = GetSizeofMenuItemInfo(); + + for (int nItem = 0 ; nItem < nMenuItemCount; ++nItem) + { + std::vector<TCHAR> vTChar( MAX_MENU_STRING+1, _T('\0') ); + TCHAR* szStr = &vTChar[0]; + + std::vector<TCHAR> vStripped( MAX_MENU_STRING+1, _T('\0') ); + TCHAR* szStripped = &vStripped[0]; + + mii.fMask = MIIM_TYPE; + mii.fType = MFT_STRING; + mii.dwTypeData = szStr; + mii.cch = MAX_MENU_STRING; + + // Fill the contents of szStr from the menu item + if (::GetMenuItemInfo(hMenu, nItem, TRUE, &mii) && (lstrlen(szStr) <= MAX_MENU_STRING)) + { + // Strip out any & characters + int j = 0; + for (int i = 0; i < lstrlen(szStr); ++i) + { + if (szStr[i] != _T('&')) + szStripped[j++] = szStr[i]; + } + szStripped[j] = _T('\0'); // Append null tchar + + // Compare the strings + if (0 == lstrcmp(szStripped, szItem)) + return nItem; + } + } + + return -1; + } + + inline tString CFrame::GetMRUEntry(UINT nIndex) + { + tString tsPathName; + if (nIndex < m_vMRUEntries.size()) + { + tsPathName = m_vMRUEntries[nIndex]; + + // Now put the selected entry at Index 0 + AddMRUEntry(tsPathName.c_str()); + } + return tsPathName; + } + + inline CRect CFrame::GetViewRect() const + { + // Get the frame's client area + CRect rcFrame = GetClientRect(); + + // Get the statusbar's window area + CRect rcStatus; + if (GetStatusBar().IsWindowVisible() || !IsWindowVisible()) + rcStatus = GetStatusBar().GetWindowRect(); + + // Get the top rebar or toolbar's window area + CRect rcTop; + if (IsReBarSupported() && m_bUseReBar) + rcTop = GetReBar().GetWindowRect(); + else + if (GetToolBar().IsWindow() && GetToolBar().IsWindowVisible()) + rcTop = GetToolBar().GetWindowRect(); + + // Return client size less the rebar and status windows + int top = rcFrame.top + rcTop.Height(); + int left = rcFrame.left; + int right = rcFrame.right; + int bottom = rcFrame.Height() - (rcStatus.Height()); + if ((bottom <= top) ||( right <= left)) + top = left = right = bottom = 0; + + CRect rcView(left, top, right, bottom); + return rcView; + } + + inline void CFrame::LoadCommonControls() + { + HMODULE hComCtl; + + try + { + // Load the Common Controls DLL + hComCtl = ::LoadLibrary(_T("COMCTL32.DLL")); + if (!hComCtl) + throw CWinException(_T("Failed to load COMCTL32.DLL")); + + if (GetComCtlVersion() > 470) + { + // Declare a pointer to the InItCommonControlsEx function + typedef BOOL WINAPI INIT_EX(INITCOMMONCONTROLSEX*); + INIT_EX* pfnInit = (INIT_EX*)::GetProcAddress(hComCtl, "InitCommonControlsEx"); + + // Load the full set of common controls + INITCOMMONCONTROLSEX InitStruct = {0}; + InitStruct.dwSize = sizeof(INITCOMMONCONTROLSEX); + InitStruct.dwICC = ICC_COOL_CLASSES|ICC_DATE_CLASSES|ICC_INTERNET_CLASSES|ICC_NATIVEFNTCTL_CLASS| + ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES|ICC_WIN95_CLASSES; + + // Call InitCommonControlsEx + if(!((*pfnInit)(&InitStruct))) + throw CWinException(_T("InitCommonControlsEx failed")); + } + else + { + ::InitCommonControls(); + } + + ::FreeLibrary(hComCtl); + } + + catch (const CWinException &e) + { + e.what(); + if (hComCtl) + ::FreeLibrary(hComCtl); + + throw; + } + } + + inline BOOL CFrame::LoadRegistryMRUSettings(UINT nMaxMRU /*= 0*/) + { + // Load the MRU list from the registry + + assert(!m_tsKeyName.empty()); // KeyName must be set before calling LoadRegistryMRUSettings + HKEY hKey = NULL; + BOOL bRet = FALSE; + + try + { + m_nMaxMRU = MIN(nMaxMRU, 16); + std::vector<tString> vMRUEntries; + tString tsKey = _T("Software\\") + m_tsKeyName + _T("\\Recent Files"); + + if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey)) + { + for (UINT i = 0; i < m_nMaxMRU; ++i) + { + DWORD dwType = REG_SZ; + DWORD dwBufferSize = 0; + TCHAR szSubKey[10] = _T(""); + wsprintf(szSubKey, _T("File %d\0"), i+1); + + if (ERROR_SUCCESS != RegQueryValueEx(hKey, szSubKey, NULL, &dwType, NULL, &dwBufferSize)) + throw CWinException(_T("RegQueryValueEx failed\n")); + + std::vector<TCHAR> PathName( dwBufferSize, _T('\0') ); + TCHAR* pTCharArray = &PathName[0]; + + // load the entry from the registry + if (ERROR_SUCCESS != RegQueryValueEx(hKey, szSubKey, NULL, &dwType, (LPBYTE)pTCharArray, &dwBufferSize)) + throw CWinException(_T("RegQueryValueEx failed\n")); + + if ( lstrlen( pTCharArray ) ) + vMRUEntries.push_back( pTCharArray ); + } + + // successfully loaded all MRU values, so store them + m_vMRUEntries = vMRUEntries; + RegCloseKey(hKey); + bRet = TRUE; + } + } + + catch(const CWinException& e) + { + TRACE(_T("Failed to load MRU values from registry\n")); + e.what(); + + if (hKey) + RegCloseKey(hKey); + } + + return bRet; + } + + inline BOOL CFrame::LoadRegistrySettings(LPCTSTR szKeyName) + { + assert (NULL != szKeyName); + m_tsKeyName = szKeyName; + + tString tsKey = _T("Software\\") + m_tsKeyName + _T("\\Frame Settings"); + HKEY hKey = 0; + BOOL bRet = FALSE; + + try + { + if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey)) + { + DWORD dwType = REG_BINARY; + DWORD BufferSize = sizeof(DWORD); + DWORD dwTop, dwLeft, dwWidth, dwHeight, dwStatusBar, dwToolBar; + if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("Top"), NULL, &dwType, (LPBYTE)&dwTop, &BufferSize)) + throw CWinException(_T("RegQueryValueEx Failed")); + if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("Left"), NULL, &dwType, (LPBYTE)&dwLeft, &BufferSize)) + throw CWinException(_T("RegQueryValueEx Failed")); + if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("Width"), NULL, &dwType, (LPBYTE)&dwWidth, &BufferSize)) + throw CWinException(_T("RegQueryValueEx Failed")); + if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("Height"), NULL, &dwType, (LPBYTE)&dwHeight, &BufferSize)) + throw CWinException(_T("RegQueryValueEx Failed")); + if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("StatusBar"), NULL, &dwType, (LPBYTE)&dwStatusBar, &BufferSize)) + throw CWinException(_T("RegQueryValueEx Failed")); + if (ERROR_SUCCESS != RegQueryValueEx(hKey, _T("ToolBar"), NULL, &dwType, (LPBYTE)&dwToolBar, &BufferSize)) + throw CWinException(_T("RegQueryValueEx Failed")); + + m_rcPosition.top = dwTop; + m_rcPosition.left = dwLeft; + m_rcPosition.bottom = m_rcPosition.top + dwHeight; + m_rcPosition.right = m_rcPosition.left + dwWidth; + m_bShowStatusBar = dwStatusBar & 1; + m_bShowToolBar = dwToolBar & 1; + + RegCloseKey(hKey); + bRet = TRUE; + } + } + + catch (const CWinException& e) + { + TRACE(_T("Failed to load values from registry, using defaults!\n")); + e.what(); + + if (hKey) + RegCloseKey(hKey); + } + + return bRet; + } + + inline void CFrame::OnActivate(WPARAM wParam, LPARAM lParam) + { + // Do default processing first + DefWindowProc(WM_ACTIVATE, wParam, lParam); + + if (LOWORD(wParam) == WA_INACTIVE) + { + // Save the hwnd of the window which currently has focus + // (this must be CFrame window itself or a child window + if (!IsIconic()) m_hOldFocus = ::GetFocus(); + + // Send a notification to the view window + int idCtrl = ::GetDlgCtrlID(m_hOldFocus); + NMHDR nhdr={0}; + nhdr.hwndFrom = m_hOldFocus; + nhdr.idFrom = idCtrl; + nhdr.code = UWM_FRAMELOSTFOCUS; + if (GetView()->IsWindow()) + GetView()->SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr); + } + else + { + // Now set the focus to the appropriate child window + if (m_hOldFocus) ::SetFocus(m_hOldFocus); + + // Send a notification to the view window + int idCtrl = ::GetDlgCtrlID(m_hOldFocus); + NMHDR nhdr={0}; + nhdr.hwndFrom = m_hOldFocus; + nhdr.idFrom = idCtrl; + nhdr.code = UWM_FRAMEGOTFOCUS; + if (GetView()->IsWindow()) + GetView()->SendMessage(WM_NOTIFY, (WPARAM)idCtrl, (LPARAM)&nhdr); + } + } + + inline void CFrame::OnClose() + { + // Called in response to a WM_CLOSE message for the frame. + ShowWindow(SW_HIDE); + SaveRegistrySettings(); + + GetMenuBar().Destroy(); + GetToolBar().Destroy(); + GetReBar().Destroy(); + GetStatusBar().Destroy(); + GetView()->Destroy(); + } + + inline void CFrame::OnCreate() + { + // This is called when the frame window is being created. + // Override this in CMainFrame if you wish to modify what happens here + + // Set the icon + SetIconLarge(IDW_MAIN); + SetIconSmall(IDW_MAIN); + + // Set the keyboard accelerators + m_hAccel = LoadAccelerators(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_MAIN)); + GetApp()->SetAccelerators(m_hAccel, this); + + // Set the Caption + SetWindowText(LoadString(IDW_MAIN)); + + // Set the theme for the frame elements + SetTheme(); + + // Create the rebar and menubar + if (IsReBarSupported() && m_bUseReBar) + { + // Create the rebar + GetReBar().Create(this); + + // Create the menu inside rebar + GetMenuBar().Create(&GetReBar()); + AddMenuBarBand(); + } + + // Setup the menu + SetFrameMenu(IDW_MAIN); + UpdateMRUMenu(); + + // Create the ToolBar + if (m_bUseToolBar) + { + CreateToolBar(); + ShowToolBar(m_bShowToolBar); + } + else + { + ::CheckMenuItem(GetFrameMenu(), IDW_VIEW_TOOLBAR, MF_UNCHECKED); + ::EnableMenuItem(GetFrameMenu(), IDW_VIEW_TOOLBAR, MF_GRAYED); + } + + // Create the status bar + GetStatusBar().Create(this); + ShowStatusBar(m_bShowStatusBar); + + // Create the view window + assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window + GetView()->Create(this); + + // Disable XP themes for the menubar + if ( m_bUseThemes || (GetWinVersion() < 2600) ) // themes or WinVersion < Vista + GetMenuBar().SetWindowTheme(L" ", L" "); + + // Start timer for Status updates + if (m_bShowIndicatorStatus || m_bShowMenuStatus) + SetTimer(ID_STATUS_TIMER, 200, NULL); + + // Reposition the child windows + RecalcLayout(); + } + + inline void CFrame::OnDestroy() + { + SetMenu(NULL); + KillTimer(ID_STATUS_TIMER); + ::PostQuitMessage(0); // Terminates the application + } + + inline LRESULT CFrame::OnDrawItem(WPARAM wParam, LPARAM lParam) + // OwnerDraw is used to render the popup menu items + { + LPDRAWITEMSTRUCT pdis = (LPDRAWITEMSTRUCT) lParam; + if (pdis->CtlType != ODT_MENU) + return CWnd::WndProcDefault(WM_DRAWITEM, wParam, lParam); + + CRect rc = pdis->rcItem; + ItemData* pmd = (ItemData*)pdis->itemData; + CDC* pDrawDC = FromHandle(pdis->hDC); + MenuTheme tm = GetMenuTheme(); + + int Iconx = 16; + int Icony = 16; + if (m_himlMenu) ImageList_GetIconSize(m_himlMenu, &Iconx, &Icony); + int BarWidth = tm.UseThemes? Iconx + 8 : 0; + + // Draw the side bar + if (tm.UseThemes) + { + CRect rcBar = rc; + rcBar.right = BarWidth; + pDrawDC->GradientFill(tm.clrPressed1, tm.clrPressed2, rcBar, TRUE); + } + + if (pmd->fType & MFT_SEPARATOR) + { + // draw separator + CRect rcSep = rc; + rcSep.left = BarWidth; + if (tm.UseThemes) + pDrawDC->SolidFill(RGB(255,255,255), rcSep); + else + pDrawDC->SolidFill(GetSysColor(COLOR_MENU), rcSep); + rcSep.top += (rc.bottom - rc.top)/2; + rcSep.left = BarWidth + 2; + pDrawDC->DrawEdge(rcSep, EDGE_ETCHED, BF_TOP); + } + else + { + // draw menu item + BOOL bDisabled = pdis->itemState & ODS_GRAYED; + BOOL bSelected = pdis->itemState & ODS_SELECTED; + BOOL bChecked = pdis->itemState & ODS_CHECKED; + CRect rcDraw = rc; + + if ((bSelected) && (!bDisabled)) + { + // draw selected item background + if (tm.UseThemes) + { + pDrawDC->CreateSolidBrush(tm.clrHot1); + pDrawDC->CreatePen(PS_SOLID, 1, tm.clrOutline); + pDrawDC->Rectangle(rcDraw.left, rcDraw.top, rcDraw.right, rcDraw.bottom); + } + else + pDrawDC->SolidFill(GetSysColor(COLOR_HIGHLIGHT), rcDraw); + } + else + { + // draw non-selected item background + rcDraw.left = BarWidth; + if (tm.UseThemes) + pDrawDC->SolidFill(RGB(255,255,255), rcDraw); + else + pDrawDC->SolidFill(GetSysColor(COLOR_MENU), rcDraw); + } + + if (bChecked) + DrawCheckmark(pdis, *pDrawDC); + else + DrawMenuIcon(pdis, *pDrawDC, bDisabled); + + // Calculate the text rect size + rc.left = rc.bottom - rc.top + 2; + if (_tcschr(pmd->GetItemText(), _T('\t'))) + rc.right -= POST_TEXT_GAP; // Add POST_TEXT_GAP if the text includes a tab + + // Draw the text + int iMode = pDrawDC->SetBkMode(TRANSPARENT); + COLORREF colorText; + if (tm.UseThemes) + { + rc.left += 8; + colorText = GetSysColor(bDisabled ? COLOR_GRAYTEXT : COLOR_MENUTEXT); + } + else + colorText = GetSysColor(bDisabled ? COLOR_GRAYTEXT : bSelected ? COLOR_HIGHLIGHTTEXT : COLOR_MENUTEXT); + + DrawMenuText(*pDrawDC, pmd->GetItemText(), rc, colorText); + pDrawDC->SetBkMode(iMode); + } + + pDrawDC->Detach(); // Optional, deletes GDI objects sooner + return TRUE; + } + + inline void CFrame::OnExitMenuLoop() + { + if (m_bUseCustomDraw) + { + for (UINT nItem = 0; nItem < m_vMenuItemData.size(); ++nItem) + { + // Undo OwnerDraw and put the text back + MENUITEMINFO mii = {0}; + mii.cbSize = GetSizeofMenuItemInfo(); + + mii.fMask = MIIM_TYPE | MIIM_DATA; + mii.fType = m_vMenuItemData[nItem]->fType; + mii.dwTypeData = m_vMenuItemData[nItem]->GetItemText(); + mii.cch = lstrlen(m_vMenuItemData[nItem]->GetItemText()); + mii.dwItemData = 0; + ::SetMenuItemInfo(m_vMenuItemData[nItem]->hMenu, m_vMenuItemData[nItem]->nPos, TRUE, &mii); + } + + m_vMenuItemData.clear(); + } + } + + inline void CFrame::OnHelp() + { + // Ensure only one dialog displayed even for multiple hits of the F1 button + if (!m_AboutDialog.IsWindow()) + { + // Store the window handle that currently has keyboard focus + HWND hPrevFocus = ::GetFocus(); + if (hPrevFocus == GetMenuBar()) + hPrevFocus = m_hWnd; + + m_AboutDialog.SetDlgParent(this); + m_AboutDialog.DoModal(); + + ::SetFocus(hPrevFocus); + } + } + + inline void CFrame::OnInitMenuPopup(WPARAM wParam, LPARAM lParam) + { + // The system menu shouldn't be owner drawn + if (HIWORD(lParam)) return; + + if (m_bUseCustomDraw) + { + CMenu* pMenu = FromHandle((HMENU)wParam); + + for (UINT i = 0; i < pMenu->GetMenuItemCount(); ++i) + { + MENUITEMINFO mii = {0}; + mii.cbSize = GetSizeofMenuItemInfo(); + + TCHAR szMenuItem[MAX_MENU_STRING] = _T(""); + + // Use old fashioned MIIM_TYPE instead of MIIM_FTYPE for MS VC6 compatibility + mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_SUBMENU; + mii.dwTypeData = szMenuItem; + mii.cch = MAX_MENU_STRING -1; + + // Send message for menu updates + UINT menuItem = pMenu->GetMenuItemID(i); + SendMessage(UWM_UPDATE_COMMAND, (WPARAM)menuItem, 0); + + // Specify owner-draw for the menu item type + if (pMenu->GetMenuItemInfo(i, &mii, TRUE)) + { + if (0 == mii.dwItemData) + { + ItemData* pItem = new ItemData; // deleted in OnExitMenuLoop + pItem->hMenu = *pMenu; + pItem->nPos = i; + pItem->fType = mii.fType; + pItem->hSubMenu = mii.hSubMenu; + mii.fType |= MFT_OWNERDRAW; + lstrcpyn(pItem->GetItemText(), szMenuItem, MAX_MENU_STRING); + mii.dwItemData = (DWORD_PTR)pItem; + + m_vMenuItemData.push_back(ItemDataPtr(pItem)); // Store pItem in m_vMenuItemData + pMenu->SetMenuItemInfo(i, &mii, TRUE); // Store pItem in mii + } + } + } + } + } + + inline LRESULT CFrame::OnMeasureItem(WPARAM wParam, LPARAM lParam) + // Called before the Popup menu is displayed, so that the MEASUREITEMSTRUCT + // values can be assigned with the menu item's dimensions. + { + LPMEASUREITEMSTRUCT pmis = (LPMEASUREITEMSTRUCT) lParam; + if (pmis->CtlType != ODT_MENU) + return CWnd::WndProcDefault(WM_MEASUREITEM, wParam, lParam); + + ItemData* pmd = (ItemData *) pmis->itemData; + assert(::IsMenu(pmd->hMenu)); // Does itemData contain a valid ItemData struct? + MenuTheme tm = GetMenuTheme(); + + if (pmd->fType & MFT_SEPARATOR) + { + pmis->itemHeight = 7; + pmis->itemWidth = 0; + } + + else + { + CClientDC DesktopDC(NULL); + + // Get the font used in menu items + NONCLIENTMETRICS nm = {0}; + nm.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(nm), &nm, 0); + // Default menu items are bold, so take this into account + if ((int)::GetMenuDefaultItem(pmd->hMenu, TRUE, GMDI_USEDISABLED) != -1) + nm.lfMenuFont.lfWeight = FW_BOLD; + + TCHAR* pItemText = &(pmd->vItemText[0]); + DesktopDC.CreateFontIndirect(&nm.lfMenuFont); + + // Calculate the size of the text + CSize size = DesktopDC.GetTextExtentPoint32(pItemText, lstrlen(pItemText)); + + // Calculate the size of the icon + int Iconx = 16; + int Icony = 16; + if (m_himlMenu) ImageList_GetIconSize(m_himlMenu, &Iconx, &Icony); + + pmis->itemHeight = 2+ MAX(MAX(size.cy, GetSystemMetrics(SM_CYMENU)-2), Icony+2); + pmis->itemWidth = size.cx + MAX(::GetSystemMetrics(SM_CXMENUSIZE), Iconx+2); + + // Allow extra width if the text includes a tab + if (_tcschr(pItemText, _T('\t'))) + pmis->itemWidth += POST_TEXT_GAP; + + // Allow extra width if the menu item has a sub menu + if (pmd->hSubMenu) + pmis->itemWidth += 10; + + // Allow extra width for themed menu + if (tm.UseThemes) + pmis->itemWidth += 8; + } + return TRUE; + } + + inline LRESULT CFrame::OnMenuChar(WPARAM wParam, LPARAM lParam) + { + if ((IsMenuBarUsed()) && (LOWORD(wParam)!= VK_SPACE)) + { + // Activate MenuBar for key pressed with Alt key held down + GetMenuBar().OnMenuChar(wParam, lParam); + return -1L; + } + return CWnd::WndProcDefault(WM_MENUCHAR, wParam, lParam); + } + + inline void CFrame::OnMenuSelect(WPARAM wParam, LPARAM lParam) + { + // Set the StatusBar text when we hover over a menu + // Only popup submenus have status strings + if (m_bShowMenuStatus) + { + int nID = LOWORD (wParam); + CMenu* pMenu = FromHandle((HMENU) lParam); + + if ((pMenu != GetMenu()) && (nID != 0) && !(HIWORD(wParam) & MF_POPUP)) + m_tsStatusText = LoadString(nID); + else + m_tsStatusText = _T("Ready"); + + SetStatusText(); + } + } + + inline LRESULT CFrame::OnNotify(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + case UWM_UNDOCKED: + m_hOldFocus = 0; + break; + case RBN_HEIGHTCHANGE: + RecalcLayout(); + Invalidate(); + break; + // case RBN_LAYOUTCHANGED: + // if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().BandsLeft) + // GetReBar().MoveBandsLeft(); + // break; + case RBN_MINMAX: + if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().ShortBands) + return 1L; // Supress maximise or minimise rebar band + break; + + // Display tooltips for the toolbar + case TTN_GETDISPINFO: + if (GetToolBar().IsWindow()) + { + CToolBar* pToolBar = 0; + if (IsReBarUsed()) + { + // Get the ToolBar's CWnd + CWnd* pWnd = FromHandle(GetReBar().HitTest(GetCursorPos())); + if (pWnd && (lstrcmp(pWnd->GetClassName(), _T("ToolbarWindow32")) == 0)) + { + pToolBar = (CToolBar*)pWnd; + } + } + + if (pToolBar) + { + LPNMTTDISPINFO lpDispInfo = (LPNMTTDISPINFO)lParam; + int iIndex = pToolBar->HitTest(); + if (iIndex >= 0) + { + int nID = pToolBar->GetCommandID(iIndex); + if (nID > 0) + { + m_tsTooltip = LoadString(nID); + lpDispInfo->lpszText = (LPTSTR)m_tsTooltip.c_str(); + } + else + m_tsTooltip = _T(""); + } + } + } + break; + } // switch LPNMHDR + + return 0L; + + } // CFrame::Onotify(...) + + inline void CFrame::OnSetFocus() + { + SetStatusText(); + } + + inline void CFrame::OnSysColorChange() + { + // Honour theme color changes + for (int nBand = 0; nBand <= GetReBar().GetBandCount(); ++nBand) + { + GetReBar().SetBandColor(nBand, GetSysColor(COLOR_BTNTEXT), GetSysColor(COLOR_BTNFACE)); + } + + // Update the status bar font and text + NONCLIENTMETRICS nm = {0}; + nm.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo (SPI_GETNONCLIENTMETRICS, 0, &nm, 0); + LOGFONT lf = nm.lfStatusFont; + CFont* pFont = FromHandle(CreateFontIndirect(&lf)); + GetStatusBar().SetFont(pFont, FALSE); + SetStatusText(); + + if ((m_bUpdateTheme) && (m_bUseThemes)) SetTheme(); + + // Reposition and redraw everything + RecalcLayout(); + RedrawWindow(NULL, NULL, RDW_ERASE | RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); + + // Forward the message to the view window + m_pView->PostMessage(WM_SYSCOLORCHANGE, 0L, 0L); + } + + inline LRESULT CFrame::OnSysCommand(WPARAM wParam, LPARAM lParam) + { + if ((SC_KEYMENU == wParam) && (VK_SPACE != lParam) && IsMenuBarUsed()) + { + GetMenuBar().OnSysCommand(wParam, lParam); + return 0L; + } + + if (SC_MINIMIZE == wParam) + m_hOldFocus = ::GetFocus(); + + return CWnd::WndProcDefault(WM_SYSCOMMAND, wParam, lParam); + } + + inline void CFrame::OnTimer(WPARAM wParam) + { + if (ID_STATUS_TIMER == wParam) + { + if (m_bShowMenuStatus) + { + // Get the toolbar the point is over + CToolBar* pToolBar = 0; + if (IsReBarUsed()) + { + // Get the ToolBar's CWnd + CWnd* pWnd = FromHandle(GetReBar().HitTest(GetCursorPos())); + if (pWnd && (dynamic_cast<CToolBar*>(pWnd)) && !(dynamic_cast<CMenuBar*>(pWnd))) + pToolBar = (CToolBar*)pWnd; + } + else + { + CPoint pt = GetCursorPos(); + CWnd* pWnd = WindowFromPoint(GetCursorPos()); + if (pWnd && (dynamic_cast<CToolBar*>(pWnd))) + pToolBar = (CToolBar*)pWnd; + } + + if ((pToolBar) && (WindowFromPoint(GetCursorPos()) == pToolBar)) + { + // Which toolbar button is the mouse cursor hovering over? + int nButton = pToolBar->HitTest(); + if (nButton >= 0) + { + int nID = pToolBar->GetCommandID(nButton); + // Only update the statusbar if things have changed + if (nID != m_nOldID) + { + if (nID != 0) + m_tsStatusText = LoadString(nID); + else + m_tsStatusText = _T("Ready"); + + if (GetStatusBar().IsWindow()) + SetStatusText(); + } + m_nOldID = nID; + } + } + else + { + if (m_nOldID != -1) + { + m_tsStatusText = _T("Ready"); + SetStatusText(); + } + m_nOldID = -1; + } + } + + if (m_bShowIndicatorStatus) + SetStatusIndicators(); + } + } + + inline void CFrame::OnViewStatusBar() + { + m_bShowStatusBar = !m_bShowStatusBar; + ShowStatusBar(m_bShowStatusBar); + } + + inline void CFrame::OnViewToolBar() + { + m_bShowToolBar = !m_bShowToolBar; + ShowToolBar(m_bShowToolBar); + } + + inline void CFrame::PreCreate(CREATESTRUCT& cs) + { + // Set the frame window styles + cs.style = WS_OVERLAPPEDWINDOW | WS_VISIBLE; + + // Set the original window position + cs.x = m_rcPosition.left; + cs.y = m_rcPosition.top; + cs.cx = m_rcPosition.Width(); + cs.cy = m_rcPosition.Height(); + } + + inline void CFrame::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = _T("Win32++ Frame"); + } + + inline void CFrame::RecalcLayout() + { + CWnd* pView = GetView(); + if ((!pView) || (!pView->GetHwnd())) + return; + + // Resize the status bar + if (GetStatusBar().IsWindow() && m_bShowStatusBar) + { + GetStatusBar().SetWindowPos(NULL, 0, 0, 0, 0, SWP_SHOWWINDOW); + GetStatusBar().Invalidate(); + SetStatusText(); + } + + // Resize the rebar or toolbar + if (IsReBarUsed()) + { + GetReBar().SendMessage(WM_SIZE, 0L, 0L); + GetReBar().Invalidate(); + } + else if (m_bUseToolBar && m_bShowToolBar) + GetToolBar().SendMessage(TB_AUTOSIZE, 0L, 0L); + + // Resize the View window + CRect rClient = GetViewRect(); + if ((rClient.bottom - rClient.top) >= 0) + { + int x = rClient.left; + int y = rClient.top; + int cx = rClient.Width(); + int cy = rClient.Height(); + + pView->SetWindowPos( NULL, x, y, cx, cy, SWP_SHOWWINDOW|SWP_ASYNCWINDOWPOS ); + } + + // Adjust rebar bands + if (IsReBarUsed()) + { + if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().BandsLeft) + GetReBar().MoveBandsLeft(); + + if (IsMenuBarUsed()) + SetMenuBarBandSize(); + } + } + + inline void CFrame::RemoveMRUEntry(LPCTSTR szMRUEntry) + { + std::vector<tString>::iterator it; + for (it = m_vMRUEntries.begin(); it != m_vMRUEntries.end(); ++it) + { + if ((*it) == szMRUEntry) + { + m_vMRUEntries.erase(it); + break; + } + } + + UpdateMRUMenu(); + } + + inline BOOL CFrame::SaveRegistrySettings() + { + // Store the window position in the registry + if (!m_tsKeyName.empty()) + { + tString tsKeyName = _T("Software\\") + m_tsKeyName + _T("\\Frame Settings"); + HKEY hKey = NULL; + + try + { + if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) + throw CWinException(_T("RegCreateKeyEx failed")); + + WINDOWPLACEMENT Wndpl = {0}; + Wndpl.length = sizeof(WINDOWPLACEMENT); + + if (GetWindowPlacement(Wndpl)) + { + // Get the Frame's window position + CRect rc = Wndpl.rcNormalPosition; + DWORD dwTop = MAX(rc.top, 0); + DWORD dwLeft = MAX(rc.left, 0); + DWORD dwWidth = MAX(rc.Width(), 100); + DWORD dwHeight = MAX(rc.Height(), 50); + + if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("Top"), 0, REG_DWORD, (LPBYTE)&dwTop, sizeof(DWORD))) + throw CWinException(_T("RegSetValueEx failed")); + if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("Left"), 0, REG_DWORD, (LPBYTE)&dwLeft, sizeof(DWORD))) + throw CWinException(_T("RegSetValueEx failed")); + if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("Width"), 0, REG_DWORD, (LPBYTE)&dwWidth, sizeof(DWORD))) + throw CWinException(_T("RegSetValueEx failed")); + if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("Height"), 0, REG_DWORD, (LPBYTE)&dwHeight, sizeof(DWORD))) + throw CWinException(_T("RegSetValueEx failed")); + } + + // Store the ToolBar and statusbar states + DWORD dwShowToolBar = m_bShowToolBar; + DWORD dwShowStatusBar = m_bShowStatusBar; + + if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("ToolBar"), 0, REG_DWORD, (LPBYTE)&dwShowToolBar, sizeof(DWORD))) + throw CWinException(_T("RegSetValueEx failed")); + if (ERROR_SUCCESS != RegSetValueEx(hKey, _T("StatusBar"), 0, REG_DWORD, (LPBYTE)&dwShowStatusBar, sizeof(DWORD))) + throw CWinException(_T("RegSetValueEx failed")); + + RegCloseKey(hKey); + } + + catch (const CWinException& e) + { + TRACE(_T("Failed to save registry settings\n")); + + if (hKey) + { + // Roll back the registry changes by deleting this subkey + RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str()); + RegCloseKey(hKey); + } + + e.what(); + return FALSE; + } + + // Store the MRU entries in the registry + if (m_nMaxMRU > 0) + { + tString tsKeyName = _T("Software\\") + m_tsKeyName + _T("\\Recent Files"); + HKEY hKey = NULL; + + try + { + if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) + throw CWinException(_T("RegCreateKeyEx failed")); + + for (UINT i = 0; i < m_nMaxMRU; ++i) + { + TCHAR szSubKey[10]; + wsprintf(szSubKey, _T("File %d\0"), i+1); + tString tsPathName; + if (i < m_vMRUEntries.size()) + tsPathName = m_vMRUEntries[i]; + + if (ERROR_SUCCESS != RegSetValueEx(hKey, szSubKey, 0, REG_SZ, (LPBYTE)tsPathName.c_str(), (1 + lstrlen(tsPathName.c_str()))*sizeof(TCHAR))) + throw CWinException(_T("RegSetValueEx failed")); + } + + RegCloseKey(hKey); + } + + catch (const CWinException& e) + { + TRACE(_T("Failed to save registry MRU settings\n")); + + if (hKey) + { + // Roll back the registry changes by deleting this subkey + RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str()); + RegCloseKey(hKey); + } + + e.what(); + return FALSE; + } + } + } + + return TRUE; + } + + inline void CFrame::SetFrameMenu(INT ID_MENU) + { + // Sets the frame's menu from a Resouce ID. + // A resource ID of 0 removes the menu from the frame. + HMENU hMenu = 0; + if (ID_MENU != 0) + { + // Sets the frame's menu from a resource ID. + hMenu = ::LoadMenu(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(ID_MENU)); + assert (hMenu); + } + + SetFrameMenu(hMenu); + } + + inline void CFrame::SetFrameMenu(HMENU hMenu) + { + // Sets the frame's menu from a HMENU. + m_Menu.Attach(hMenu); + + if (IsMenuBarUsed()) + { + GetMenuBar().SetMenu(GetFrameMenu()); + BOOL bShow = (hMenu != NULL); // boolean expression + ShowMenu(bShow); + } + else + SetMenu(&m_Menu); + } + + inline UINT CFrame::SetMenuIcons(const std::vector<UINT>& MenuData, COLORREF crMask, UINT ToolBarID, UINT ToolBarDisabledID) + { + // Remove any existing menu icons + if (m_himlMenu) ImageList_Destroy(m_himlMenu); + if (m_himlMenuDis) ImageList_Destroy(m_himlMenuDis); + m_himlMenu = NULL; + m_himlMenuDis = NULL; + m_vMenuIcons.clear(); + + // Exit if no ToolBarID is specified + if (ToolBarID == 0) return 0; + + // Add the menu icons from the bitmap IDs + return AddMenuIcons(MenuData, crMask, ToolBarID, ToolBarDisabledID); + } + + inline void CFrame::SetMenuBarBandSize() + { + // Sets the minimum width of the MenuBar band to the width of the rebar + // This prevents other bands from moving to this MenuBar's row. + + CRect rcClient = GetClientRect(); + CReBar& RB = GetReBar(); + int nBand = RB.GetBand(GetMenuBar()); + CRect rcBorder = RB.GetBandBorders(nBand); + + REBARBANDINFO rbbi = {0}; + rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_SIZE; + RB.GetBandInfo(nBand, rbbi); + + int Width; + if ((GetReBar().GetReBarTheme().UseThemes) && (GetReBar().GetReBarTheme().LockMenuBand)) + Width = rcClient.Width() - rcBorder.Width() - 2; + else + Width = GetMenuBar().GetMaxSize().cx; + + rbbi.cxMinChild = Width; + rbbi.cx = Width; + + RB.SetBandInfo(nBand, rbbi); + } + + inline void CFrame::SetMenuTheme(MenuTheme& Theme) + { + m_ThemeMenu.UseThemes = Theme.UseThemes; + m_ThemeMenu.clrHot1 = Theme.clrHot1; + m_ThemeMenu.clrHot2 = Theme.clrHot2; + m_ThemeMenu.clrPressed1 = Theme.clrPressed1; + m_ThemeMenu.clrPressed2 = Theme.clrPressed2; + m_ThemeMenu.clrOutline = Theme.clrOutline; + + GetMenuBar().SetMenuBarTheme(Theme); // Sets the theme for MenuBar buttons + Invalidate(); + } + + inline void CFrame::SetStatusIndicators() + { + if (::IsWindow(GetStatusBar())) + { + LPCTSTR Status1 = (::GetKeyState(VK_CAPITAL) & 0x0001)? _T("\tCAP") : _T(""); + LPCTSTR Status2 = (::GetKeyState(VK_NUMLOCK) & 0x0001)? _T("\tNUM") : _T(""); + LPCTSTR Status3 = (::GetKeyState(VK_SCROLL) & 0x0001)? _T("\tSCRL"): _T(""); + + // Only update indictors if the text has changed + if (Status1 != m_OldStatus[0]) GetStatusBar().SetPartText(1, (Status1)); + if (Status2 != m_OldStatus[1]) GetStatusBar().SetPartText(2, (Status2)); + if (Status3 != m_OldStatus[2]) GetStatusBar().SetPartText(3, (Status3)); + + m_OldStatus[0] = Status1; + m_OldStatus[1] = Status2; + m_OldStatus[2] = Status3; + } + } + + inline void CFrame::SetStatusText() + { + if (::IsWindow(GetStatusBar())) + { + // Calculate the width of the text indicators + CClientDC dcStatus(&GetStatusBar()); + CSize csCAP = dcStatus.GetTextExtentPoint32(_T("\tCAP"), lstrlen(_T("\tCAP"))); + CSize csNUM = dcStatus.GetTextExtentPoint32(_T("\tNUM"), lstrlen(_T("\tNUM"))); + CSize csSCRL = dcStatus.GetTextExtentPoint32(_T("\tSCRL "), lstrlen(_T("\tSCRL "))); + + // Get the coordinates of the parent window's client area. + CRect rcClient = GetClientRect(); + int width = MAX(300, rcClient.right); + + if (m_bShowIndicatorStatus) + { + // Create 4 panes + GetStatusBar().SetPartWidth(0, width - (csCAP.cx+csNUM.cx+csSCRL.cx+20)); + GetStatusBar().SetPartWidth(1, csCAP.cx); + GetStatusBar().SetPartWidth(2, csNUM.cx); + GetStatusBar().SetPartWidth(3, csSCRL.cx); + + SetStatusIndicators(); + } + + // Place text in the 1st pane + GetStatusBar().SetPartText(0, m_tsStatusText.c_str()); + } + } + + inline void CFrame::SetTheme() + { + // Note: To modify theme colors, override this function in CMainframe, + // and make any modifications there. + + // Avoid themes if using less than 16 bit colors + CClientDC DesktopDC(NULL); + if (DesktopDC.GetDeviceCaps(BITSPIXEL) < 16) + m_bUseThemes = FALSE; + + BOOL T = TRUE; + BOOL F = FALSE; + + if (m_bUseThemes) + { + // Set a flag redo SetTheme when the theme changes + m_bUpdateTheme = TRUE; + + // Detect the XP theme name + WCHAR Name[30] = L""; + HMODULE hMod = ::LoadLibrary(_T("uxtheme.dll")); + if(hMod) + { + typedef HRESULT (__stdcall *PFNGETCURRENTTHEMENAME)(LPWSTR pszThemeFileName, int cchMaxNameChars, + LPWSTR pszColorBuff, int cchMaxColorChars, LPWSTR pszSizeBuff, int cchMaxSizeChars); + + PFNGETCURRENTTHEMENAME pfn = (PFNGETCURRENTTHEMENAME)GetProcAddress(hMod, "GetCurrentThemeName"); + + (*pfn)(0, 0, Name, 30, 0, 0); + + ::FreeLibrary(hMod); + } + + enum Themetype{ Modern, Grey, Blue, Silver, Olive }; + + int Theme = Grey; + if (GetWinVersion() < 2600) // Not for Vista and above + { + if (0 == wcscmp(L"NormalColor", Name)) Theme = Blue; + if (0 == wcscmp(L"Metallic", Name)) Theme = Silver; + if (0 == wcscmp(L"HomeStead", Name)) Theme = Olive; + } + else + Theme = Modern; + + switch (Theme) + { + case Modern: + { + ToolBarTheme tt = {T, RGB(180, 250, 255), RGB(140, 190, 255), RGB(150, 220, 255), RGB(80, 100, 255), RGB(127, 127, 255)}; + ReBarTheme tr = {T, RGB(220, 225, 250), RGB(240, 242, 250), RGB(240, 240, 250), RGB(180, 200, 230), F, T, T, T, T, F}; + MenuTheme tm = {T, RGB(180, 250, 255), RGB(140, 190, 255), RGB(240, 250, 255), RGB(120, 170, 220), RGB(127, 127, 255)}; + + GetToolBar().SetToolBarTheme(tt); + SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar + + GetReBar().SetReBarTheme(tr); + } + break; + + case Grey: // A color scheme suitable for 16 bit colors. Suitable for Windows older than XP. + { + ToolBarTheme tt = {T, RGB(182, 189, 210), RGB(182, 189, 210), RGB(133, 146, 181), RGB(133, 146, 181), RGB(10, 36, 106)}; + ReBarTheme tr = {T, RGB(212, 208, 200), RGB(212, 208, 200), RGB(230, 226, 222), RGB(220, 218, 208), F, T, T, T, T, F}; + MenuTheme tm = {T, RGB(182, 189, 210), RGB( 182, 189, 210), RGB(200, 196, 190), RGB(200, 196, 190), RGB(100, 100, 100)}; + + GetToolBar().SetToolBarTheme(tt); + SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar + + GetReBar().SetReBarTheme(tr); + } + break; + case Blue: + { + // Used for XP default (blue) color scheme + ToolBarTheme tt = {T, RGB(255, 230, 190), RGB(255, 190, 100), RGB(255, 140, 40), RGB(255, 180, 80), RGB(192, 128, 255)}; + ReBarTheme tr = {T, RGB(150,190,245), RGB(196,215,250), RGB(220,230,250), RGB( 70,130,220), F, T, T, T, T, F}; + MenuTheme tm = {T, RGB(255, 230, 190), RGB(255, 190, 100), RGB(220,230,250), RGB(150,190,245), RGB(128, 128, 200)}; + + GetToolBar().SetToolBarTheme(tt); + SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar + + GetReBar().SetReBarTheme(tr); + } + break; + + case Silver: + { + // Used for XP Silver color scheme + ToolBarTheme tt = {T, RGB(192, 210, 238), RGB(192, 210, 238), RGB(152, 181, 226), RGB(152, 181, 226), RGB(49, 106, 197)}; + ReBarTheme tr = {T, RGB(225, 220, 240), RGB(240, 240, 245), RGB(245, 240, 255), RGB(160, 155, 180), F, T, T, T, T, F}; + MenuTheme tm = {T, RGB(196, 215, 250), RGB( 120, 180, 220), RGB(240, 240, 245), RGB(170, 165, 185), RGB(128, 128, 150)}; + + GetToolBar().SetToolBarTheme(tt); + SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar + + GetReBar().SetReBarTheme(tr); + } + break; + + case Olive: + { + // Used for XP Olive color scheme + ReBarTheme tr = {T, RGB(215, 216, 182), RGB(242, 242, 230), RGB(249, 255, 227), RGB(178, 191, 145), F, T, T, T, T, F}; + ToolBarTheme tt = {T, RGB(255, 230, 190), RGB(255, 190, 100), RGB(255, 140, 40), RGB(255, 180, 80), RGB(200, 128, 128)}; + MenuTheme tm = {T, RGB(255, 230, 190), RGB(255, 190, 100), RGB(249, 255, 227), RGB(178, 191, 145), RGB(128, 128, 128)}; + + GetToolBar().SetToolBarTheme(tt); + SetMenuTheme(tm); // Sets the theme for popup menus and MenuBar + + GetReBar().SetReBarTheme(tr); + } + break; + } + } + else + { + // Use a classic style by default + ReBarTheme tr = {T, 0, 0, 0, 0, F, T, T, F, F, F}; + GetReBar().SetReBarTheme(tr); + } + + RecalcLayout(); + } + + inline void CFrame::SetToolBarImages(COLORREF crMask, UINT ToolBarID, UINT ToolBarHotID, UINT ToolBarDisabledID) + // Either sets the imagelist or adds/replaces bitmap depending on ComCtl32.dll version + // Assumes the width of the button image = bitmap_size / buttons + // Assumes buttons have been already been added via AdddToolBarButton + // The colour mask is ignored for 32bit bitmaps, but is required for 24bit bitmaps + // The colour mask is often grey RGB(192,192,192) or magenta (255,0,255) + // The color mask is ignored for 32bit bitmap resources + // The Hot and disabled bitmap resources can be 0 + { + GetToolBar().SetImages(crMask, ToolBarID, ToolBarHotID, ToolBarDisabledID); + } + + inline void CFrame::SetupToolBar() + { + // Use this function to set the Resource IDs for the toolbar(s). + +/* // Set the Resource IDs for the toolbar buttons + AddToolBarButton( IDM_FILE_NEW ); + AddToolBarButton( IDM_FILE_OPEN ); + AddToolBarButton( IDM_FILE_SAVE ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_EDIT_CUT ); + AddToolBarButton( IDM_EDIT_COPY ); + AddToolBarButton( IDM_EDIT_PASTE ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_FILE_PRINT ); + AddToolBarButton( 0 ); // Separator + AddToolBarButton( IDM_HELP_ABOUT ); +*/ + } + + inline void CFrame::SetView(CWnd& wndView) + // Sets or changes the View window displayed within the frame + { + if (m_pView != &wndView) + { + // Destroy the existing view window (if any) + if (m_pView) m_pView->Destroy(); + + // Assign the view window + m_pView = &wndView; + + if (m_hWnd) + { + // The frame is already created, so create and position the new view too + assert(GetView()); // Use SetView in CMainFrame's constructor to set the view window + GetView()->Create(this); + RecalcLayout(); + } + } + } + + inline void CFrame::ShowMenu(BOOL bShow) + { + if (bShow) + { + if (IsReBarUsed()) + GetReBar().SendMessage(RB_SHOWBAND, GetReBar().GetBand(GetMenuBar()), TRUE); + else + SetMenu(&m_Menu); + } + else + { + if (IsReBarUsed()) + GetReBar().SendMessage(RB_SHOWBAND, GetReBar().GetBand(GetMenuBar()), FALSE); + else + SetMenu(NULL); + } + + if (GetReBar().IsWindow()) + { + if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().BandsLeft) + GetReBar().MoveBandsLeft(); + } + + // Reposition the Windows + RecalcLayout(); + } + + + + inline void CFrame::ShowStatusBar(BOOL bShow) + { + if (bShow) + { + m_Menu.CheckMenuItem(IDW_VIEW_STATUSBAR, MF_CHECKED); + GetStatusBar().ShowWindow(SW_SHOW); + } + else + { + m_Menu.CheckMenuItem(IDW_VIEW_STATUSBAR, MF_UNCHECKED); + GetStatusBar().ShowWindow(SW_HIDE); + } + + // Reposition the Windows + RecalcLayout(); + } + + inline void CFrame::ShowToolBar(BOOL bShow) + { + if (bShow) + { + m_Menu.CheckMenuItem(IDW_VIEW_TOOLBAR, MF_CHECKED); + if (IsReBarUsed()) + GetReBar().SendMessage(RB_SHOWBAND, GetReBar().GetBand(GetToolBar()), TRUE); + else + GetToolBar().ShowWindow(SW_SHOW); + } + else + { + m_Menu.CheckMenuItem(IDW_VIEW_TOOLBAR, MF_UNCHECKED); + if (IsReBarUsed()) + GetReBar().SendMessage(RB_SHOWBAND, GetReBar().GetBand(GetToolBar()), FALSE); + else + GetToolBar().ShowWindow(SW_HIDE); + } + + if (GetReBar().IsWindow()) + { + if (GetReBar().GetReBarTheme().UseThemes && GetReBar().GetReBarTheme().BandsLeft) + GetReBar().MoveBandsLeft(); + } + + // Reposition the Windows + RecalcLayout(); + } + + inline void CFrame::UpdateMRUMenu() + { + if (0 >= m_nMaxMRU) return; + + // Set the text for the MRU Menu + tString tsMRUArray[16]; + UINT MaxMRUArrayIndex = 0; + if (m_vMRUEntries.size() > 0) + { + for (UINT n = 0; ((n < m_vMRUEntries.size()) && (n <= m_nMaxMRU)); ++n) + { + tsMRUArray[n] = m_vMRUEntries[n]; + if (tsMRUArray[n].length() > MAX_MENU_STRING - 10) + { + // Truncate the string if its too long + tsMRUArray[n].erase(0, tsMRUArray[n].length() - MAX_MENU_STRING +10); + tsMRUArray[n] = _T("... ") + tsMRUArray[n]; + } + + // Prefix the string with its number + TCHAR tVal[5]; + wsprintf(tVal, _T("%d "), n+1); + tsMRUArray[n] = tVal + tsMRUArray[n]; + MaxMRUArrayIndex = n; + } + } + else + { + tsMRUArray[0] = _T("Recent Files"); + } + + // Set MRU menu items + MENUITEMINFO mii = {0}; + mii.cbSize = GetSizeofMenuItemInfo(); + + int nFileItem = 0; // We place the MRU items under the left most menu item + CMenu* pFileMenu = GetFrameMenu().GetSubMenu(nFileItem); + + if (pFileMenu) + { + // Remove all but the first MRU Menu entry + for (UINT u = IDW_FILE_MRU_FILE2; u <= IDW_FILE_MRU_FILE1 +16; ++u) + { + pFileMenu->DeleteMenu(u, MF_BYCOMMAND); + } + + int MaxMRUIndex = (int)MIN(MaxMRUArrayIndex, m_nMaxMRU); + + for (int index = MaxMRUIndex; index >= 0; --index) + { + mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_STATE; + mii.fState = (0 == m_vMRUEntries.size())? MFS_GRAYED : 0; + mii.fType = MFT_STRING; + mii.wID = IDW_FILE_MRU_FILE1 + index; + mii.dwTypeData = (LPTSTR)tsMRUArray[index].c_str(); + + BOOL bResult; + if (index == MaxMRUIndex) + // Replace the last MRU entry first + bResult = pFileMenu->SetMenuItemInfo(IDW_FILE_MRU_FILE1, &mii, FALSE); + else + // Insert the other MRU entries next + bResult = pFileMenu->InsertMenuItem(IDW_FILE_MRU_FILE1 + index + 1, &mii, FALSE); + + if (!bResult) + { + TRACE(_T("Failed to set MRU menu item\n")); + break; + } + } + } + + DrawMenuBar(); + } + + inline LRESULT CFrame::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_ACTIVATE: + OnActivate(wParam, lParam); + return 0L; + case WM_CLOSE: + OnClose(); + break; + case WM_DESTROY: + OnDestroy(); + return 0L; + case WM_ERASEBKGND: + return 0L; + case WM_HELP: + OnHelp(); + return 0L; + case WM_MENUCHAR: + return OnMenuChar(wParam, lParam); + case WM_MENUSELECT: + OnMenuSelect(wParam, lParam); + return 0L; + case WM_SETFOCUS: + OnSetFocus(); + break; + case WM_SIZE: + RecalcLayout(); + return 0L; + case WM_SYSCOLORCHANGE: + // Changing themes trigger this + OnSysColorChange(); + return 0L; + case WM_SYSCOMMAND: + return OnSysCommand(wParam, lParam); + case WM_TIMER: + OnTimer(wParam); + return 0L; + case WM_DRAWITEM: + // Owner draw menu items + return OnDrawItem(wParam, lParam); + case WM_INITMENUPOPUP: + OnInitMenuPopup(wParam, lParam); + break; + case WM_MEASUREITEM: + return OnMeasureItem(wParam, lParam); + case WM_EXITMENULOOP: + OnExitMenuLoop(); + break; + case UWM_GETMENUTHEME: + { + MenuTheme& tm = GetMenuTheme(); + return (LRESULT)&tm; + } + case UWM_GETREBARTHEME: + { + ReBarTheme& rm = GetReBarTheme(); + return (LRESULT)&rm; + } + case UWM_GETTOOLBARTHEME: + { + ToolBarTheme& tt = GetToolBarTheme(); + return (LRESULT)&tt; + } + } // switch uMsg + + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } // LRESULT CFrame::WndProcDefault(...) + + +} // namespace Win32xx + +#endif // _WIN32XX_FRAME_H_ diff --git a/mmc_updater/depends/win32cpp/gdi.h b/mmc_updater/depends/win32cpp/gdi.h new file mode 100644 index 00000000..45141f7b --- /dev/null +++ b/mmc_updater/depends/win32cpp/gdi.h @@ -0,0 +1,3944 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// gdi.h +// Declaration of the CDC class, and CBitmapInfoPtr class + +// The CDC class provides a device context, along with the various associated +// objects such as Bitmaps, Brushes, Bitmaps, Fonts and Pens. This class +// handles the creation, selection, de-selection and deletion of these objects +// automatically. It also automatically deletes or releases the device context +// itself as appropriate. Any failure to create the new GDI object throws an +// exception. +// +// The CDC class is sufficient for most GDI programming needs. Sometimes +// however we need to have the GDI object seperated from the device context. +// Wrapper classes for GDI objects are provided for this purpose. The classes +// are CBitmap, CBrush, CFont, CPalette, CPen and CRgn. These classes +// automatically delete the GDI resouce assigned to them when their destructor +// is called. These wrapper class objects can be attached to the CDC as +// shown below. +// +// Coding Exampe without CDC ... +// void DrawLine() +// { +// HDC hdcClient = ::GetDC(m_hWnd); +// HDC hdcMem = ::CreateCompatibleDC(hdcClient); +// HBITMAP hBitmap = ::CreateCompatibleBitmap(hdcClient, cx, cy); +// HBITMAP hOldBitmap = (HBITMAP)::SelectObject(hdcMem, hBitmap); +// HPEN hPen = ::CreatePen(PS_SOLID, 1, RGB(255,0,0); +// HPEN hOldPen = (HPEN)::SelectObject(hdcMem, hPen); +// ::MoveToEx(hdcMem, 0, 0, NULL); +// ::LineTo(hdcMem, 50, 50); +// ::BitBlt(hdcClient, 0, 0, cx, cy, hdcMem, 0, 0); +// ::SelectObject(hdcMem, hOldPen); +// ::DeleteObject(hPen); +// ::SelectObject(hdcMem, hOldBitmap); +// ::DeleteObject(hBitmap); +// ::DeleteDC(hdcMem); +// ::ReleaseDC(m_hWnd, hdcClient); +// } +// +// Coding Example with CDC classes ... +// void DrawLine() +// { +// CClientDC dcClient(this) +// CMemDC dcMem(&dcClient); +// CBitmap* pOldBitmap = dcMem.CreateCompatibleBitmap(&dcClient, cx, cy); +// CPen* pOldPen = CMemDC.CreatePen(PS_SOLID, 1, RGB(255,0,0); +// CMemDC.MoveTo(0, 0); +// CMemDC.LineTo(50, 50); +// dcClient.BitBlt(0, 0, cx, cy, &CMemDC, 0, 0); +// } +// +// Coding Example with CDC classes and CPen ... +// void DrawLine() +// { +// CClientDC dcClient(this) +// CMemDC CMemDC(&dcClient); +// CBitmap* pOldBitmap = dcMem.CreateCompatibleBitmap(&dcClient, cx, cy); +// CPen MyPen(PS_SOLID, 1, RGB(255,0,0)); +// CPen* pOldPen = CMemDC.SelectObject(&MyPen); +// CMemDC.MoveTo(0, 0); +// CMemDC.LineTo(50, 50); +// dcClient.BitBlt(0, 0, cx, cy, &CMemDC, 0, 0); +// } + +// Notes: +// * When the CDC object drops out of scope, it's destructor is called, releasing +// or deleting the device context as appropriate. +// * When the destructor for CBitmap, CBrush, CPalette, CPen and CRgn are called, +// the destructor is called deleting their GDI object. +// * When the CDC object' destructor is called, any GDI objects created by one of +// the CDC member functions (CDC::CreatePen for example) will be deleted. +// * Bitmaps can only be selected into one device context at a time. +// * Palettes use SelectPalatte to select them into device the context. +// * Regions use SelectClipRgn to select them into the device context. +// * The FromHandle function can be used to convert a GDI handle (HDC, HPEN, +// HBITMAP etc) to a pointer of the appropriate GDI class (CDC, CPen CBitmap etc). +// The FromHandle function creates a temporary object unless the HANDLE is already +// assigned to a GDI class. Temporary objects don't delete their GDI object when +// their destructor is called. +// * All the GDI classes are reference counted. This allows functions to safely +// pass these objects by value, as well as by pointer or by reference. + +// The CBitmapInfoPtr class is a convienient wrapper for the BITMAPINFO structure. +// The size of the BITMAPINFO structure is dependant on the type of HBITMAP, and its +// space needs to be allocated dynamically. CBitmapInfoPtr automatically allocates +// and deallocates the memory for the structure. A CBitmapInfoPtr object can be +// used anywhere in place of a LPBITMAPINFO. LPBITMAPINFO is used in functions like +// GetDIBits and SetDIBits. +// +// Coding example ... +// CDC MemDC = CreateCompatibleDC(NULL); +// CBitmapInfoPtr pbmi(hBitmap); +// MemDC.GetDIBits(hBitmap, 0, pbmi->bmiHeader.biHeight, NULL, pbmi, DIB_RGB_COLORS); + +#ifndef _WIN32XX_GDI_H_ +#define _WIN32XX_GDI_H_ + +#include "wincore.h" + +// Disable macros from Windowsx.h +#undef CopyRgn + +namespace Win32xx +{ + + ///////////////////////////////////////////////////////////////// + // Declarations for some global functions in the Win32xx namespace + // +#ifndef _WIN32_WCE + void GrayScaleBitmap(CBitmap* pbmSource); + void TintBitmap(CBitmap* pbmSource, int cRed, int cGreen, int cBlue); + HIMAGELIST CreateDisabledImageList(HIMAGELIST himlNormal); +#endif + + /////////////////////////////////////////////// + // Declarations for the CGDIObject class + // + class CGDIObject + { + friend CBitmap* FromHandle(HBITMAP hBitmap); + friend CBrush* FromHandle(HBRUSH hBrush); + friend CDC* FromHandle(HDC hDC); + friend CFont* FromHandle(HFONT hFont); + friend CPalette* FromHandle(HPALETTE hPalette); + friend CPen* FromHandle(HPEN hPen); + friend CRgn* FromHandle(HRGN hRgn); + + public: + struct DataMembers // A structure that contains the data members for CGDIObject + { + HGDIOBJ hGDIObject; + long Count; + BOOL bRemoveObject; + }; + CGDIObject(); + CGDIObject(const CGDIObject& rhs); + virtual ~CGDIObject(); + CGDIObject& operator = ( const CGDIObject& rhs ); + void operator = (HGDIOBJ hObject); + + void Attach(HGDIOBJ hObject); + HGDIOBJ Detach(); + HGDIOBJ GetHandle() const; + int GetObject(int nCount, LPVOID pObject) const; + + protected: + DataMembers* m_pData; + + private: + void AddToMap(); + BOOL RemoveFromMap(); + void Release(); + }; + + + /////////////////////////////////////////////// + // Declarations for the CBitmap class + // + class CBitmap : public CGDIObject + { + public: + CBitmap(); + CBitmap(HBITMAP hBitmap); + CBitmap(LPCTSTR lpstr); + CBitmap(int nID); + operator HBITMAP() const; + ~CBitmap(); + + // Create and load methods + BOOL LoadBitmap(LPCTSTR lpszName); + BOOL LoadBitmap(int nID); + BOOL LoadImage(LPCTSTR lpszName, int cxDesired, int cyDesired, UINT fuLoad); + BOOL LoadImage(UINT nID, int cxDesired, int cyDesired, UINT fuLoad); + BOOL LoadOEMBitmap(UINT nIDBitmap); + HBITMAP CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitsPerPixel, LPCVOID lpBits); + HBITMAP CreateCompatibleBitmap(CDC* pDC, int nWidth, int nHeight); + HBITMAP CreateDIBSection(CDC* pDC, CONST BITMAPINFO* lpbmi, UINT uColorUse, LPVOID* ppvBits, HANDLE hSection, DWORD dwOffset); + +#ifndef _WIN32_WCE + HBITMAP CreateDIBitmap(CDC* pDC, CONST BITMAPINFOHEADER* lpbmih, DWORD dwInit, LPCVOID lpbInit, CONST BITMAPINFO* lpbmi, UINT uColorUse); + HBITMAP CreateMappedBitmap(UINT nIDBitmap, UINT nFlags = 0, LPCOLORMAP lpColorMap = NULL, int nMapSize = 0); + HBITMAP CreateBitmapIndirect(LPBITMAP lpBitmap); + int GetDIBits(CDC* pDC, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT uColorUse) const; + int SetDIBits(CDC* pDC, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse); + CSize GetBitmapDimensionEx() const; + CSize SetBitmapDimensionEx(int nWidth, int nHeight); +#endif // !_WIN32_WCE + + // Attributes + BITMAP GetBitmapData() const; + }; + + + /////////////////////////////////////////////// + // Declarations for the CBrush class + // + class CBrush : public CGDIObject + { + public: + CBrush(); + CBrush(HBRUSH hBrush); + CBrush(COLORREF crColor); + operator HBRUSH() const; + ~CBrush(); + + HBRUSH CreateSolidBrush(COLORREF crColor); + HBRUSH CreatePatternBrush(CBitmap* pBitmap); + LOGBRUSH GetLogBrush() const; + +#ifndef _WIN32_WCE + HBRUSH CreateHatchBrush(int nIndex, COLORREF crColor); + HBRUSH CreateBrushIndirect(LPLOGBRUSH lpLogBrush); + HBRUSH CreateDIBPatternBrush(HGLOBAL hglbDIBPacked, UINT fuColorSpec); + HBRUSH CreateDIBPatternBrushPt(LPCVOID lpPackedDIB, UINT nUsage); +#endif // !defined(_WIN32_WCE) + + }; + + + /////////////////////////////////////////////// + // Declarations for the CFont class + // + class CFont : public CGDIObject + { + public: + CFont(); + CFont(HFONT hFont); + CFont(const LOGFONT* lpLogFont); + operator HFONT() const; + ~CFont(); + + // Create methods + HFONT CreateFontIndirect(const LOGFONT* lpLogFont); + HFONT CreatePointFont(int nPointSize, LPCTSTR lpszFaceName, CDC* pDC = NULL, BOOL bBold = FALSE, BOOL bItalic = FALSE); + HFONT CreatePointFontIndirect(const LOGFONT* lpLogFont, CDC* pDC = NULL); + +#ifndef _WIN32_WCE + HFONT CreateFont(int nHeight, int nWidth, int nEscapement, + int nOrientation, int nWeight, DWORD dwItalic, DWORD dwUnderline, + DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision, + DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily, + LPCTSTR lpszFacename); +#endif // #ifndef _WIN32_WCE + + // Attributes + LOGFONT GetLogFont() const; + }; + + + /////////////////////////////////////////////// + // Declarations for the CPalette class + // + class CPalette : public CGDIObject + { + public: + CPalette(); + CPalette(HPALETTE hPalette); + operator HPALETTE() const; + ~CPalette(); + + // Create methods + HPALETTE CreatePalette(LPLOGPALETTE lpLogPalette); + +#ifndef _WIN32_WCE + HPALETTE CreateHalftonePalette(CDC* pDC); +#endif // !_WIN32_WCE + + // Attributes + int GetEntryCount() const; + UINT GetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) const; + UINT SetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors); + + // Operations +#ifndef _WIN32_WCE + BOOL ResizePalette(UINT nNumEntries); + void AnimatePalette(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors); +#endif // !_WIN32_WCE + + UINT GetNearestPaletteIndex (COLORREF crColor) const; + + }; + + + /////////////////////////////////////////////// + // Declarations for the CPen class + // + class CPen : public CGDIObject + { + public: + CPen(); + CPen(HPEN hPen); + CPen(int nPenStyle, int nWidth, COLORREF crColor); +#ifndef _WIN32_WCE + CPen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL); +#endif // !_WIN32_WCE + operator HPEN() const; + ~CPen(); + + HPEN CreatePen(int nPenStyle, int nWidth, COLORREF crColor); + HPEN CreatePenIndirect(LPLOGPEN lpLogPen); + LOGPEN GetLogPen() const; + +#ifndef _WIN32_WCE + HPEN ExtCreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount = 0, const DWORD* lpStyle = NULL); + EXTLOGPEN GetExtLogPen() const; +#endif // !_WIN32_WCE + + }; + + + /////////////////////////////////////////////// + // Declarations for the CRgn class + // + class CRgn : public CGDIObject + { + public: + CRgn(); + CRgn(HRGN hRgn); + operator HRGN() const; + ~CRgn (); + + // Create methods + HRGN CreateRectRgn(int x1, int y1, int x2, int y2); + HRGN CreateRectRgnIndirect(const RECT& rc); + HRGN CreateFromData(const XFORM* lpXForm, int nCount, const RGNDATA* pRgnData); + +#ifndef _WIN32_WCE + HRGN CreateEllipticRgn(int x1, int y1, int x2, int y2); + HRGN CreateEllipticRgnIndirect(const RECT& rc); + HRGN CreatePolygonRgn(LPPOINT lpPoints, int nCount, int nMode); + HRGN CreatePolyPolygonRgn(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount, int nPolyFillMode); + HRGN CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3); + HRGN CreateFromPath(HDC hDC); +#endif // !_WIN32_WCE + + // Operations + void SetRectRgn(int x1, int y1, int x2, int y2); + void SetRectRgn(const RECT& rc); + int CombineRgn(CRgn* pRgnSrc1, CRgn* pRgnSrc2, int nCombineMode); + int CombineRgn(CRgn* pRgnSrc, int nCombineMode); + int CopyRgn(CRgn* pRgnSrc); + BOOL EqualRgn(CRgn* pRgn) const; + int OffsetRgn(int x, int y); + int OffsetRgn(POINT& pt); + int GetRgnBox(RECT& rc) const; + BOOL PtInRegion(int x, int y) const; + BOOL PtInRegion(POINT& pt) const; + BOOL RectInRegion(const RECT& rc) const; + int GetRegionData(LPRGNDATA lpRgnData, int nDataSize) const; + }; + + + /////////////////////////////////////////////// + // Declarations for the CDC class + // + class CDC + { + friend class CWinApp; + friend class CWnd; + friend CDC* FromHandle(HDC hDC); + + public: + struct DataMembers // A structure that contains the data members for CDC + { + std::vector<GDIPtr> m_vGDIObjects; // Smart pointers to internally created Bitmaps, Brushes, Fonts, Bitmaps and Regions + HDC hDC; // The HDC belonging to this CDC + long Count; // Reference count + BOOL bRemoveHDC; // Delete/Release the HDC on destruction + HWND hWnd; // The HWND of a Window or Client window DC + int nSavedDCState; // The save state of the HDC. + }; + + CDC(); // Constructs a new CDC without assigning a HDC + CDC(HDC hDC, HWND hWnd = 0); // Assigns a HDC to a new CDC + CDC(const CDC& rhs); // Constructs a new copy of the CDC + virtual ~CDC(); + operator HDC() const { return m_pData->hDC; } // Converts a CDC to a HDC + CDC& operator = (const CDC& rhs); // Assigns a CDC to an existing CDC + + void Attach(HDC hDC, HWND hWnd = 0); + void Destroy(); + HDC Detach(); + HDC GetHDC() const { return m_pData->hDC; } + CPalette* SelectPalette(const CPalette* pPalette, BOOL bForceBkgnd); + CBitmap* SelectObject(const CBitmap* pBitmap); + CBrush* SelectObject(const CBrush* pBrush); + CFont* SelectObject(const CFont* pFont); + CPalette* SelectObject(const CPalette* pPalette); + CPen* SelectObject(const CPen* pPen); + +#ifndef _WIN32_WCE + void operator = (const HDC hDC); +#endif + + // Initialization + BOOL CreateCompatibleDC(CDC* pDC); + BOOL CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, const DEVMODE* pInitData); + int GetDeviceCaps(int nIndex) const; +#ifndef _WIN32_WCE + BOOL CreateIC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, const DEVMODE* pInitData); +#endif + + // Create and Select Bitmaps + CBitmap* CreateBitmap(int cx, int cy, UINT Planes, UINT BitsPerPixel, LPCVOID pvColors); + CBitmap* CreateCompatibleBitmap(CDC* pDC, int cx, int cy); + CBitmap* CreateDIBSection(CDC* pDC, const BITMAPINFO& bmi, UINT iUsage, LPVOID *ppvBits, + HANDLE hSection, DWORD dwOffset); + BITMAP GetBitmapData() const; + CBitmap* LoadBitmap(UINT nID); + CBitmap* LoadBitmap(LPCTSTR lpszName); + CBitmap* LoadImage(UINT nID, int cxDesired, int cyDesired, UINT fuLoad); + CBitmap* LoadImage(LPCTSTR lpszName, int cxDesired, int cyDesired, UINT fuLoad); + CBitmap* LoadOEMBitmap(UINT nIDBitmap); // for OBM_/OCR_/OIC + +#ifndef _WIN32_WCE + CBitmap* CreateBitmapIndirect(LPBITMAP pBitmap); + CBitmap* CreateDIBitmap(CDC* pDC, const BITMAPINFOHEADER& bmih, DWORD fdwInit, LPCVOID lpbInit, + BITMAPINFO& bmi, UINT fuUsage); + CBitmap* CreateMappedBitmap(UINT nIDBitmap, UINT nFlags /*= 0*/, LPCOLORMAP lpColorMap /*= NULL*/, int nMapSize /*= 0*/); +#endif + + // Create and Select Brushes + CBrush* CreatePatternBrush(CBitmap* pBitmap); + CBrush* CreateSolidBrush(COLORREF rbg); + LOGBRUSH GetLogBrush() const; + +#ifndef _WIN32_WCE + CBrush* CreateBrushIndirect(LPLOGBRUSH pLogBrush); + CBrush* CreateHatchBrush(int fnStyle, COLORREF rgb); + CBrush* CreateDIBPatternBrush(HGLOBAL hglbDIBPacked, UINT fuColorSpec); + CBrush* CreateDIBPatternBrushPt(LPCVOID lpPackedDIB, UINT iUsage); +#endif + + // Create and Select Fonts + CFont* CreateFontIndirect(LPLOGFONT plf); + LOGFONT GetLogFont() const; + +#ifndef _WIN32_WCE + CFont* CreateFont(int nHeight, int nWidth, int nEscapement, int nOrientation, int fnWeight, + DWORD fdwItalic, DWORD fdwUnderline, DWORD fdwStrikeOut, DWORD fdwCharSet, + DWORD fdwOutputPrecision, DWORD fdwClipPrecision, DWORD fdwQuality, + DWORD fdwPitchAndFamily, LPCTSTR lpszFace); +#endif + + // Create and Select Pens + CPen* CreatePen(int nStyle, int nWidth, COLORREF rgb); + CPen* CreatePenIndirect(LPLOGPEN pLogPen); + LOGPEN GetLogPen() const; + + // Create Select Regions + int CreateRectRgn(int left, int top, int right, int bottom); + int CreateRectRgnIndirect(const RECT& rc); + int CreateFromData(const XFORM* Xform, DWORD nCount, const RGNDATA *pRgnData); +#ifndef _WIN32_WCE + int CreateEllipticRgn(int left, int top, int right, int bottom); + int CreateEllipticRgnIndirect(const RECT& rc); + int CreatePolygonRgn(LPPOINT ppt, int cPoints, int fnPolyFillMode); + int CreatePolyPolygonRgn(LPPOINT ppt, LPINT pPolyCounts, int nCount, int fnPolyFillMode); +#endif + + // Wrappers for WinAPI functions + + // Point and Line Drawing Functions + CPoint GetCurrentPosition() const; + CPoint MoveTo(int x, int y) const; + CPoint MoveTo(POINT pt) const; + BOOL LineTo(int x, int y) const; + BOOL LineTo(POINT pt) const; + COLORREF GetPixel(int x, int y) const; + COLORREF GetPixel(POINT pt) const; + COLORREF SetPixel(int x, int y, COLORREF crColor) const; + COLORREF SetPixel(POINT pt, COLORREF crColor) const; +#ifndef _WIN32_WCE + BOOL Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const; + BOOL Arc(RECT& rc, POINT ptStart, POINT ptEnd) const; + BOOL ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const; + BOOL ArcTo(RECT& rc, POINT ptStart, POINT ptEnd) const; + BOOL AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle) const; + int GetArcDirection() const; + int SetArcDirection(int nArcDirection) const; + BOOL PolyDraw(const POINT* lpPoints, const BYTE* lpTypes, int nCount) const; + BOOL Polyline(LPPOINT lpPoints, int nCount) const; + BOOL PolyPolyline(const POINT* lpPoints, const DWORD* lpPolyPoints, int nCount) const; + BOOL PolylineTo(const POINT* lpPoints, int nCount) const; + BOOL PolyBezier(const POINT* lpPoints, int nCount) const; + BOOL PolyBezierTo(const POINT* lpPoints, int nCount) const; + BOOL SetPixelV(int x, int y, COLORREF crColor) const; + BOOL SetPixelV(POINT pt, COLORREF crColor) const; +#endif + + // Shape Drawing Functions + void DrawFocusRect(const RECT& rc) const; + BOOL Ellipse(int x1, int y1, int x2, int y2) const; + BOOL Ellipse(const RECT& rc) const; + BOOL Polygon(LPPOINT lpPoints, int nCount) const; + BOOL Rectangle(int x1, int y1, int x2, int y2) const; + BOOL Rectangle(const RECT& rc) const; + BOOL RoundRect(int x1, int y1, int x2, int y2, int nWidth, int nHeight) const; + BOOL RoundRect(const RECT& rc, int nWidth, int nHeight) const; + +#ifndef _WIN32_WCE + BOOL Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const; + BOOL Chord(const RECT& rc, POINT ptStart, POINT ptEnd) const; + BOOL Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const; + BOOL Pie(const RECT& rc, POINT ptStart, POINT ptEnd) const; + BOOL PolyPolygon(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount) const; +#endif + + // Fill and Image Drawing functions + BOOL FillRect(const RECT& rc, CBrush* pBrushr) const; + BOOL InvertRect(const RECT& rc) const; + BOOL DrawIconEx(int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth, UINT istepIfAniCur, CBrush* pFlickerFreeDraw, UINT diFlags) const; + BOOL DrawEdge(const RECT& rc, UINT nEdge, UINT nFlags) const; + BOOL DrawFrameControl(const RECT& rc, UINT nType, UINT nState) const; + BOOL FillRgn(CRgn* pRgn, CBrush* pBrush) const; + void GradientFill(COLORREF Color1, COLORREF Color2, const RECT& rc, BOOL bVertical); + void SolidFill(COLORREF Color, const RECT& rc); + +#ifndef _WIN32_WCE + BOOL DrawIcon(int x, int y, HICON hIcon) const; + BOOL DrawIcon(POINT point, HICON hIcon) const; + BOOL FrameRect(const RECT& rc, CBrush* pBrush) const; + BOOL PaintRgn(CRgn* pRgn) const; +#endif + + // Bitmap Functions + void DrawBitmap(int x, int y, int cx, int cy, CBitmap& Bitmap, COLORREF clrMask); + int StretchDIBits(int XDest, int YDest, int nDestWidth, int nDestHeight, int XSrc, int YSrc, int nSrcWidth, + int nSrcHeight, CONST VOID *lpBits, BITMAPINFO& bi, UINT iUsage, DWORD dwRop) const; + BOOL PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop) const; + BOOL BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop) const; + BOOL StretchBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop) const; + +#ifndef _WIN32_WCE + int GetDIBits(CBitmap* pBitmap, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbi, UINT uUsage) const; + int SetDIBits(CBitmap* pBitmap, UINT uStartScan, UINT cScanLines, CONST VOID *lpvBits, LPBITMAPINFO lpbi, UINT fuColorUse) const; + int GetStretchBltMode() const; + int SetStretchBltMode(int iStretchMode) const; + BOOL FloodFill(int x, int y, COLORREF crColor) const; + BOOL ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType) const; +#endif + + // Brush Functions +#ifdef GetDCBrushColor + COLORREF GetDCBrushColor() const; + COLORREF SetDCBrushColor(COLORREF crColor) const; +#endif + + // Clipping Functions + int ExcludeClipRect(int Left, int Top, int Right, int BottomRect); + int ExcludeClipRect(const RECT& rc); + int GetClipBox(RECT& rc); + int GetClipRgn(HRGN hrgn); + int IntersectClipRect(int Left, int Top, int Right, int Bottom); + int IntersectClipRect(const RECT& rc); + BOOL RectVisible(const RECT& rc); + int SelectClipRgn(CRgn* pRgn); + +#ifndef _WIN32_WCE + int ExtSelectClipRgn(CRgn* pRgn, int fnMode); + int OffsetClipRgn(int nXOffset, int nYOffset); + BOOL PtVisible(int X, int Y); +#endif + + // Co-ordinate Functions +#ifndef _WIN32_WCE + BOOL DPtoLP(LPPOINT lpPoints, int nCount) const; + BOOL DPtoLP(RECT& rc) const; + BOOL LPtoDP(LPPOINT lpPoints, int nCount) const; + BOOL LPtoDP(RECT& rc) const; +#endif + + // Layout Functions + DWORD GetLayout() const; + DWORD SetLayout(DWORD dwLayout) const; + + // Mapping functions +#ifndef _WIN32_WCE + int GetMapMode() const; + int SetMapMode(int nMapMode) const; + BOOL GetViewportOrgEx(LPPOINT lpPoint) const; + BOOL SetViewportOrgEx(int x, int y, LPPOINT lpPoint = NULL) const; + BOOL SetViewportOrgEx(POINT point, LPPOINT lpPointRet = NULL) const; + BOOL OffsetViewportOrgEx(int nWidth, int nHeight, LPPOINT lpPoint = NULL) const; + BOOL GetViewportExtEx(LPSIZE lpSize) const; + BOOL SetViewportExtEx(int x, int y, LPSIZE lpSize) const; + BOOL SetViewportExtEx(SIZE size, LPSIZE lpSizeRet) const; + BOOL ScaleViewportExtEx(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize) const; + BOOL OffsetWindowOrg(int nWidth, int nHeight, LPPOINT lpPoint) const; + BOOL GetWindowExtEx(LPSIZE lpSize) const; + BOOL SetWindowExtEx(int x, int y, LPSIZE lpSize) const; + BOOL SetWindowExtEx(SIZE size, LPSIZE lpSizeRet) const; + BOOL ScaleWindowExtEx(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize) const; + BOOL GetWindowOrgEx(LPPOINT lpPoint) const; + BOOL SetWindowOrgEx(int x, int y, LPPOINT lpPoint) const; + BOOL SetWindowOrgEx(POINT point, LPPOINT lpPointRet) const; + BOOL OffsetWindowOrgEx(int nWidth, int nHeight, LPPOINT lpPoint) const; +#endif + + // Printer Functions + int StartDoc(LPDOCINFO lpDocInfo) const; + int EndDoc() const; + int StartPage() const; + int EndPage() const; + int AbortDoc() const; + int SetAbortProc(BOOL (CALLBACK* lpfn)(HDC, int)) const; + + // Text Functions + int DrawText(LPCTSTR lpszString, int nCount, LPRECT lprc, UINT nFormat) const; + BOOL ExtTextOut(int x, int y, UINT nOptions, LPCRECT lprc, LPCTSTR lpszString, int nCount = -1, LPINT lpDxWidths = NULL) const; + COLORREF GetBkColor() const; + int GetBkMode() const; + UINT GetTextAlign() const; + int GetTextFace(int nCount, LPTSTR lpszFacename) const; + COLORREF GetTextColor() const; + BOOL GetTextMetrics(TEXTMETRIC& Metrics) const; + COLORREF SetBkColor(COLORREF crColor) const; + int SetBkMode(int iBkMode) const; + UINT SetTextAlign(UINT nFlags) const; + COLORREF SetTextColor(COLORREF crColor) const; + +#ifndef _WIN32_WCE + int DrawTextEx(LPTSTR lpszString, int nCount, LPRECT lprc, UINT nFormat, LPDRAWTEXTPARAMS lpDTParams) const; + CSize GetTabbedTextExtent(LPCTSTR lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions) const; + int GetTextCharacterExtra() const; + CSize GetTextExtentPoint32(LPCTSTR lpszString, int nCount) const; + BOOL GrayString(CBrush* pBrush, GRAYSTRINGPROC lpOutputFunc, LPARAM lpData, int nCount, int x, int y, int nWidth, int nHeight) const; + int SetTextCharacterExtra(int nCharExtra) const; + int SetTextJustification(int nBreakExtra, int nBreakCount) const; + CSize TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin) const; + BOOL TextOut(int x, int y, LPCTSTR lpszString, int nCount = -1) const; +#endif + + private: + void AddToMap(); + static CDC* AddTempHDC(HDC hDC, HWND hWnd); + void Release(); + BOOL RemoveFromMap(); + + DataMembers* m_pData; // pointer to the class's data members + }; + + class CClientDC : public CDC + { + public: + CClientDC(const CWnd* pWnd) + { + if (pWnd) assert(pWnd->IsWindow()); + HWND hWnd = pWnd? pWnd->GetHwnd() : GetDesktopWindow(); + Attach(::GetDC(hWnd), hWnd); + } + virtual ~CClientDC() {} + }; + + class CMemDC : public CDC + { + public: + CMemDC(const CDC* pDC) + { + if (pDC) assert(pDC->GetHDC()); + HDC hDC = pDC? pDC->GetHDC() : NULL; + Attach(::CreateCompatibleDC(hDC)); + } + virtual ~CMemDC() {} + }; + + class CPaintDC : public CDC + { + public: + CPaintDC(const CWnd* pWnd) + { + assert(pWnd->IsWindow()); + m_hWnd = pWnd->GetHwnd(); + Attach(::BeginPaint(pWnd->GetHwnd(), &m_ps), m_hWnd); + } + + virtual ~CPaintDC() { ::EndPaint(m_hWnd, &m_ps); } + + private: + HWND m_hWnd; + PAINTSTRUCT m_ps; + }; + + class CWindowDC : public CDC + { + public: + CWindowDC(const CWnd* pWnd) + { + if (pWnd) assert(pWnd->IsWindow()); + HWND hWnd = pWnd? pWnd->GetHwnd() : GetDesktopWindow(); + Attach(::GetWindowDC(hWnd), hWnd); + } + virtual ~CWindowDC() {} + }; + +#ifndef _WIN32_WCE + class CMetaFileDC : public CDC + { + public: + CMetaFileDC() : m_hMF(0), m_hEMF(0) {} + virtual ~CMetaFileDC() + { + if (m_hMF) + { + ::CloseMetaFile(GetHDC()); + ::DeleteMetaFile(m_hMF); + } + if (m_hEMF) + { + ::CloseEnhMetaFile(GetHDC()); + ::DeleteEnhMetaFile(m_hEMF); + } + } + void Create(LPCTSTR lpszFilename = NULL) { Attach(::CreateMetaFile(lpszFilename)); } + void CreateEnhanced(CDC* pDCRef, LPCTSTR lpszFileName, LPCRECT lpBounds, LPCTSTR lpszDescription) + { + HDC hDC = pDCRef? pDCRef->GetHDC() : NULL; + ::CreateEnhMetaFile(hDC, lpszFileName, lpBounds, lpszDescription); + assert(GetHDC()); + } + HMETAFILE Close() { return ::CloseMetaFile(GetHDC()); } + HENHMETAFILE CloseEnhanced() { return ::CloseEnhMetaFile(GetHDC()); } + + private: + HMETAFILE m_hMF; + HENHMETAFILE m_hEMF; + }; +#endif + + + /////////////////////////////////////////////// + // Declarations for the CBitmapInfoPtr class + // The CBitmapInfoPtr class is a convienient wrapper for the BITMAPINFO structure. + class CBitmapInfoPtr + { + public: + CBitmapInfoPtr(CBitmap* pBitmap) + { + BITMAP bmSource = pBitmap->GetBitmapData(); + + // Convert the color format to a count of bits. + WORD cClrBits = (WORD)(bmSource.bmPlanes * bmSource.bmBitsPixel); + if (cClrBits == 1) cClrBits = 1; + else if (cClrBits <= 4) cClrBits = 4; + else if (cClrBits <= 8) cClrBits = 8; + else if (cClrBits <= 16) cClrBits = 16; + else if (cClrBits <= 24) cClrBits = 24; + else cClrBits = 32; + + // Allocate memory for the BITMAPINFO structure. + UINT uQuadSize = (cClrBits == 24)? 0 : sizeof(RGBQUAD) * (int)(1 << cClrBits); + m_bmi.assign(sizeof(BITMAPINFOHEADER) + uQuadSize, 0); + m_pbmiArray = (LPBITMAPINFO) &m_bmi[0]; + + m_pbmiArray->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + m_pbmiArray->bmiHeader.biHeight = bmSource.bmHeight; + m_pbmiArray->bmiHeader.biWidth = bmSource.bmWidth; + m_pbmiArray->bmiHeader.biPlanes = bmSource.bmPlanes; + m_pbmiArray->bmiHeader.biBitCount = bmSource.bmBitsPixel; + m_pbmiArray->bmiHeader.biCompression = BI_RGB; + if (cClrBits < 24) + m_pbmiArray->bmiHeader.biClrUsed = (1<<cClrBits); + } + LPBITMAPINFO get() const { return m_pbmiArray; } + operator LPBITMAPINFO() const { return m_pbmiArray; } + LPBITMAPINFO operator->() const { return m_pbmiArray; } + + private: + CBitmapInfoPtr(const CBitmapInfoPtr&); // Disable copy construction + CBitmapInfoPtr& operator = (const CBitmapInfoPtr&); // Disable assignment operator + LPBITMAPINFO m_pbmiArray; + std::vector<byte> m_bmi; + }; + + + CBitmap* FromHandle(HBITMAP hBitmap); + CBrush* FromHandle(HBRUSH hBrush); + CFont* FromHandle(HFONT hFont); + CPalette* FromHandle(HPALETTE hPalette); + CPen* FromHandle(HPEN hPen); + CRgn* FromHandle(HRGN hRgn); + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + /////////////////////////////////////////////// + // Declarations for the CGDIObject class + // + + inline CGDIObject::CGDIObject() + // Constructs the CGDIObject + { + m_pData = new DataMembers; + m_pData->hGDIObject = 0; + m_pData->Count = 1L; + m_pData->bRemoveObject = TRUE; + } + + inline CGDIObject::CGDIObject(const CGDIObject& rhs) + // Note: A copy of a CGDIObject is a clone of the original. + // Both objects manipulate the one HGDIOBJ. + { + m_pData = rhs.m_pData; + InterlockedIncrement(&m_pData->Count); + } + + inline CGDIObject::~CGDIObject() + // Deconstructs the CGDIObject + { + Release(); + } + + inline CGDIObject& CGDIObject::operator = ( const CGDIObject& rhs ) + // Note: A copy of a CGDIObject is a clone of the original. + // Both objects manipulate the one HGDIOBJ. + { + if (this != &rhs) + { + InterlockedIncrement(&rhs.m_pData->Count); + Release(); + m_pData = rhs.m_pData; + } + + return *this; + } + + inline void CGDIObject::operator = (HGDIOBJ hObject) + { + assert(m_pData); + assert (m_pData->hGDIObject == NULL); + m_pData->hGDIObject = hObject; + } + + inline void CGDIObject::AddToMap() + // Store the HDC and CDC pointer in the HDC map + { + assert( GetApp() ); + GetApp()->m_csMapLock.Lock(); + + assert(m_pData->hGDIObject); + assert(!GetApp()->GetCGDIObjectFromMap(m_pData->hGDIObject)); + + GetApp()->m_mapGDI.insert(std::make_pair(m_pData->hGDIObject, this)); + GetApp()->m_csMapLock.Release(); + } + + inline void CGDIObject::Attach(HGDIOBJ hObject) + // Attaches a GDI HANDLE to the CGDIObject. + // The HGDIOBJ will be automatically deleted when the destructor is called unless it is detached. + { + assert(m_pData); + + if (m_pData->hGDIObject != NULL && m_pData->hGDIObject != hObject) + { + ::DeleteObject(Detach()); + } + + CGDIObject* pObject = GetApp()->GetCGDIObjectFromMap(hObject); + if (pObject) + { + delete m_pData; + m_pData = pObject->m_pData; + InterlockedIncrement(&m_pData->Count); + } + else + { + m_pData->hGDIObject = hObject; + AddToMap(); + } + } + + inline HGDIOBJ CGDIObject::Detach() + // Detaches the HGDIOBJ from this object. + { + assert(m_pData); + assert(m_pData->hGDIObject); + + GetApp()->m_csMapLock.Lock(); + RemoveFromMap(); + HGDIOBJ hObject = m_pData->hGDIObject; + m_pData->hGDIObject = 0; + + if (m_pData->Count) + { + if (InterlockedDecrement(&m_pData->Count) == 0) + { + delete m_pData; + } + } + + GetApp()->m_csMapLock.Release(); + + // Assign values to our data members + m_pData = new DataMembers; + m_pData->hGDIObject = 0; + m_pData->Count = 1L; + m_pData->bRemoveObject = TRUE; + + return hObject; + } + + inline HGDIOBJ CGDIObject::GetHandle() const + { + assert(m_pData); + return m_pData->hGDIObject; + } + + inline int CGDIObject::GetObject(int nCount, LPVOID pObject) const + { + assert(m_pData); + return ::GetObject(m_pData->hGDIObject, nCount, pObject); + } + + inline void CGDIObject::Release() + { + assert(m_pData); + BOOL bSucceeded = TRUE; + + if (InterlockedDecrement(&m_pData->Count) == 0) + { + if (m_pData->hGDIObject != NULL) + { + if (m_pData->bRemoveObject) + bSucceeded = ::DeleteObject(m_pData->hGDIObject); + else + bSucceeded = TRUE; + } + + RemoveFromMap(); + delete m_pData; + m_pData = 0; + } + + assert(bSucceeded); + } + + inline BOOL CGDIObject::RemoveFromMap() + { + BOOL Success = FALSE; + + if( GetApp() ) + { + // Allocate an iterator for our HDC map + std::map<HGDIOBJ, CGDIObject*, CompareGDI>::iterator m; + + CWinApp* pApp = GetApp(); + if (pApp) + { + // Erase the CGDIObject pointer entry from the map + pApp->m_csMapLock.Lock(); + m = pApp->m_mapGDI.find(m_pData->hGDIObject); + if (m != pApp->m_mapGDI.end()) + { + pApp->m_mapGDI.erase(m); + Success = TRUE; + } + + pApp->m_csMapLock.Release(); + } + } + + return Success; + } + + + /////////////////////////////////////////////// + // Declarations for the CBitmap class + // + inline CBitmap::CBitmap() + { + } + + inline CBitmap::CBitmap(HBITMAP hBitmap) + { + assert(m_pData); + Attach(hBitmap); + } + + inline CBitmap::CBitmap(LPCTSTR lpszName) + { + LoadBitmap(lpszName); + } + + inline CBitmap::CBitmap(int nID) + { + LoadBitmap(nID); + } + + inline CBitmap::operator HBITMAP() const + { + assert(m_pData); + return (HBITMAP)m_pData->hGDIObject; + } + + inline CBitmap::~CBitmap() + { + } + + inline BOOL CBitmap::LoadBitmap(int nID) + // Loads a bitmap from a resource using the resource ID. + { + return LoadBitmap(MAKEINTRESOURCE(nID)); + } + + inline BOOL CBitmap::LoadBitmap(LPCTSTR lpszName) + // Loads a bitmap from a resource using the resource string. + { + assert(GetApp()); + assert(m_pData); + + HBITMAP hBitmap = (HBITMAP)::LoadImage(GetApp()->GetResourceHandle(), lpszName, IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); + if (hBitmap) + { + Attach(hBitmap); + } + return (0 != hBitmap); // boolean expression + } + + inline BOOL CBitmap::LoadImage(UINT nID, int cxDesired, int cyDesired, UINT fuLoad) + // Loads a bitmap from a resource using the resource ID. + { + return LoadImage(MAKEINTRESOURCE(nID), cxDesired, cyDesired, fuLoad); + } + + inline BOOL CBitmap::LoadImage(LPCTSTR lpszName, int cxDesired, int cyDesired, UINT fuLoad) + // Loads a bitmap from a resource using the resource string. + { + assert(GetApp()); + assert(m_pData); + + HBITMAP hBitmap = (HBITMAP)::LoadImage(GetApp()->GetResourceHandle(), lpszName, IMAGE_BITMAP, cxDesired, cyDesired, fuLoad); + if (hBitmap) + { + Attach(hBitmap); + } + return (0 != hBitmap); // boolean expression + } + + inline BOOL CBitmap::LoadOEMBitmap(UINT nIDBitmap) // for OBM_/OCR_/OIC_ + // Loads a predefined bitmap + // Predefined bitmaps include: OBM_BTNCORNERS, OBM_BTSIZE, OBM_CHECK, OBM_CHECKBOXES, OBM_CLOSE, OBM_COMBO + // OBM_DNARROW, OBM_DNARROWD, OBM_DNARROWI, OBM_LFARROW, OBM_LFARROWD, OBM_LFARROWI, OBM_MNARROW,OBM_OLD_CLOSE + // OBM_OLD_DNARROW, OBM_OLD_LFARROW, OBM_OLD_REDUCE, OBM_OLD_RESTORE, OBM_OLD_RGARROW, OBM_OLD_UPARROW + // OBM_OLD_ZOOM, OBM_REDUCE, OBM_REDUCED, OBM_RESTORE, OBM_RESTORED, OBM_RGARROW, OBM_RGARROWD, OBM_RGARROWI + // OBM_SIZE, OBM_UPARROW, OBM_UPARROWD, OBM_UPARROWI, OBM_ZOOM, OBM_ZOOMD + { + assert(m_pData); + + HBITMAP hBitmap = ::LoadBitmap(NULL, MAKEINTRESOURCE(nIDBitmap)); + if (hBitmap) + { + Attach( ::LoadBitmap(NULL, MAKEINTRESOURCE(nIDBitmap)) ); + } + return (0 != hBitmap); // boolean expression + } + +#ifndef _WIN32_WCE + inline HBITMAP CBitmap::CreateMappedBitmap(UINT nIDBitmap, UINT nFlags /*= 0*/, LPCOLORMAP lpColorMap /*= NULL*/, int nMapSize /*= 0*/) + // Creates a new bitmap using the bitmap data and colors specified by the bitmap resource and the color mapping information. + { + assert(GetApp()); + assert(m_pData); + HBITMAP hBitmap = ::CreateMappedBitmap(GetApp()->GetResourceHandle(), nIDBitmap, (WORD)nFlags, lpColorMap, nMapSize); + Attach(hBitmap); + return hBitmap; + } +#endif // !_WIN32_WCE + + inline HBITMAP CBitmap::CreateBitmap(int nWidth, int nHeight, UINT nPlanes, UINT nBitsPerPixel, LPCVOID lpBits) + // Creates a bitmap with the specified width, height, and color format (color planes and bits-per-pixel). + { + assert(m_pData); + HBITMAP hBitmap = ::CreateBitmap(nWidth, nHeight, nPlanes, nBitsPerPixel, lpBits); + Attach(hBitmap); + return hBitmap; + } + +#ifndef _WIN32_WCE + inline HBITMAP CBitmap::CreateBitmapIndirect(LPBITMAP lpBitmap) + // Creates a bitmap with the width, height, and color format specified in the BITMAP structure. + { + assert(m_pData); + HBITMAP hBitmap = ::CreateBitmapIndirect(lpBitmap); + Attach(hBitmap); + return hBitmap; + } +#endif // !_WIN32_WCE + + inline HBITMAP CBitmap::CreateCompatibleBitmap(CDC* pDC, int nWidth, int nHeight) + // Creates a bitmap compatible with the device that is associated with the specified device context. + { + assert(m_pData); + assert(pDC); + HBITMAP hBitmap = ::CreateCompatibleBitmap(pDC->GetHDC(), nWidth, nHeight); + Attach(hBitmap); + return hBitmap; + } + + // Attributes + inline BITMAP CBitmap::GetBitmapData() const + // Retrieves the BITMAP structure + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + BITMAP bmp = {0}; + ::GetObject(m_pData->hGDIObject, sizeof(BITMAP), &bmp); + return bmp; + } + +#ifndef _WIN32_WCE + inline CSize CBitmap::GetBitmapDimensionEx() const + // Retrieves the dimensions of a compatible bitmap. + // The retrieved dimensions must have been set by the SetBitmapDimensionEx function. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + CSize Size; + ::GetBitmapDimensionEx((HBITMAP)m_pData->hGDIObject, &Size); + return Size; + } + + inline CSize CBitmap::SetBitmapDimensionEx(int nWidth, int nHeight) + // The SetBitmapDimensionEx function assigns preferred dimensions to a bitmap. + // These dimensions can be used by applications; however, they are not used by the system. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + CSize Size; + ::SetBitmapDimensionEx((HBITMAP)m_pData->hGDIObject, nWidth, nHeight, Size); + return Size; + } + + // DIB support + inline HBITMAP CBitmap::CreateDIBitmap(CDC* pDC, CONST BITMAPINFOHEADER* lpbmih, DWORD dwInit, CONST VOID* lpbInit, CONST BITMAPINFO* lpbmi, UINT uColorUse) + // Creates a compatible bitmap (DDB) from a DIB and, optionally, sets the bitmap bits. + { + assert(m_pData); + assert(pDC); + HBITMAP hBitmap = ::CreateDIBitmap(pDC->GetHDC(), lpbmih, dwInit, lpbInit, lpbmi, uColorUse); + Attach(hBitmap); + return hBitmap; + } +#endif // !_WIN32_WCE + + inline HBITMAP CBitmap::CreateDIBSection(CDC* pDC, CONST BITMAPINFO* lpbmi, UINT uColorUse, VOID** ppvBits, HANDLE hSection, DWORD dwOffset) + // Creates a DIB that applications can write to directly. The function gives you a pointer to the location of the bitmap bit values. + // You can supply a handle to a file-mapping object that the function will use to create the bitmap, or you can let the system allocate the memory for the bitmap. + { + assert(m_pData); + assert(pDC); + HBITMAP hBitmap = ::CreateDIBSection(pDC->GetHDC(), lpbmi, uColorUse, ppvBits, hSection, dwOffset); + Attach(hBitmap); + return hBitmap; + } + +#ifndef _WIN32_WCE + inline int CBitmap::GetDIBits(CDC* pDC, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbmi, UINT uColorUse) const + // Retrieves the bits of the specified compatible bitmap and copies them into a buffer as a DIB using the specified format. + { + assert(m_pData); + assert(pDC); + assert(m_pData->hGDIObject != NULL); + return ::GetDIBits(pDC->GetHDC(), (HBITMAP)m_pData->hGDIObject, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse); + } + + inline int CBitmap::SetDIBits(CDC* pDC, UINT uStartScan, UINT cScanLines, CONST VOID* lpvBits, CONST BITMAPINFO* lpbmi, UINT uColorUse) + // Sets the pixels in a compatible bitmap (DDB) using the color data found in the specified DIB. + { + assert(m_pData); + assert(pDC); + assert(m_pData->hGDIObject != NULL); + return ::SetDIBits(pDC->GetHDC(), (HBITMAP)m_pData->hGDIObject, uStartScan, cScanLines, lpvBits, lpbmi, uColorUse); + } +#endif // !_WIN32_WCE + + + /////////////////////////////////////////////// + // Definitions of the CBrush class + // + inline CBrush::CBrush() + { + } + + inline CBrush::CBrush(HBRUSH hBrush) + { + assert(m_pData); + Attach(hBrush); + } + + inline CBrush::CBrush(COLORREF crColor) + { + Attach( ::CreateSolidBrush(crColor) ); + assert (m_pData->hGDIObject); + } + + inline CBrush::operator HBRUSH() const + { + assert(m_pData); + return (HBRUSH)m_pData->hGDIObject; + } + + inline CBrush::~CBrush() + { + } + + inline HBRUSH CBrush::CreateSolidBrush(COLORREF crColor) + // Creates a logical brush that has the specified solid color. + { + assert(m_pData); + HBRUSH hBrush = ::CreateSolidBrush(crColor); + Attach(hBrush); + return hBrush; + } + +#ifndef _WIN32_WCE + inline HBRUSH CBrush::CreateHatchBrush(int nIndex, COLORREF crColor) + // Creates a logical brush that has the specified hatch pattern and color. + { + assert(m_pData); + HBRUSH hBrush = ::CreateHatchBrush(nIndex, crColor); + Attach(hBrush); + return hBrush; + } + + inline HBRUSH CBrush::CreateBrushIndirect(LPLOGBRUSH lpLogBrush) + // Creates a logical brush from style, color, and pattern specified in the LOGPRUSH struct. + { + assert(m_pData); + HBRUSH hBrush = ::CreateBrushIndirect(lpLogBrush); + Attach(hBrush); + return hBrush; + } + + inline HBRUSH CBrush::CreateDIBPatternBrush(HGLOBAL hglbDIBPacked, UINT fuColorSpec) + // Creates a logical brush that has the pattern specified by the specified device-independent bitmap (DIB). + { + assert(m_pData); + HBRUSH hBrush = ::CreateDIBPatternBrush(hglbDIBPacked, fuColorSpec); + Attach(hBrush); + return hBrush; + } + + inline HBRUSH CBrush::CreateDIBPatternBrushPt(LPCVOID lpPackedDIB, UINT nUsage) + // Creates a logical brush that has the pattern specified by the device-independent bitmap (DIB). + { + assert(m_pData); + HBRUSH hBrush = ::CreateDIBPatternBrushPt(lpPackedDIB, nUsage); + Attach(hBrush); + return hBrush; + } + +#endif // !defined(_WIN32_WCE) + + inline HBRUSH CBrush::CreatePatternBrush(CBitmap* pBitmap) + // Creates a logical brush with the specified bitmap pattern. The bitmap can be a DIB section bitmap, + // which is created by the CreateDIBSection function, or it can be a device-dependent bitmap. + { + assert(m_pData); + assert(pBitmap); + HBRUSH hBrush = ::CreatePatternBrush(*pBitmap); + Attach(hBrush); + return hBrush; + } + + inline LOGBRUSH CBrush::GetLogBrush() const + // Retrieves the LOGBRUSH structure that defines the style, color, and pattern of a physical brush. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + LOGBRUSH LogBrush = {0}; + ::GetObject (m_pData->hGDIObject, sizeof(LOGBRUSH), &LogBrush); + return LogBrush; + } + + + /////////////////////////////////////////////// + // Definitions of the CFont class + // + inline CFont::CFont() + { + } + + inline CFont::CFont(HFONT hFont) + { + assert(m_pData); + Attach(hFont); + } + + inline CFont::CFont(const LOGFONT* lpLogFont) + { + assert(m_pData); + Attach( ::CreateFontIndirect(lpLogFont) ); + } + + inline CFont::operator HFONT() const + { + assert(m_pData); + return (HFONT)m_pData->hGDIObject; + } + + inline CFont::~CFont() + { + } + + inline HFONT CFont::CreateFontIndirect(const LOGFONT* lpLogFont) + // Creates a logical font that has the specified characteristics. + { + assert(m_pData); + HFONT hFont = ::CreateFontIndirect(lpLogFont); + Attach(hFont); + return hFont; + } + + inline HFONT CFont::CreatePointFont(int nPointSize, LPCTSTR lpszFaceName, CDC* pDC /*= NULL*/, BOOL bBold /*= FALSE*/, BOOL bItalic /*= FALSE*/) + // Creates a font of a specified typeface and point size. + { + LOGFONT logFont = { 0 }; + logFont.lfCharSet = DEFAULT_CHARSET; + logFont.lfHeight = nPointSize; + + lstrcpy(logFont.lfFaceName, lpszFaceName); + + if (bBold) + logFont.lfWeight = FW_BOLD; + if (bItalic) + logFont.lfItalic = (BYTE)TRUE; + + return CreatePointFontIndirect(&logFont, pDC); + } + + inline HFONT CFont::CreatePointFontIndirect(const LOGFONT* lpLogFont, CDC* pDC /* = NULL*/) + // Creates a font of a specified typeface and point size. + // This function automatically converts the height in lfHeight to logical units using the specified device context. + { + HDC hDC = pDC? pDC->GetHDC() : NULL; + HDC hDC1 = (hDC != NULL) ? hDC : ::GetDC(HWND_DESKTOP); + + // convert nPointSize to logical units based on hDC + LOGFONT logFont = *lpLogFont; + +#ifndef _WIN32_WCE + POINT pt = { 0, 0 }; + pt.y = ::MulDiv(::GetDeviceCaps(hDC1, LOGPIXELSY), logFont.lfHeight, 720); // 72 points/inch, 10 decipoints/point + ::DPtoLP(hDC1, &pt, 1); + + POINT ptOrg = { 0, 0 }; + ::DPtoLP(hDC1, &ptOrg, 1); + + logFont.lfHeight = -abs(pt.y - ptOrg.y); +#else // CE specific + // DP and LP are always the same on CE + logFont.lfHeight = -abs(((::GetDeviceCaps(hDC1, LOGPIXELSY)* logFont.lfHeight)/ 720)); +#endif // _WIN32_WCE + + if (hDC == NULL) + ::ReleaseDC (NULL, hDC1); + + return CreateFontIndirect (&logFont); + } + +#ifndef _WIN32_WCE + + inline HFONT CFont::CreateFont(int nHeight, int nWidth, int nEscapement, + int nOrientation, int nWeight, DWORD dwItalic, DWORD dwUnderline, + DWORD dwStrikeOut, DWORD dwCharSet, DWORD dwOutPrecision, + DWORD dwClipPrecision, DWORD dwQuality, DWORD dwPitchAndFamily, + LPCTSTR lpszFacename) + // Creates a logical font with the specified characteristics. + { + HFONT hFont = ::CreateFont(nHeight, nWidth, nEscapement, + nOrientation, nWeight, dwItalic, dwUnderline, dwStrikeOut, + dwCharSet, dwOutPrecision, dwClipPrecision, dwQuality, + dwPitchAndFamily, lpszFacename); + + Attach(hFont); + return hFont; + } +#endif // #ifndef _WIN32_WCE + + inline LOGFONT CFont::GetLogFont() const + // Retrieves the Logfont structure that contains font attributes. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + LOGFONT LogFont = {0}; + ::GetObject(m_pData->hGDIObject, sizeof(LOGFONT), &LogFont); + return LogFont; + } + + + /////////////////////////////////////////////// + // Definitions of the CPalette class + // + inline CPalette::CPalette() + { + } + + inline CPalette::CPalette(HPALETTE hPalette) + { + Attach(hPalette); + } + + inline CPalette::operator HPALETTE() const + { + assert(m_pData); + return (HPALETTE)m_pData->hGDIObject; + } + + inline CPalette::~CPalette () + { + } + + inline HPALETTE CPalette::CreatePalette(LPLOGPALETTE lpLogPalette) + // Creates a logical palette from the information in the specified LOGPALETTE structure. + { + assert(m_pData); + HPALETTE hPalette = ::CreatePalette (lpLogPalette); + Attach(hPalette); + return hPalette; + } + +#ifndef _WIN32_WCE + inline HPALETTE CPalette::CreateHalftonePalette(CDC* pDC) + // Creates a halftone palette for the specified device context (DC). + { + assert(m_pData); + assert(pDC); + HPALETTE hPalette = ::CreateHalftonePalette(pDC->GetHDC()); + Attach(hPalette); + return hPalette; + } +#endif // !_WIN32_WCE + + inline int CPalette::GetEntryCount() const + // Retrieve the number of entries in the palette. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + WORD nEntries = 0; + ::GetObject(m_pData->hGDIObject, sizeof(WORD), &nEntries); + return (int)nEntries; + } + + inline UINT CPalette::GetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) const + // Retrieves a specified range of palette entries from the palette. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::GetPaletteEntries((HPALETTE)m_pData->hGDIObject, nStartIndex, nNumEntries, lpPaletteColors); + } + + inline UINT CPalette::SetPaletteEntries(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) + // Sets RGB (red, green, blue) color values and flags in a range of entries in the palette. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::SetPaletteEntries((HPALETTE)m_pData->hGDIObject, nStartIndex, nNumEntries, lpPaletteColors); + } + +#ifndef _WIN32_WCE + inline void CPalette::AnimatePalette(UINT nStartIndex, UINT nNumEntries, LPPALETTEENTRY lpPaletteColors) + // Replaces entries in the palette. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + ::AnimatePalette((HPALETTE)m_pData->hGDIObject, nStartIndex, nNumEntries, lpPaletteColors); + } + + inline BOOL CPalette::ResizePalette(UINT nNumEntries) + // Increases or decreases the size of the palette based on the specified value. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::ResizePalette((HPALETTE)m_pData->hGDIObject, nNumEntries); + } +#endif // !_WIN32_WCE + + inline UINT CPalette::GetNearestPaletteIndex(COLORREF crColor) const + // Retrieves the index for the entry in the palette most closely matching a specified color value. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::GetNearestPaletteIndex((HPALETTE)m_pData->hGDIObject, crColor); + } + + + /////////////////////////////////////////////// + // Declarations for the CPen class + // + inline CPen::CPen() + { + } + + inline CPen::CPen(HPEN hPen) + { + Attach(hPen); + } + + inline CPen::CPen(int nPenStyle, int nWidth, COLORREF crColor) + { + assert(m_pData); + Attach( ::CreatePen(nPenStyle, nWidth, crColor) ); + } + +#ifndef _WIN32_WCE + inline CPen::CPen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount /*= 0*/, const DWORD* lpStyle /*= NULL*/) + { + assert(m_pData); + Attach( ::ExtCreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount, lpStyle) ); + } +#endif // !_WIN32_WCE + + inline CPen::operator HPEN () const + { + assert(m_pData); + return (HPEN)m_pData->hGDIObject; + } + + inline CPen::~CPen() + { + } + + inline HPEN CPen::CreatePen(int nPenStyle, int nWidth, COLORREF crColor) + // Creates a logical pen that has the specified style, width, and color. + { + assert(m_pData); + HPEN hPen = ::CreatePen(nPenStyle, nWidth, crColor); + Attach(hPen); + return hPen; + } + + inline HPEN CPen::CreatePenIndirect(LPLOGPEN lpLogPen) + // Creates a logical cosmetic pen that has the style, width, and color specified in a structure. + { + assert(m_pData); + HPEN hPen = ::CreatePenIndirect(lpLogPen); + Attach(hPen); + return hPen; + } + + inline LOGPEN CPen::GetLogPen() const + { + // Retrieves the LOGPEN struct that specifies the pen's style, width, and color. + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + + LOGPEN LogPen = {0}; + ::GetObject(m_pData->hGDIObject, sizeof(LOGPEN), &LogPen); + return LogPen; + } + +#ifndef _WIN32_WCE + inline HPEN CPen::ExtCreatePen(int nPenStyle, int nWidth, const LOGBRUSH* pLogBrush, int nStyleCount /* = 0*/, const DWORD* lpStyle /*= NULL*/) + // Creates a logical cosmetic or geometric pen that has the specified style, width, and brush attributes. + { + assert(m_pData); + HPEN hPen = ::ExtCreatePen(nPenStyle, nWidth, pLogBrush, nStyleCount, lpStyle); + Attach(hPen); + return hPen; + } + + inline EXTLOGPEN CPen::GetExtLogPen() const + // Retrieves the EXTLOGPEN struct that specifies the pen's style, width, color and brush attributes. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + + EXTLOGPEN ExLogPen = {0}; + ::GetObject(m_pData->hGDIObject, sizeof(EXTLOGPEN), &ExLogPen); + return ExLogPen; + } +#endif // !_WIN32_WCE + + + /////////////////////////////////////////////// + // Definitions of the CRgn class + // + inline CRgn::CRgn() + { + } + + inline CRgn::CRgn(HRGN hRgn) + { + assert(m_pData); + Attach(hRgn); + } + + inline CRgn::operator HRGN() const + { + assert(m_pData); + return (HRGN)m_pData->hGDIObject; + } + + inline CRgn::~CRgn() + { + } + + inline HRGN CRgn::CreateRectRgn(int x1, int y1, int x2, int y2) + // Creates a rectangular region. + { + assert(m_pData); + HRGN hRgn = ::CreateRectRgn(x1, y1, x2, y2); + Attach(hRgn); + return hRgn; + } + + inline HRGN CRgn::CreateRectRgnIndirect(const RECT& rc) + // Creates a rectangular region. + { + assert(m_pData); + HRGN hRgn = ::CreateRectRgnIndirect(&rc); + Attach(hRgn); + return hRgn; + } + +#ifndef _WIN32_WCE + inline HRGN CRgn::CreateEllipticRgn(int x1, int y1, int x2, int y2) + // Creates an elliptical region. + { + assert(m_pData); + HRGN hRgn = ::CreateEllipticRgn(x1, y1, x2, y2); + Attach(hRgn); + return hRgn; + } + + inline HRGN CRgn::CreateEllipticRgnIndirect(const RECT& rc) + // Creates an elliptical region. + { + assert(m_pData); + HRGN hRgn = ::CreateEllipticRgnIndirect(&rc); + Attach(hRgn); + return hRgn; + } + + inline HRGN CRgn::CreatePolygonRgn(LPPOINT lpPoints, int nCount, int nMode) + // Creates a polygonal region. + { + assert(m_pData); + HRGN hRgn = ::CreatePolygonRgn(lpPoints, nCount, nMode); + Attach(hRgn); + return hRgn; + } + + inline HRGN CRgn::CreatePolyPolygonRgn(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount, int nPolyFillMode) + // Creates a region consisting of a series of polygons. The polygons can overlap. + { + assert(m_pData); + HRGN hRgn = ::CreatePolyPolygonRgn(lpPoints, lpPolyCounts, nCount, nPolyFillMode); + Attach(hRgn); + return hRgn; + } + + inline HRGN CRgn::CreateRoundRectRgn(int x1, int y1, int x2, int y2, int x3, int y3) + // Creates a rectangular region with rounded corners. + { + assert(m_pData); + HRGN hRgn = ::CreateRoundRectRgn(x1, y1, x2, y2, x3, y3); + Attach(hRgn); + return hRgn; + } + + inline HRGN CRgn::CreateFromPath(HDC hDC) + // Creates a region from the path that is selected into the specified device context. + // The resulting region uses device coordinates. + { + assert(m_pData); + assert(hDC != NULL); + HRGN hRgn = ::PathToRegion(hDC); + Attach(hRgn); + return hRgn; + } + +#endif // !_WIN32_WCE + + inline HRGN CRgn::CreateFromData(const XFORM* lpXForm, int nCount, const RGNDATA* pRgnData) + // Creates a region from the specified region and transformation data. + { + assert(m_pData); + HRGN hRgn = ::ExtCreateRegion(lpXForm, nCount, pRgnData); + Attach(hRgn); + return hRgn; + } + + inline void CRgn::SetRectRgn(int x1, int y1, int x2, int y2) + // converts the region into a rectangular region with the specified coordinates. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + ::SetRectRgn((HRGN)m_pData->hGDIObject, x1, y1, x2, y2); + } + + inline void CRgn::SetRectRgn(const RECT& rc) + // converts the region into a rectangular region with the specified coordinates. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + ::SetRectRgn((HRGN)m_pData->hGDIObject, rc.left, rc.top, rc.right, rc.bottom); + } + + inline int CRgn::CombineRgn(CRgn* pRgnSrc1, CRgn* pRgnSrc2, int nCombineMode) + // Combines two sepcified regions and stores the result. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + assert(pRgnSrc1); + assert(pRgnSrc2); + return ::CombineRgn((HRGN)m_pData->hGDIObject, *pRgnSrc1, *pRgnSrc2, nCombineMode); + } + + inline int CRgn::CombineRgn(CRgn* pRgnSrc, int nCombineMode) + // Combines the sepcified region with the current region. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + assert(pRgnSrc); + return ::CombineRgn((HRGN)m_pData->hGDIObject, (HRGN)m_pData->hGDIObject, *pRgnSrc, nCombineMode); + } + + inline int CRgn::CopyRgn(CRgn* pRgnSrc) + // Assigns the specified region to the current region. + { + assert(m_pData); + assert(m_pData->hGDIObject == NULL); + assert(pRgnSrc); + return ::CombineRgn((HRGN)m_pData->hGDIObject, *pRgnSrc, NULL, RGN_COPY); + } + + inline BOOL CRgn::EqualRgn(CRgn* pRgn) const + // Checks the two specified regions to determine whether they are identical. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + assert(pRgn); + return ::EqualRgn((HRGN)m_pData->hGDIObject, *pRgn); + } + + inline int CRgn::OffsetRgn(int x, int y) + // Moves a region by the specified offsets. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::OffsetRgn((HRGN)m_pData->hGDIObject, x, y); + } + + inline int CRgn::OffsetRgn(POINT& pt) + // Moves a region by the specified offsets. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::OffsetRgn((HRGN)m_pData->hGDIObject, pt.x, pt.y); + } + + inline int CRgn::GetRgnBox(RECT& rc) const + // Retrieves the bounding rectangle of the region, and stores it in the specified RECT. + // The return value indicates the region's complexity: NULLREGION;SIMPLEREGION; or COMPLEXREGION. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::GetRgnBox((HRGN)m_pData->hGDIObject, &rc); + } + + inline int CRgn::GetRegionData(LPRGNDATA lpRgnData, int nDataSize) const + // Fills the specified buffer with data describing a region. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return (int)::GetRegionData((HRGN)m_pData->hGDIObject, nDataSize, lpRgnData); + } + + inline BOOL CRgn::PtInRegion(int x, int y) const + // Determines whether the specified point is inside the specified region. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::PtInRegion((HRGN)m_pData->hGDIObject, x, y); + } + + inline BOOL CRgn::PtInRegion(POINT& pt) const + // Determines whether the specified point is inside the specified region. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::PtInRegion((HRGN)m_pData->hGDIObject, pt.x, pt.y); + } + + inline BOOL CRgn::RectInRegion(const RECT& rc) const + // Determines whether the specified rect is inside the specified region. + { + assert(m_pData); + assert(m_pData->hGDIObject != NULL); + return ::RectInRegion((HRGN)m_pData->hGDIObject, &rc); + } + + + /////////////////////////////////////////////// + // Definitions of the CDC class + // + inline CDC::CDC() + { + // Allocate memory for our data members + m_pData = new DataMembers; + + // Assign values to our data members + m_pData->hDC = 0; + m_pData->Count = 1L; + m_pData->bRemoveHDC = TRUE; + m_pData->hWnd = 0; + } + + inline CDC::CDC(HDC hDC, HWND hWnd /*= 0*/) + // This constructor assigns an existing HDC to the CDC + // The HDC WILL be released or deleted when the CDC object is destroyed + // The hWnd paramter is only used in WindowsCE. It specifies the HWND of a Window or + // Window Client DC + + // Note: this constructor permits a call like this: + // CDC MyCDC = SomeHDC; + // or + // CDC MyCDC = ::CreateCompatibleDC(SomeHDC); + // or + // CDC MyCDC = ::GetDC(SomeHWND); + { + UNREFERENCED_PARAMETER(hWnd); + assert(hDC); + + CDC* pDC = GetApp()->GetCDCFromMap(hDC); + if (pDC) + { + m_pData = pDC->m_pData; + InterlockedIncrement(&m_pData->Count); + } + else + { + // Allocate memory for our data members + m_pData = new DataMembers; + + // Assign values to our data members + m_pData->hDC = hDC; + m_pData->Count = 1L; + m_pData->bRemoveHDC = TRUE; + m_pData->nSavedDCState = ::SaveDC(hDC); +#ifndef _WIN32_WCE + m_pData->hWnd = ::WindowFromDC(hDC); +#else + m_pData->hWnd = hWnd; +#endif + if (m_pData->hWnd) + AddToMap(); + } + } + +#ifndef _WIN32_WCE + inline void CDC::operator = (const HDC hDC) + // Note: this assignment operater permits a call like this: + // CDC MyCDC; + // MyCDC = SomeHDC; + { + Attach(hDC); + } +#endif + + inline CDC::CDC(const CDC& rhs) // Copy constructor + // The copy constructor is called when a temporary copy of the CDC needs to be created. + // This can happen when a CDC is passed by value in a function call. Each CDC copy manages + // the same Device Context and GDI objects. + { + m_pData = rhs.m_pData; + InterlockedIncrement(&m_pData->Count); + } + + inline CDC& CDC::operator = (const CDC& rhs) + // Note: A copy of a CDC is a clone of the original. + // Both objects manipulate the one HDC + { + if (this != &rhs) + { + InterlockedIncrement(&rhs.m_pData->Count); + Release(); + m_pData = rhs.m_pData; + } + + return *this; + } + + inline CDC::~CDC () + { + Release(); + } + + inline void CDC::AddToMap() + // Store the HDC and CDC pointer in the HDC map + { + assert( GetApp() ); + assert(m_pData->hDC); + GetApp()->m_csMapLock.Lock(); + + assert(m_pData->hDC); + assert(!GetApp()->GetCDCFromMap(m_pData->hDC)); + + GetApp()->m_mapHDC.insert(std::make_pair(m_pData->hDC, this)); + GetApp()->m_csMapLock.Release(); + } + + inline void CDC::Attach(HDC hDC, HWND hWnd /* = 0*/) + // Attaches a HDC to the CDC object. + // The HDC will be automatically deleted or released when the destructor is called. + // The hWnd parameter is only used on WindowsCE. It specifies the HWND of a Window or + // Window Client DC + { + UNREFERENCED_PARAMETER(hWnd); + assert(m_pData); + assert(0 == m_pData->hDC); + assert(hDC); + + CDC* pDC = GetApp()->GetCDCFromMap(hDC); + if (pDC) + { + delete m_pData; + m_pData = pDC->m_pData; + InterlockedIncrement(&m_pData->Count); + } + else + { + m_pData->hDC = hDC; + +#ifndef _WIN32_WCE + m_pData->hWnd = ::WindowFromDC(hDC); +#else + m_pData->hWnd = hWnd; +#endif + + if (m_pData->hWnd == 0) + AddToMap(); + m_pData->nSavedDCState = ::SaveDC(hDC); + } + } + + inline HDC CDC::Detach() + // Detaches the HDC from this object. + { + assert(m_pData); + assert(m_pData->hDC); + + GetApp()->m_csMapLock.Lock(); + RemoveFromMap(); + HDC hDC = m_pData->hDC; + m_pData->hDC = 0; + + if (m_pData->Count) + { + if (InterlockedDecrement(&m_pData->Count) == 0) + { + delete m_pData; + } + } + + GetApp()->m_csMapLock.Release(); + + // Assign values to our data members + m_pData = new DataMembers; + m_pData->hDC = 0; + m_pData->Count = 1L; + m_pData->bRemoveHDC = TRUE; + m_pData->hWnd = 0; + + return hDC; + } + + // Initialization + inline BOOL CDC::CreateCompatibleDC(CDC* pDC) + // Returns a memory device context (DC) compatible with the specified device. + { + assert(m_pData->hDC == NULL); + HDC hdcSource = (pDC == NULL)? NULL : pDC->GetHDC(); + HDC hDC = ::CreateCompatibleDC(hdcSource); + if (hDC) + { + m_pData->hDC = hDC; + AddToMap(); + } + return (hDC != NULL); // boolean expression + } + + inline BOOL CDC::CreateDC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, const DEVMODE* pInitData) + // Returns a device context (DC) for a device using the specified name. + { + assert(m_pData->hDC == NULL); + HDC hDC = ::CreateDC(lpszDriver, lpszDevice, lpszOutput, pInitData); + if (hDC) + { + m_pData->hDC = hDC; + AddToMap(); + } + return (hDC != NULL); // boolean expression + } + +#ifndef _WIN32_WCE + inline BOOL CDC::CreateIC(LPCTSTR lpszDriver, LPCTSTR lpszDevice, LPCTSTR lpszOutput, const DEVMODE* pInitData) + { + assert(m_pData->hDC == NULL); + HDC hDC = ::CreateIC(lpszDriver, lpszDevice, lpszOutput, pInitData); + if (hDC) + { + m_pData->hDC = hDC; + AddToMap(); + } + return (hDC != NULL); // boolean expression + } +#endif + + inline void CDC::DrawBitmap(int x, int y, int cx, int cy, CBitmap& Bitmap, COLORREF clrMask) + // Draws the specified bitmap to the specified DC using the mask colour provided as the transparent colour + // Suitable for use with a Window DC or a memory DC + { + // Create the Image memory DC + CMemDC dcImage(this); + dcImage.SetBkColor(clrMask); + dcImage.SelectObject(&Bitmap); + + // Create the Mask memory DC + CMemDC dcMask(this); + dcMask.CreateBitmap(cx, cy, 1, 1, NULL); + dcMask.BitBlt(0, 0, cx, cy, &dcImage, 0, 0, SRCCOPY); + + // Mask the image to 'this' DC + BitBlt(x, y, cx, cy, &dcImage, 0, 0, SRCINVERT); + BitBlt(x, y, cx, cy, &dcMask, 0, 0, SRCAND); + BitBlt(x, y, cx, cy, &dcImage, 0, 0, SRCINVERT); + } + + inline CDC* CDC::AddTempHDC(HDC hDC, HWND hWnd) + // Returns the CDC object associated with the device context handle + // The HDC is removed when the CDC is destroyed + { + assert( GetApp() ); + CDC* pDC = new CDC; + pDC->m_pData->hDC = hDC; + GetApp()->AddTmpDC(pDC); + pDC->m_pData->bRemoveHDC = TRUE; + pDC->m_pData->hWnd = hWnd; + return pDC; + } + + inline void CDC::GradientFill(COLORREF Color1, COLORREF Color2, const RECT& rc, BOOL bVertical) + // An efficient color gradient filler compatible with all Windows operating systems + { + int Width = rc.right - rc.left; + int Height = rc.bottom - rc.top; + + int r1 = GetRValue(Color1); + int g1 = GetGValue(Color1); + int b1 = GetBValue(Color1); + + int r2 = GetRValue(Color2); + int g2 = GetGValue(Color2); + int b2 = GetBValue(Color2); + + COLORREF OldBkColor = GetBkColor(); + + if (bVertical) + { + for(int i=0; i < Width; ++i) + { + int r = r1 + (i * (r2-r1) / Width); + int g = g1 + (i * (g2-g1) / Width); + int b = b1 + (i * (b2-b1) / Width); + SetBkColor(RGB(r, g, b)); + CRect line( i + rc.left, rc.top, i + 1 + rc.left, rc.top+Height); + ExtTextOut(0, 0, ETO_OPAQUE, line, NULL, 0, 0); + } + } + else + { + for(int i=0; i < Height; ++i) + { + int r = r1 + (i * (r2-r1) / Height); + int g = g1 + (i * (g2-g1) / Height); + int b = b1 + (i * (b2-b1) / Height); + SetBkColor(RGB(r, g, b)); + CRect line(rc.left, i + rc.top, rc.left+Width, i + 1 + rc.top); + ExtTextOut(0, 0, ETO_OPAQUE, line, NULL, 0, 0); + } + } + + SetBkColor(OldBkColor); + } + + inline void CDC::Release() + { + GetApp()->m_csMapLock.Lock(); + + if (m_pData->Count) + { + if (InterlockedDecrement(&m_pData->Count) == 0) + { + Destroy(); + delete m_pData; + m_pData = 0; + } + } + + GetApp()->m_csMapLock.Release(); + } + + inline BOOL CDC::RemoveFromMap() + { + BOOL Success = FALSE; + + if( GetApp() ) + { + // Allocate an iterator for our HDC map + std::map<HDC, CDC*, CompareHDC>::iterator m; + + CWinApp* pApp = GetApp(); + if (pApp) + { + // Erase the CDC pointer entry from the map + pApp->m_csMapLock.Lock(); + m = pApp->m_mapHDC.find(m_pData->hDC); + if (m != pApp->m_mapHDC.end()) + { + pApp->m_mapHDC.erase(m); + Success = TRUE; + } + + pApp->m_csMapLock.Release(); + } + } + return Success; + } + + inline void CDC::SolidFill(COLORREF Color, const RECT& rc) + // Fills a rectangle with a solid color + { + COLORREF OldColor = SetBkColor(Color); + ExtTextOut(0, 0, ETO_OPAQUE, &rc, NULL, 0, 0); + SetBkColor(OldColor); + } + + // Bitmap functions + inline CBitmap* CDC::CreateCompatibleBitmap(CDC* pDC, int cx, int cy) + // Creates a compatible bitmap and selects it into the device context. + { + assert(m_pData->hDC); + assert(pDC); + + CBitmap* pBitmap = new CBitmap; + pBitmap->CreateCompatibleBitmap(pDC, cx, cy); + m_pData->m_vGDIObjects.push_back(pBitmap); + return SelectObject(pBitmap); + } + + inline CBitmap* CDC::CreateBitmap(int cx, int cy, UINT Planes, UINT BitsPerPixel, LPCVOID pvColors) + // Creates a bitmap and selects it into the device context. + // Returns a pointer to the old bitmap selected out of the device context + { + assert(m_pData->hDC); + + CBitmap* pBitmap = new CBitmap; + pBitmap->CreateBitmap(cx, cy, Planes, BitsPerPixel, pvColors); + m_pData->m_vGDIObjects.push_back(pBitmap); + return SelectObject(pBitmap); + } + +#ifndef _WIN32_WCE + inline CBitmap* CDC::CreateBitmapIndirect (LPBITMAP lpBitmap) + // Creates a bitmap and selects it into the device context. + // Returns a pointer to the old bitmap selected out of the device context + { + assert(m_pData->hDC); + + CBitmap* pBitmap = new CBitmap; + pBitmap->CreateBitmapIndirect(lpBitmap); + m_pData->m_vGDIObjects.push_back(pBitmap); + return SelectObject(pBitmap); + } + + inline CBitmap* CDC::CreateDIBitmap(CDC* pDC, const BITMAPINFOHEADER& bmih, DWORD fdwInit, LPCVOID lpbInit, + BITMAPINFO& bmi, UINT fuUsage) + // Creates a bitmap and selects it into the device context. + // Returns a pointer to the old bitmap selected out of the device context + { + assert(m_pData->hDC); + assert(pDC); + + CBitmap* pBitmap = new CBitmap; + pBitmap->CreateDIBitmap(pDC, &bmih, fdwInit, lpbInit, &bmi, fuUsage); + m_pData->m_vGDIObjects.push_back(pBitmap); + return SelectObject(pBitmap); + } +#endif + + inline CBitmap* CDC::CreateDIBSection(CDC* pDC, const BITMAPINFO& bmi, UINT iUsage, LPVOID *ppvBits, + HANDLE hSection, DWORD dwOffset) + // Creates a bitmap and selects it into the device context. + // Returns a pointer to the old bitmap selected out of the device context + { + assert(m_pData->hDC); + assert(pDC); + + CBitmap* pBitmap = new CBitmap; + pBitmap->CreateDIBSection(pDC, &bmi, iUsage, ppvBits, hSection, dwOffset); + m_pData->m_vGDIObjects.push_back(pBitmap); + return SelectObject(pBitmap); + } + + inline void CDC::Destroy() + // Deletes or releases the device context and returns the CDC object to its + // default state, ready for reuse. + { + if (m_pData->hDC) + { + RemoveFromMap(); + if (m_pData->bRemoveHDC) + { + // Return the DC back to its initial state + ::RestoreDC(m_pData->hDC, m_pData->nSavedDCState); + + // We need to release a Window DC, and delete a memory DC + if (m_pData->hWnd) + ::ReleaseDC(m_pData->hWnd, m_pData->hDC); + else + if (!::DeleteDC(m_pData->hDC)) + ::ReleaseDC(NULL, m_pData->hDC); + + m_pData->hDC = 0; + m_pData->hWnd = 0; + m_pData->bRemoveHDC = TRUE; + } + } + + // RemoveFromMap(); + } + + inline BITMAP CDC::GetBitmapData() const + // Retrieves the BITMAP information for the current HBITMAP. + { + assert(m_pData->hDC); + + HBITMAP hbm = (HBITMAP)::GetCurrentObject(m_pData->hDC, OBJ_BITMAP); + BITMAP bm = {0}; + ::GetObject(hbm, sizeof(bm), &bm); + return bm; + } + + inline CBitmap* CDC::LoadBitmap(UINT nID) + // Loads a bitmap from the resource and selects it into the device context + { + return LoadBitmap(MAKEINTRESOURCE(nID)); + } + + inline CBitmap* CDC::LoadBitmap(LPCTSTR lpszName) + // Loads a bitmap from the resource and selects it into the device context + // Returns a pointer to the old bitmap selected out of the device context + { + assert(m_pData->hDC); + + CBitmap* pBitmap = new CBitmap; + BOOL bResult = pBitmap->LoadBitmap(lpszName); + m_pData->m_vGDIObjects.push_back(pBitmap); + + return bResult? SelectObject(pBitmap) : NULL; + } + + inline CBitmap* CDC::LoadImage(UINT nID, int cxDesired, int cyDesired, UINT fuLoad) + // Loads a bitmap from the resource and selects it into the device context + // Returns a pointer to the old bitmap selected out of the device context + { + return LoadImage(nID, cxDesired, cyDesired, fuLoad); + } + + inline CBitmap* CDC::LoadImage(LPCTSTR lpszName, int cxDesired, int cyDesired, UINT fuLoad) + // Loads a bitmap from the resource and selects it into the device context + // Returns a pointer to the old bitmap selected out of the device context + { + assert(m_pData->hDC); + + CBitmap* pBitmap = new CBitmap; + BOOL bResult = pBitmap->LoadImage(lpszName, cxDesired, cyDesired, fuLoad); + m_pData->m_vGDIObjects.push_back(pBitmap); + return bResult? SelectObject(pBitmap) : NULL; + } + + inline CBitmap* CDC::LoadOEMBitmap(UINT nIDBitmap) // for OBM_/OCR_/OIC_ + // Loads a predefined system bitmap and selects it into the device context + // Returns a pointer to the old bitmap selected out of the device context + { + assert(m_pData->hDC); + + CBitmap* pBitmap = new CBitmap; + BOOL bResult = pBitmap->LoadOEMBitmap(nIDBitmap); + m_pData->m_vGDIObjects.push_back(pBitmap); + return bResult? SelectObject(pBitmap) : NULL; + } + +#ifndef _WIN32_WCE + inline CBitmap* CDC::CreateMappedBitmap(UINT nIDBitmap, UINT nFlags /*= 0*/, LPCOLORMAP lpColorMap /*= NULL*/, int nMapSize /*= 0*/) + // creates and selects a new bitmap using the bitmap data and colors specified by the bitmap resource and the color mapping information. + // Returns a pointer to the old bitmap selected out of the device context + { + assert(m_pData->hDC); + + CBitmap* pBitmap = new CBitmap; + pBitmap->CreateMappedBitmap(nIDBitmap, (WORD)nFlags, lpColorMap, nMapSize); + m_pData->m_vGDIObjects.push_back(pBitmap); + return SelectObject(pBitmap); + } +#endif // !_WIN32_WCE + + + // Brush functions +#ifndef _WIN32_WCE + inline CBrush* CDC::CreateBrushIndirect(LPLOGBRUSH pLogBrush) + // Creates the brush and selects it into the device context. + // Returns a pointer to the old brush selected out of the device context. + { + assert(m_pData->hDC); + + CBrush* pBrush = new CBrush; + pBrush->CreateBrushIndirect(pLogBrush); + m_pData->m_vGDIObjects.push_back(pBrush); + return SelectObject(pBrush); + } + + inline CBrush* CDC::CreateHatchBrush(int fnStyle, COLORREF rgb) + // Creates a brush with the specified hatch pattern and color, and selects it into the device context. + // Returns a pointer to the old brush selected out of the device context. + { + assert(m_pData->hDC); + + CBrush* pBrush = new CBrush; + pBrush->CreateHatchBrush(fnStyle, rgb); + m_pData->m_vGDIObjects.push_back(pBrush); + return SelectObject(pBrush); + } + + inline CBrush* CDC::CreateDIBPatternBrush(HGLOBAL hglbDIBPacked, UINT fuColorSpec) + // Creates a logical from the specified device-independent bitmap (DIB), and selects it into the device context. + // Returns a pointer to the old brush selected out of the device context. + { + assert(m_pData->hDC); + + CBrush* pBrush = new CBrush; + pBrush->CreateDIBPatternBrush(hglbDIBPacked, fuColorSpec); + m_pData->m_vGDIObjects.push_back(pBrush); + return SelectObject(pBrush); + } + + inline CBrush* CDC::CreateDIBPatternBrushPt(LPCVOID lpPackedDIB, UINT iUsage) + // Creates a logical from the specified device-independent bitmap (DIB), and selects it into the device context. + // Returns a pointer to the old brush selected out of the device context. + { + assert(m_pData->hDC); + + CBrush* pBrush = new CBrush; + pBrush->CreateDIBPatternBrushPt(lpPackedDIB, iUsage); + m_pData->m_vGDIObjects.push_back(pBrush); + return SelectObject(pBrush); + } +#endif + + inline CBrush* CDC::CreatePatternBrush(CBitmap* pBitmap) + // Creates the brush with the specified pattern, and selects it into the device context. + // Returns a pointer to the old brush selected out of the device context. + { + assert(m_pData->hDC); + assert(pBitmap); + + CBrush* pBrush = new CBrush; + pBrush->CreatePatternBrush(pBitmap); + m_pData->m_vGDIObjects.push_back(pBrush); + return SelectObject(pBrush); + } + + inline CBrush* CDC::CreateSolidBrush(COLORREF rgb) + // Creates the brush with the specified color, and selects it into the device context. + // Returns a pointer to the old brush selected out of the device context. + { + assert(m_pData->hDC); + + CBrush* pBrush = new CBrush; + pBrush->CreateSolidBrush(rgb); + m_pData->m_vGDIObjects.push_back(pBrush); + return SelectObject(pBrush); + } + + inline LOGBRUSH CDC::GetLogBrush() const + // Retrieves the current brush information + { + assert(m_pData->hDC); + + HBRUSH hBrush = (HBRUSH)::GetCurrentObject(m_pData->hDC, OBJ_BRUSH); + LOGBRUSH lBrush = {0}; + ::GetObject(hBrush, sizeof(lBrush), &lBrush); + return lBrush; + } + + + // Font functions +#ifndef _WIN32_WCE + inline CFont* CDC::CreateFont ( + int nHeight, // height of font + int nWidth, // average character width + int nEscapement, // angle of escapement + int nOrientation, // base-line orientation angle + int fnWeight, // font weight + DWORD fdwItalic, // italic attribute option + DWORD fdwUnderline, // underline attribute option + DWORD fdwStrikeOut, // strikeout attribute option + DWORD fdwCharSet, // character set identifier + DWORD fdwOutputPrecision, // output precision + DWORD fdwClipPrecision, // clipping precision + DWORD fdwQuality, // output quality + DWORD fdwPitchAndFamily, // pitch and family + LPCTSTR lpszFace // typeface name + ) + + // Creates a logical font with the specified characteristics. + // Returns a pointer to the old font selected out of the device context. + { + assert(m_pData->hDC); + + CFont* pFont = new CFont; + pFont->CreateFont (nHeight, nWidth, nEscapement, nOrientation, fnWeight, + fdwItalic, fdwUnderline, fdwStrikeOut, fdwCharSet, + fdwOutputPrecision, fdwClipPrecision, fdwQuality, + fdwPitchAndFamily, lpszFace); + m_pData->m_vGDIObjects.push_back(pFont); + return SelectObject(pFont); + } +#endif + + inline CFont* CDC::CreateFontIndirect(LPLOGFONT plf) + // Creates a logical font and selects it into the device context. + // Returns a pointer to the old font selected out of the device context. + { + assert(m_pData->hDC); + + CFont* pFont = new CFont; + pFont->CreateFontIndirect(plf); + m_pData->m_vGDIObjects.push_back(pFont); + return SelectObject(pFont); + } + + inline LOGFONT CDC::GetLogFont() const + // Retrieves the current font information. + { + assert(m_pData->hDC); + + HFONT hFont = (HFONT)::GetCurrentObject(m_pData->hDC, OBJ_FONT); + LOGFONT lFont = {0}; + ::GetObject(hFont, sizeof(lFont), &lFont); + return lFont; + } + + // Pen functions + inline CPen* CDC::CreatePen (int nStyle, int nWidth, COLORREF rgb) + // Creates the pen and selects it into the device context. + // Returns a pointer to the old pen selected out of the device context. + { + assert(m_pData->hDC); + + CPen* pPen = new CPen; + pPen->CreatePen(nStyle, nWidth, rgb); + m_pData->m_vGDIObjects.push_back(pPen); + return SelectObject(pPen); + } + + inline CPen* CDC::CreatePenIndirect (LPLOGPEN pLogPen) + // Creates the pen and selects it into the device context. + // Returns a pointer to the old pen selected out of the device context. + { + assert(m_pData->hDC); + + CPen* pPen = new CPen; + pPen->CreatePenIndirect(pLogPen); + m_pData->m_vGDIObjects.push_back(pPen); + return SelectObject(pPen); + } + + inline LOGPEN CDC::GetLogPen() const + // Retrieves the current pen information as a LOGPEN + { + assert(m_pData->hDC); + + HPEN hPen = (HPEN)::GetCurrentObject(m_pData->hDC, OBJ_PEN); + LOGPEN lPen = {0}; + ::GetObject(hPen, sizeof(lPen), &lPen); + return lPen; + } + + // Region functions + inline int CDC::CreateRectRgn(int left, int top, int right, int bottom) + // Creates a rectangular region from the rectangle co-ordinates. + // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR. + { + assert(m_pData->hDC); + + CRgn* pRgn = new CRgn; + pRgn->CreateRectRgn(left, top, right, bottom); + m_pData->m_vGDIObjects.push_back(pRgn); + return SelectClipRgn(pRgn); + } + + inline int CDC::CreateRectRgnIndirect(const RECT& rc) + // Creates a rectangular region from the rectangle co-ordinates. + // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR. + { + assert(m_pData->hDC); + + CRgn* pRgn = new CRgn; + pRgn->CreateRectRgnIndirect(rc); + m_pData->m_vGDIObjects.push_back(pRgn); + return SelectClipRgn(pRgn); + } + + inline int CDC::CreateFromData(const XFORM* Xform, DWORD nCount, const RGNDATA *pRgnData) + // Creates a region from the specified region data and tranformation data. + // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR. + // Notes: GetRegionData can be used to get a region's data + // If the XFROM pointer is NULL, the identity transformation is used. + { + assert(m_pData->hDC); + + CRgn* pRgn = new CRgn; + pRgn->CreateFromData(Xform, nCount, pRgnData); + m_pData->m_vGDIObjects.push_back(pRgn); + return SelectClipRgn(pRgn); + } + + +#ifndef _WIN32_WCE + inline int CDC::CreateEllipticRgn(int left, int top, int right, int bottom) + // Creates the ellyiptical region from the bounding rectangle co-ordinates + // and selects it into the device context. + // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR. + { + assert(m_pData->hDC); + + CRgn* pRgn = new CRgn; + pRgn->CreateEllipticRgn(left, top, right, bottom); + m_pData->m_vGDIObjects.push_back(pRgn); + return SelectClipRgn(pRgn); + } + + inline int CDC::CreateEllipticRgnIndirect(const RECT& rc) + // Creates the ellyiptical region from the bounding rectangle co-ordinates + // and selects it into the device context. + // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR. + { + assert(m_pData->hDC); + + CRgn* pRgn = new CRgn; + pRgn->CreateEllipticRgnIndirect(rc); + m_pData->m_vGDIObjects.push_back(pRgn); + return SelectClipRgn(pRgn); + } + + inline int CDC::CreatePolygonRgn(LPPOINT ppt, int cPoints, int fnPolyFillMode) + // Creates the polygon region from the array of points and selects it into + // the device context. The polygon is presumed closed. + // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR. + { + assert(m_pData->hDC); + + CRgn* pRgn = new CRgn; + pRgn->CreatePolygonRgn(ppt, cPoints, fnPolyFillMode); + m_pData->m_vGDIObjects.push_back(pRgn); + return SelectClipRgn(pRgn); + } + + inline int CDC::CreatePolyPolygonRgn(LPPOINT ppt, LPINT pPolyCounts, int nCount, int fnPolyFillMode) + // Creates the polygon region from a series of polygons.The polygons can overlap. + // The return value specifies the region's complexity: NULLREGION;SIMPLEREGION;COMPLEXREGION;ERROR. + { + assert(m_pData->hDC); + + CRgn* pRgn = new CRgn; + pRgn->CreatePolyPolygonRgn(ppt, pPolyCounts, nCount, fnPolyFillMode); + m_pData->m_vGDIObjects.push_back(pRgn); + return SelectClipRgn(pRgn); + } +#endif + + + // Wrappers for WinAPI functions + + inline int CDC::GetDeviceCaps (int nIndex) const + // Retrieves device-specific information for the specified device. + { + assert(m_pData->hDC); + return ::GetDeviceCaps(m_pData->hDC, nIndex); + } + + // Brush Functions +#ifdef GetDCBrushColor + inline COLORREF CDC::GetDCBrushColor() const + { + assert(m_pData->hDC); + return ::GetDCBrushColor(m_pData->hDC); + } + + inline COLORREF CDC::SetDCBrushColor(COLORREF crColor) const + { + assert(m_pData->hDC); + return ::SetDCBrushColor(m_pData->hDC, crColor); + } +#endif + + // Clipping functions + inline int CDC::ExcludeClipRect(int Left, int Top, int Right, int BottomRect) + // Creates a new clipping region that consists of the existing clipping region minus the specified rectangle. + { + assert(m_pData->hDC); + return ::ExcludeClipRect(m_pData->hDC, Left, Top, Right, BottomRect); + } + + inline int CDC::ExcludeClipRect(const RECT& rc) + // Creates a new clipping region that consists of the existing clipping region minus the specified rectangle. + { + assert(m_pData->hDC); + return ::ExcludeClipRect(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom); + } + + inline int CDC::GetClipBox (RECT& rc) + // Retrieves the dimensions of the tightest bounding rectangle that can be drawn around the current visible area on the device. + { + assert(m_pData->hDC); + return ::GetClipBox(m_pData->hDC, &rc); + } + + inline int CDC::GetClipRgn(HRGN hrgn) + // Retrieves a handle identifying the current application-defined clipping region for the specified device context. + // hrgn: A handle to an existing region before the function is called. + // After the function returns, this parameter is a handle to a copy of the current clipping region. + { + assert(m_pData->hDC); + return ::GetClipRgn(m_pData->hDC, hrgn); + } + + inline int CDC::IntersectClipRect(int Left, int Top, int Right, int Bottom) + // Creates a new clipping region from the intersection of the current clipping region and the specified rectangle. + { + assert(m_pData->hDC); + return ::IntersectClipRect(m_pData->hDC, Left, Top, Right, Bottom); + } + + inline int CDC::IntersectClipRect(const RECT& rc) + // Creates a new clipping region from the intersection of the current clipping region and the specified rectangle. + { + assert(m_pData->hDC); + return ::IntersectClipRect(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom); + } + + inline BOOL CDC::RectVisible(const RECT& rc) + // Determines whether any part of the specified rectangle lies within the clipping region of a device context. + { + assert(m_pData->hDC); + return ::RectVisible (m_pData->hDC, &rc); + } + + inline int CDC::SelectClipRgn(CRgn* pRgn) + // Selects a region as the current clipping region for the specified device context. + // Note: Only a copy of the selected region is used. + // To remove a device-context's clipping region, specify a NULL region handle. + { + assert(m_pData->hDC); + return ::SelectClipRgn(m_pData->hDC, pRgn? (HRGN)pRgn->GetHandle() : 0); + } + +#ifndef _WIN32_WCE + inline int CDC::ExtSelectClipRgn(CRgn* pRgn, int fnMode) + // Combines the specified region with the current clipping region using the specified mode. + { + assert(m_pData->hDC); + assert(pRgn); + return ::ExtSelectClipRgn(m_pData->hDC, *pRgn, fnMode); + } +#endif + + inline CBitmap* CDC::SelectObject(const CBitmap* pBitmap) + // Use this to attach an existing bitmap. + { + assert(m_pData->hDC); + assert(pBitmap); + + return FromHandle( (HBITMAP)::SelectObject(m_pData->hDC, *pBitmap) ); + } + + inline CBrush* CDC::SelectObject(const CBrush* pBrush) + // Use this to attach an existing brush. + { + assert(m_pData->hDC); + assert(pBrush); + + return FromHandle( (HBRUSH)::SelectObject(m_pData->hDC, *pBrush) ); + } + + inline CFont* CDC::SelectObject(const CFont* pFont) + // Use this to attach an existing font. + { + assert(m_pData->hDC); + assert(pFont); + + return FromHandle( (HFONT)::SelectObject(m_pData->hDC, *pFont) ); + } + + inline CPalette* CDC::SelectObject(const CPalette* pPalette) + // Use this to attach an existing Palette. + { + assert(m_pData->hDC); + assert(pPalette); + + return FromHandle( (HPALETTE)::SelectObject(m_pData->hDC, *pPalette) ); + } + + inline CPen* CDC::SelectObject(const CPen* pPen) + // Use this to attach an existing pen. + { + assert(m_pData->hDC); + assert(pPen); + + return FromHandle( (HPEN)::SelectObject(m_pData->hDC, *pPen) ); + } + + inline CPalette* CDC::SelectPalette(const CPalette* pPalette, BOOL bForceBkgnd) + // Use this to attach an existing palette. + { + assert(m_pData->hDC); + assert(pPalette); + + return FromHandle( (HPALETTE)::SelectPalette(m_pData->hDC, *pPalette, bForceBkgnd) ); + } +#ifndef _WIN32_WCE + inline BOOL CDC::PtVisible(int X, int Y) + // Determines whether the specified point is within the clipping region of a device context. + { + assert(m_pData->hDC); + return ::PtVisible (m_pData->hDC, X, Y); + } + + inline int CDC::OffsetClipRgn(int nXOffset, int nYOffset) + // Moves the clipping region of a device context by the specified offsets. + { + assert(m_pData->hDC); + return ::OffsetClipRgn (m_pData->hDC, nXOffset, nYOffset); + } +#endif + + // Point and Line Drawing Functions + inline CPoint CDC::GetCurrentPosition() const + // Returns the current "MoveToEx" position. + { + assert(m_pData->hDC); + CPoint pt; + ::MoveToEx(m_pData->hDC, 0, 0, &pt); + ::MoveToEx(m_pData->hDC, pt.x, pt.y, NULL); + return pt; + } + + inline CPoint CDC::MoveTo(int x, int y) const + // Updates the current position to the specified point. + { + assert(m_pData->hDC); + return ::MoveToEx(m_pData->hDC, x, y, NULL); + } + + inline CPoint CDC::MoveTo(POINT pt) const + // Updates the current position to the specified point + { + assert(m_pData->hDC); + return ::MoveToEx(m_pData->hDC, pt.x, pt.y, NULL); + } + + inline BOOL CDC::LineTo(int x, int y) const + // Draws a line from the current position up to, but not including, the specified point. + { + assert(m_pData->hDC); + return ::LineTo(m_pData->hDC, x, y); + } + + inline BOOL CDC::LineTo(POINT pt) const + // Draws a line from the current position up to, but not including, the specified point. + { + assert(m_pData->hDC); + return ::LineTo(m_pData->hDC, pt.x, pt.y); + } + +#ifndef _WIN32_WCE + inline BOOL CDC::Arc(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const + // Draws an elliptical arc. + { + assert(m_pData->hDC); + return ::Arc(m_pData->hDC, x1, y1, x2, y2, x3, y3, x4, y4); + } + + inline BOOL CDC::Arc(RECT& rc, POINT ptStart, POINT ptEnd) const + // Draws an elliptical arc. + { + assert(m_pData->hDC); + return ::Arc(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom, + ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); + } + + inline BOOL CDC::ArcTo(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const + // Draws an elliptical arc. + { + assert(m_pData->hDC); + return ::ArcTo(m_pData->hDC, x1, y1, x2, y2, x3, y3, x4, y4); + } + + inline BOOL CDC::ArcTo(RECT& rc, POINT ptStart, POINT ptEnd) const + // Draws an elliptical arc. + { + assert(m_pData->hDC); + return ::ArcTo (m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom, + ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); + } + + inline BOOL CDC::AngleArc(int x, int y, int nRadius, float fStartAngle, float fSweepAngle) const + // Draws a line segment and an arc. + { + assert(m_pData->hDC); + return ::AngleArc(m_pData->hDC, x, y, nRadius, fStartAngle, fSweepAngle); + } + + inline int CDC::GetArcDirection() const + // Retrieves the current arc direction ( AD_COUNTERCLOCKWISE or AD_CLOCKWISE ). + { + assert(m_pData->hDC); + return ::GetArcDirection(m_pData->hDC); + } + + inline int CDC::SetArcDirection(int nArcDirection) const + // Sets the current arc direction ( AD_COUNTERCLOCKWISE or AD_CLOCKWISE ). + { + assert(m_pData->hDC); + return ::SetArcDirection(m_pData->hDC, nArcDirection); + } + + inline BOOL CDC::PolyDraw(const POINT* lpPoints, const BYTE* lpTypes, int nCount) const + // Draws a set of line segments and Bzier curves. + { + assert(m_pData->hDC); + return ::PolyDraw(m_pData->hDC, lpPoints, lpTypes, nCount); + } + + inline BOOL CDC::Polyline(LPPOINT lpPoints, int nCount) const + // Draws a series of line segments by connecting the points in the specified array. + { + assert(m_pData->hDC); + return ::Polyline(m_pData->hDC, lpPoints, nCount); + } + + inline BOOL CDC::PolyPolyline(const POINT* lpPoints, const DWORD* lpPolyPoints, int nCount) const + // Draws multiple series of connected line segments. + { + assert(m_pData->hDC); + return ::PolyPolyline(m_pData->hDC, lpPoints, lpPolyPoints, nCount); + } + + inline BOOL CDC::PolylineTo(const POINT* lpPoints, int nCount) const + // Draws one or more straight lines. + { + assert(m_pData->hDC); + return ::PolylineTo(m_pData->hDC, lpPoints, nCount); + } + inline BOOL CDC::PolyBezier(const POINT* lpPoints, int nCount) const + // Draws one or more Bzier curves. + { + assert(m_pData->hDC); + return ::PolyBezier(m_pData->hDC, lpPoints, nCount); + } + + inline BOOL CDC::PolyBezierTo(const POINT* lpPoints, int nCount) const + // Draws one or more Bzier curves. + { + assert(m_pData->hDC); + return ::PolyBezierTo(m_pData->hDC, lpPoints, nCount ); + } + + inline COLORREF CDC::GetPixel(int x, int y) const + // Retrieves the red, green, blue (RGB) color value of the pixel at the specified coordinates. + { + assert(m_pData->hDC); + return ::GetPixel(m_pData->hDC, x, y); + } + + inline COLORREF CDC::GetPixel(POINT pt) const + // Retrieves the red, green, blue (RGB) color value of the pixel at the specified coordinates. + { + assert(m_pData->hDC); + return ::GetPixel(m_pData->hDC, pt.x, pt.y); + } + + inline COLORREF CDC::SetPixel (int x, int y, COLORREF crColor) const + // Sets the pixel at the specified coordinates to the specified color. + { + assert(m_pData->hDC); + return ::SetPixel(m_pData->hDC, x, y, crColor); + } + + inline COLORREF CDC::SetPixel(POINT pt, COLORREF crColor) const + // Sets the pixel at the specified coordinates to the specified color. + { + assert(m_pData->hDC); + return ::SetPixel(m_pData->hDC, pt.x, pt.y, crColor); + } + + inline BOOL CDC::SetPixelV(int x, int y, COLORREF crColor) const + // Sets the pixel at the specified coordinates to the closest approximation of the specified color. + { + assert(m_pData->hDC); + return ::SetPixelV(m_pData->hDC, x, y, crColor); + } + + inline BOOL CDC::SetPixelV(POINT pt, COLORREF crColor) const + // Sets the pixel at the specified coordinates to the closest approximation of the specified color. + { + assert(m_pData->hDC); + return ::SetPixelV(m_pData->hDC, pt.x, pt.y, crColor); + } +#endif + + // Shape Drawing Functions + inline void CDC::DrawFocusRect(const RECT& rc) const + // Draws a rectangle in the style used to indicate that the rectangle has the focus. + { + assert(m_pData->hDC); + ::DrawFocusRect(m_pData->hDC, &rc); + } + + inline BOOL CDC::Ellipse(int x1, int y1, int x2, int y2) const + // Draws an ellipse. The center of the ellipse is the center of the specified bounding rectangle. + { + assert(m_pData->hDC); + return ::Ellipse(m_pData->hDC, x1, y1, x2, y2); + } + + inline BOOL CDC::Ellipse(const RECT& rc) const + // Draws an ellipse. The center of the ellipse is the center of the specified bounding rectangle. + { + assert(m_pData->hDC); + return ::Ellipse(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom); + } + + inline BOOL CDC::Polygon(LPPOINT lpPoints, int nCount) const + // Draws a polygon consisting of two or more vertices connected by straight lines. + { + assert(m_pData->hDC); + return ::Polygon(m_pData->hDC, lpPoints, nCount); + } + + inline BOOL CDC::Rectangle(int x1, int y1, int x2, int y2) const + // Draws a rectangle. The rectangle is outlined by using the current pen and filled by using the current brush. + { + assert(m_pData->hDC); + return ::Rectangle(m_pData->hDC, x1, y1, x2, y2); + } + + inline BOOL CDC::Rectangle(const RECT& rc) const + // Draws a rectangle. The rectangle is outlined by using the current pen and filled by using the current brush. + { + assert(m_pData->hDC); + return ::Rectangle(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom); + } + + inline BOOL CDC::RoundRect(int x1, int y1, int x2, int y2, int nWidth, int nHeight) const + // Draws a rectangle with rounded corners. + { + assert(m_pData->hDC); + return ::RoundRect(m_pData->hDC, x1, y1, x2, y2, nWidth, nHeight); + } + inline BOOL CDC::RoundRect(const RECT& rc, int nWidth, int nHeight) const + // Draws a rectangle with rounded corners. + { + assert(m_pData->hDC); + return ::RoundRect(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom, nWidth, nHeight ); + } + +#ifndef _WIN32_WCE + inline BOOL CDC::Chord(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const + // Draws a chord (a region bounded by the intersection of an ellipse and a line segment, called a secant). + { + assert(m_pData->hDC); + return ::Chord(m_pData->hDC, x1, y1, x2, y2, x3, y3, x4, y4); + } + + inline BOOL CDC::Chord(const RECT& rc, POINT ptStart, POINT ptEnd) const + // Draws a chord (a region bounded by the intersection of an ellipse and a line segment, called a secant). + { + assert(m_pData->hDC); + return ::Chord(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom, + ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); + } + + inline BOOL CDC::Pie(int x1, int y1, int x2, int y2, int x3, int y3, int x4, int y4) const + // Draws a pie-shaped wedge bounded by the intersection of an ellipse and two radials. + { + assert(m_pData->hDC); + return ::Pie(m_pData->hDC, x1, y1, x2, y2, x3, y3, x4, y4); + } + + inline BOOL CDC::Pie(const RECT& rc, POINT ptStart, POINT ptEnd) const + // Draws a pie-shaped wedge bounded by the intersection of an ellipse and two radials. + { + assert(m_pData->hDC); + return ::Pie(m_pData->hDC, rc.left, rc.top, rc.right, rc.bottom, + ptStart.x, ptStart.y, ptEnd.x, ptEnd.y); + } + + inline BOOL CDC::PolyPolygon(LPPOINT lpPoints, LPINT lpPolyCounts, int nCount) const + // Draws a series of closed polygons. + { + assert(m_pData->hDC); + return ::PolyPolygon(m_pData->hDC, lpPoints, lpPolyCounts, nCount); + } +#endif + + // Fill and 3D Drawing functions + inline BOOL CDC::FillRect(const RECT& rc, CBrush* pBrush) const + // Fills a rectangle by using the specified brush. + { + assert(m_pData->hDC); + assert(pBrush); + return (BOOL)::FillRect(m_pData->hDC, &rc, *pBrush); + } + + inline BOOL CDC::InvertRect(const RECT& rc) const + // Inverts a rectangle in a window by performing a logical NOT operation on the color values for each pixel in the rectangle's interior. + { + assert(m_pData->hDC); + return ::InvertRect( m_pData->hDC, &rc); + } + + inline BOOL CDC::DrawIconEx(int xLeft, int yTop, HICON hIcon, int cxWidth, int cyWidth, UINT istepIfAniCur, CBrush* pFlickerFreeDraw, UINT diFlags) const + // draws an icon or cursor, performing the specified raster operations, and stretching or compressing the icon or cursor as specified. + { + assert(m_pData->hDC); + HBRUSH hFlickerFreeDraw = pFlickerFreeDraw? (HBRUSH)pFlickerFreeDraw->GetHandle() : NULL; + return ::DrawIconEx(m_pData->hDC, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hFlickerFreeDraw, diFlags); + } + + inline BOOL CDC::DrawEdge(const RECT& rc, UINT nEdge, UINT nFlags) const + // Draws one or more edges of rectangle. + { + assert(m_pData->hDC); + return ::DrawEdge(m_pData->hDC, (LPRECT)&rc, nEdge, nFlags); + } + + inline BOOL CDC::DrawFrameControl(const RECT& rc, UINT nType, UINT nState) const + // Draws a frame control of the specified type and style. + { + assert(m_pData->hDC); + return ::DrawFrameControl(m_pData->hDC, (LPRECT)&rc, nType, nState); + } + + inline BOOL CDC::FillRgn(CRgn* pRgn, CBrush* pBrush) const + // Fills a region by using the specified brush. + { + assert(m_pData->hDC); + assert(pRgn); + assert(pBrush); + return ::FillRgn(m_pData->hDC, *pRgn, *pBrush); + } + +#ifndef _WIN32_WCE + inline BOOL CDC::DrawIcon(int x, int y, HICON hIcon) const + // Draws an icon or cursor. + { + assert(m_pData->hDC); + return ::DrawIcon(m_pData->hDC, x, y, hIcon); + } + + inline BOOL CDC::DrawIcon(POINT pt, HICON hIcon) const + // Draws an icon or cursor. + { + assert(m_pData->hDC); + return ::DrawIcon(m_pData->hDC, pt.x, pt.y, hIcon); + } + + inline BOOL CDC::FrameRect(const RECT& rc, CBrush* pBrush) const + // Draws a border around the specified rectangle by using the specified brush. + { + assert(m_pData->hDC); + assert(pBrush); + return (BOOL)::FrameRect(m_pData->hDC, &rc, *pBrush); + } + + inline BOOL CDC::PaintRgn(CRgn* pRgn) const + // Paints the specified region by using the brush currently selected into the device context. + { + assert(m_pData->hDC); + assert(pRgn); + return ::PaintRgn(m_pData->hDC, *pRgn); + } +#endif + + // Bitmap Functions + inline int CDC::StretchDIBits(int XDest, int YDest, int nDestWidth, int nDestHeight, int XSrc, int YSrc, int nSrcWidth, + int nSrcHeight, CONST VOID *lpBits, BITMAPINFO& bi, UINT iUsage, DWORD dwRop) const + // Copies the color data for a rectangle of pixels in a DIB to the specified destination rectangle. + { + assert(m_pData->hDC); + return ::StretchDIBits(m_pData->hDC, XDest, YDest, nDestWidth, nDestHeight, XSrc, YSrc, nSrcWidth, nSrcHeight, lpBits, &bi, iUsage, dwRop); + } + + inline BOOL CDC::PatBlt(int x, int y, int nWidth, int nHeight, DWORD dwRop) const + // Paints the specified rectangle using the brush that is currently selected into the device context. + { + assert(m_pData->hDC); + return ::PatBlt(m_pData->hDC, x, y, nWidth, nHeight, dwRop); + } + + inline BOOL CDC::BitBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, DWORD dwRop) const + // Performs a bit-block transfer of the color data corresponding to a rectangle of pixels from the specified source device context into a destination device context. + { + assert(m_pData->hDC); + assert(pSrcDC); + return ::BitBlt(m_pData->hDC, x, y, nWidth, nHeight, pSrcDC->GetHDC(), xSrc, ySrc, dwRop); + } + + inline BOOL CDC::StretchBlt(int x, int y, int nWidth, int nHeight, CDC* pSrcDC, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight, DWORD dwRop) const + // Copies a bitmap from a source rectangle into a destination rectangle, stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary. + { + assert(m_pData->hDC); + assert(pSrcDC); + return ::StretchBlt(m_pData->hDC, x, y, nWidth, nHeight, pSrcDC->GetHDC(), xSrc, ySrc, nSrcWidth, nSrcHeight, dwRop); + } + +#ifndef _WIN32_WCE + inline int CDC::GetDIBits(CBitmap* pBitmap, UINT uStartScan, UINT cScanLines, LPVOID lpvBits, LPBITMAPINFO lpbi, UINT uUsage) const + // Retrieves the bits of the specified compatible bitmap and copies them into a buffer as a DIB using the specified format. + { + assert(m_pData->hDC); + assert(pBitmap); + return ::GetDIBits(m_pData->hDC, *pBitmap, uStartScan, cScanLines, lpvBits, lpbi, uUsage); + } + + inline int CDC::SetDIBits(CBitmap* pBitmap, UINT uStartScan, UINT cScanLines, CONST VOID *lpvBits, LPBITMAPINFO lpbi, UINT fuColorUse) const + // Sets the pixels in a compatible bitmap (DDB) using the color data found in the specified DIB. + { + assert(m_pData->hDC); + return ::SetDIBits(m_pData->hDC, *pBitmap, uStartScan, cScanLines, lpvBits, lpbi, fuColorUse); + } + + inline int CDC::GetStretchBltMode() const + // Retrieves the current stretching mode. + // Possible modes: BLACKONWHITE, COLORONCOLOR, HALFTONE, STRETCH_ANDSCANS, STRETCH_DELETESCANS, STRETCH_HALFTONE, STRETCH_ORSCANS, WHITEONBLACK + { + assert(m_pData->hDC); + return ::GetStretchBltMode(m_pData->hDC); + } + + inline int CDC::SetStretchBltMode(int iStretchMode) const + // Sets the stretching mode. + // Possible modes: BLACKONWHITE, COLORONCOLOR, HALFTONE, STRETCH_ANDSCANS, STRETCH_DELETESCANS, STRETCH_HALFTONE, STRETCH_ORSCANS, WHITEONBLACK + { + assert(m_pData->hDC); + return ::SetStretchBltMode(m_pData->hDC, iStretchMode); + } + + inline BOOL CDC::FloodFill(int x, int y, COLORREF crColor) const + // Fills an area of the display surface with the current brush. + { + assert(m_pData->hDC); + return ::FloodFill(m_pData->hDC, x, y, crColor); + } + + inline BOOL CDC::ExtFloodFill(int x, int y, COLORREF crColor, UINT nFillType) const + // Fills an area of the display surface with the current brush. + // Fill type: FLOODFILLBORDER or FLOODFILLSURFACE + { + assert(m_pData->hDC); + return ::ExtFloodFill(m_pData->hDC, x, y, crColor, nFillType ); + } + + // co-ordinate functions + inline BOOL CDC::DPtoLP(LPPOINT lpPoints, int nCount) const + // Converts device coordinates into logical coordinates. + { + assert(m_pData->hDC); + return ::DPtoLP(m_pData->hDC, lpPoints, nCount); + } + + inline BOOL CDC::DPtoLP(RECT& rc) const + // Converts device coordinates into logical coordinates. + { + assert(m_pData->hDC); + return ::DPtoLP(m_pData->hDC, (LPPOINT)&rc, 2); + } + + inline BOOL CDC::LPtoDP(LPPOINT lpPoints, int nCount) const + // Converts logical coordinates into device coordinates. + { + assert(m_pData->hDC); + return ::LPtoDP(m_pData->hDC, lpPoints, nCount); + } + + inline BOOL CDC::LPtoDP(RECT& rc) const + // Converts logical coordinates into device coordinates. + { + assert(m_pData->hDC); + return ::LPtoDP(m_pData->hDC, (LPPOINT)&rc, 2); + } + +#endif + + // Layout Functions + inline DWORD CDC::GetLayout() const + // Returns the layout of a device context (LAYOUT_RTL and LAYOUT_BITMAPORIENTATIONPRESERVED). + { +#if defined(WINVER) && defined(GetLayout) && (WINVER >= 0x0500) + return ::GetLayout(m_pData->hDC); +#else + return 0; +#endif + } + + inline DWORD CDC::SetLayout(DWORD dwLayout) const + // changes the layout of a device context (DC). + // dwLayout values: LAYOUT_RTL or LAYOUT_BITMAPORIENTATIONPRESERVED + { +#if defined(WINVER) && defined (SetLayout) && (WINVER >= 0x0500) + // Sets the layout of a device context + return ::SetLayout(m_pData->hDC, dwLayout); +#else + UNREFERENCED_PARAMETER(dwLayout); // no-op + return 0; +#endif + } + + // Mapping Functions +#ifndef _WIN32_WCE + inline int CDC::GetMapMode() const + // Rretrieves the current mapping mode. + // Possible modes: MM_ANISOTROPIC, MM_HIENGLISH, MM_HIMETRIC, MM_ISOTROPIC, MM_LOENGLISH, MM_LOMETRIC, MM_TEXT, and MM_TWIPS. + { + assert(m_pData->hDC); + return ::GetMapMode(m_pData->hDC); + } + + inline BOOL CDC::GetViewportOrgEx(LPPOINT lpPoint) const + // Retrieves the x-coordinates and y-coordinates of the viewport origin for the device context. + { + assert(m_pData->hDC); + return ::GetViewportOrgEx(m_pData->hDC, lpPoint); + } + + inline int CDC::SetMapMode(int nMapMode) const + // Sets the mapping mode of the specified device context. + { + assert(m_pData->hDC); + return ::SetMapMode(m_pData->hDC, nMapMode); + } + + inline BOOL CDC::SetViewportOrgEx(int x, int y, LPPOINT lpPoint /* = NULL */) const + // Specifies which device point maps to the window origin (0,0). + { + assert(m_pData->hDC); + return ::SetViewportOrgEx(m_pData->hDC, x, y, lpPoint); + } + + inline BOOL CDC::SetViewportOrgEx(POINT point, LPPOINT lpPointRet /* = NULL */) const + // Specifies which device point maps to the window origin (0,0). + { + assert(m_pData->hDC); + return SetViewportOrgEx(point.x, point.y, lpPointRet); + } + + inline BOOL CDC::OffsetViewportOrgEx(int nWidth, int nHeight, LPPOINT lpPoint /* = NULL */) const + // Modifies the viewport origin for the device context using the specified horizontal and vertical offsets. + { + assert(m_pData->hDC); + return ::OffsetViewportOrgEx(m_pData->hDC, nWidth, nHeight, lpPoint); + } + + inline BOOL CDC::GetViewportExtEx(LPSIZE lpSize) const + // Retrieves the x-extent and y-extent of the current viewport for the device context. + { + assert(m_pData->hDC); + return ::GetViewportExtEx(m_pData->hDC, lpSize); + } + + inline BOOL CDC::SetViewportExtEx(int x, int y, LPSIZE lpSize ) const + // Sets the horizontal and vertical extents of the viewport for the device context by using the specified values. + { + assert(m_pData->hDC); + return ::SetViewportExtEx(m_pData->hDC, x, y, lpSize); + } + + inline BOOL CDC::SetViewportExtEx(SIZE size, LPSIZE lpSizeRet ) const + // Sets the horizontal and vertical extents of the viewport for the device context by using the specified values. + { + assert(m_pData->hDC); + return SetViewportExtEx(size.cx, size.cy, lpSizeRet); + } + + inline BOOL CDC::ScaleViewportExtEx(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize ) const + // Modifies the viewport for the device context using the ratios formed by the specified multiplicands and divisors. + { + assert(m_pData->hDC); + return ::ScaleViewportExtEx(m_pData->hDC, xNum, xDenom, yNum, yDenom, lpSize); + } + + inline BOOL CDC::GetWindowOrgEx(LPPOINT lpPoint) const + // Retrieves the x-coordinates and y-coordinates of the window origin for the device context. + { + assert(m_pData->hDC); + return ::GetWindowOrgEx(m_pData->hDC, lpPoint); + } + + inline BOOL CDC::SetWindowOrgEx(int x, int y, LPPOINT lpPoint ) const + // Specifies which window point maps to the viewport origin (0,0). + { + assert(m_pData->hDC); + return ::SetWindowOrgEx(m_pData->hDC, x, y, lpPoint); + } + + inline BOOL CDC::SetWindowOrgEx(POINT point, LPPOINT lpPointRet ) const + // Specifies which window point maps to the viewport origin (0,0). + { + assert(m_pData->hDC); + return SetWindowOrgEx(point.x, point.y, lpPointRet); + } + + inline BOOL CDC::OffsetWindowOrgEx(int nWidth, int nHeight, LPPOINT lpPoint ) const + // Modifies the window origin for the device context using the specified horizontal and vertical offsets. + { + assert(m_pData->hDC); + return ::OffsetWindowOrgEx(m_pData->hDC, nWidth, nHeight, lpPoint); + } + + inline BOOL CDC::GetWindowExtEx(LPSIZE lpSize) const + // Retrieves the x-extent and y-extent of the window for the device context. + { + assert(m_pData->hDC); + return ::GetWindowExtEx(m_pData->hDC, lpSize); + } + + inline BOOL CDC::SetWindowExtEx(int x, int y, LPSIZE lpSize ) const + // Sets the horizontal and vertical extents of the window for the device context by using the specified values. + { + assert(m_pData->hDC); + return ::SetWindowExtEx(m_pData->hDC, x, y, lpSize); + } + + inline BOOL CDC::SetWindowExtEx(SIZE size, LPSIZE lpSizeRet) const + // Sets the horizontal and vertical extents of the window for the device context by using the specified values. + { + assert(m_pData->hDC); + return SetWindowExtEx(size.cx, size.cy, lpSizeRet); + } + + inline BOOL CDC::ScaleWindowExtEx(int xNum, int xDenom, int yNum, int yDenom, LPSIZE lpSize) const + // Modifies the window for the device context using the ratios formed by the specified multiplicands and divisors. + { + assert(m_pData->hDC); + return ::ScaleWindowExtEx(m_pData->hDC, xNum, xDenom, yNum, yDenom, lpSize); + } +#endif + + // Printer Functions + inline int CDC::StartDoc(LPDOCINFO lpDocInfo) const + // Starts a print job. + { + assert(m_pData->hDC); + return ::StartDoc(m_pData->hDC, lpDocInfo); + } + + inline int CDC::EndDoc() const + // Ends a print job. + { + assert(m_pData->hDC); + return ::EndDoc(m_pData->hDC); + } + + inline int CDC::StartPage() const + // Prepares the printer driver to accept data. + { + assert(m_pData->hDC); + return ::StartPage(m_pData->hDC); + } + + inline int CDC::EndPage() const + // Notifies the device that the application has finished writing to a page. + { + assert(m_pData->hDC); + return ::EndPage(m_pData->hDC); + } + + inline int CDC::AbortDoc() const + // Stops the current print job and erases everything drawn since the last call to the StartDoc function. + { + assert(m_pData->hDC); + return ::AbortDoc(m_pData->hDC); + } + + inline int CDC::SetAbortProc(BOOL (CALLBACK* lpfn)(HDC, int)) const + // Sets the application-defined abort function that allows a print job to be canceled during spooling. + { + assert(m_pData->hDC); + return ::SetAbortProc(m_pData->hDC, lpfn); + } + + // Text Functions + inline BOOL CDC::ExtTextOut(int x, int y, UINT nOptions, LPCRECT lprc, LPCTSTR lpszString, int nCount /*= -1*/, LPINT lpDxWidths /*=NULL*/) const + // Draws text using the currently selected font, background color, and text color + { + assert(m_pData->hDC); + + if (nCount == -1) + nCount = lstrlen (lpszString); + + return ::ExtTextOut(m_pData->hDC, x, y, nOptions, lprc, lpszString, nCount, lpDxWidths ); + } + + inline int CDC::DrawText(LPCTSTR lpszString, int nCount, LPRECT lprc, UINT nFormat) const + // Draws formatted text in the specified rectangle + { + assert(m_pData->hDC); + return ::DrawText(m_pData->hDC, lpszString, nCount, lprc, nFormat ); + } + + inline UINT CDC::GetTextAlign() const + // Retrieves the text-alignment setting + // Values: TA_BASELINE, TA_BOTTOM, TA_TOP, TA_CENTER, TA_LEFT, TA_RIGHT, TA_RTLREADING, TA_NOUPDATECP, TA_UPDATECP + { + assert(m_pData->hDC); + return ::GetTextAlign(m_pData->hDC); + } + + inline UINT CDC::SetTextAlign(UINT nFlags) const + // Sets the text-alignment setting + // Values: TA_BASELINE, TA_BOTTOM, TA_TOP, TA_CENTER, TA_LEFT, TA_RIGHT, TA_RTLREADING, TA_NOUPDATECP, TA_UPDATECP + { + assert(m_pData->hDC); + return ::SetTextAlign(m_pData->hDC, nFlags); + } + + inline int CDC::GetTextFace(int nCount, LPTSTR lpszFacename) const + // Retrieves the typeface name of the font that is selected into the device context + { + assert(m_pData->hDC); + return ::GetTextFace(m_pData->hDC, nCount, lpszFacename); + } + + inline BOOL CDC::GetTextMetrics(TEXTMETRIC& Metrics) const + // Fills the specified buffer with the metrics for the currently selected font + { + assert(m_pData->hDC); + return ::GetTextMetrics(m_pData->hDC, &Metrics); + } + + inline COLORREF CDC::GetBkColor() const + // Returns the current background color + { + assert(m_pData->hDC); + return ::GetBkColor(m_pData->hDC); + } + + inline COLORREF CDC::SetBkColor(COLORREF crColor) const + // Sets the current background color to the specified color value + { + assert(m_pData->hDC); + return ::SetBkColor(m_pData->hDC, crColor); + } + + inline COLORREF CDC::GetTextColor() const + // Retrieves the current text color + { + assert(m_pData->hDC); + return ::GetTextColor(m_pData->hDC); + } + + inline COLORREF CDC::SetTextColor(COLORREF crColor) const + // Sets the current text color + { + assert(m_pData->hDC); + return ::SetTextColor(m_pData->hDC, crColor); + } + + inline int CDC::GetBkMode() const + // returns the current background mix mode (OPAQUE or TRANSPARENT) + { + assert(m_pData->hDC); + return ::GetBkMode(m_pData->hDC); + } + + inline int CDC::SetBkMode(int iBkMode) const + // Sets the current background mix mode (OPAQUE or TRANSPARENT) + { + assert(m_pData->hDC); + return ::SetBkMode(m_pData->hDC, iBkMode); + } + +#ifndef _WIN32_WCE + inline int CDC::DrawTextEx(LPTSTR lpszString, int nCount, LPRECT lprc, UINT nFormat, LPDRAWTEXTPARAMS lpDTParams) const + // Draws formatted text in the specified rectangle with more formatting options + { + assert(m_pData->hDC); + return ::DrawTextEx(m_pData->hDC, lpszString, nCount, lprc, nFormat, lpDTParams); + } + + inline CSize CDC::GetTextExtentPoint32(LPCTSTR lpszString, int nCount) const + // Computes the width and height of the specified string of text + { + assert(m_pData->hDC); + CSize sz; + ::GetTextExtentPoint32(m_pData->hDC, lpszString, nCount, &sz); + return sz; + } + + inline CSize CDC::GetTabbedTextExtent(LPCTSTR lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions) const + // Computes the width and height of a character string + { + assert(m_pData->hDC); + DWORD dwSize = ::GetTabbedTextExtent(m_pData->hDC, lpszString, nCount, nTabPositions, lpnTabStopPositions ); + CSize sz(dwSize); + return sz; + } + + inline BOOL CDC::GrayString(CBrush* pBrush, GRAYSTRINGPROC lpOutputFunc, LPARAM lpData, int nCount, int x, int y, int nWidth, int nHeight) const + // Draws gray text at the specified location + { + assert(m_pData->hDC); + assert(pBrush); + return ::GrayString(m_pData->hDC, *pBrush, lpOutputFunc, lpData, nCount, x, y, nWidth, nHeight); + } + + inline int CDC::SetTextJustification(int nBreakExtra, int nBreakCount) const + // Specifies the amount of space the system should add to the break characters in a string of text + { + assert(m_pData->hDC); + return ::SetTextJustification(m_pData->hDC, nBreakExtra, nBreakCount); + } + + inline int CDC::GetTextCharacterExtra() const + // Retrieves the current intercharacter spacing for the device context + { + assert(m_pData->hDC); + return ::GetTextCharacterExtra(m_pData->hDC); + } + + inline int CDC::SetTextCharacterExtra(int nCharExtra) const + // Sets the intercharacter spacing + { + assert(m_pData->hDC); + return ::SetTextCharacterExtra(m_pData->hDC, nCharExtra); + } + + inline CSize CDC::TabbedTextOut(int x, int y, LPCTSTR lpszString, int nCount, int nTabPositions, LPINT lpnTabStopPositions, int nTabOrigin) const + // Writes a character string at a specified location, expanding tabs to the values specified in an array of tab-stop positions + { + assert(m_pData->hDC); + DWORD dwSize = ::TabbedTextOut(m_pData->hDC, x, y, lpszString, nCount, nTabPositions, lpnTabStopPositions, nTabOrigin ); + CSize sz(dwSize); + return sz; + } + + inline BOOL CDC::TextOut(int x, int y, LPCTSTR lpszString, int nCount/* = -1*/) const + // Writes a character string at the specified location + { + assert(m_pData->hDC); + if (nCount == -1) + nCount = lstrlen (lpszString); + + return ::TextOut(m_pData->hDC, x, y, lpszString, nCount); + } + +#endif + + + + ///////////////////////////////////////////////////////////////// + // Definitions for some global functions in the Win32xx namespace + // + +#ifndef _WIN32_WCE + inline void TintBitmap (CBitmap* pbmSource, int cRed, int cGreen, int cBlue) + // Modifies the colour of the supplied Device Dependant Bitmap, by the colour + // correction values specified. The correction values can range from -255 to +255. + // This function gains its speed by accessing the bitmap colour information + // directly, rather than using GetPixel/SetPixel. + { + // Create our LPBITMAPINFO object + CBitmapInfoPtr pbmi(pbmSource); + pbmi->bmiHeader.biBitCount = 24; + + // Create the reference DC for GetDIBits to use + CMemDC MemDC(NULL); + + // Use GetDIBits to create a DIB from our DDB, and extract the colour data + MemDC.GetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, NULL, pbmi, DIB_RGB_COLORS); + std::vector<byte> vBits(pbmi->bmiHeader.biSizeImage, 0); + byte* pByteArray = &vBits[0]; + + MemDC.GetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, pByteArray, pbmi, DIB_RGB_COLORS); + UINT nWidthBytes = pbmi->bmiHeader.biSizeImage/pbmi->bmiHeader.biHeight; + + // Ensure sane colour correction values + cBlue = MIN(cBlue, 255); + cBlue = MAX(cBlue, -255); + cRed = MIN(cRed, 255); + cRed = MAX(cRed, -255); + cGreen = MIN(cGreen, 255); + cGreen = MAX(cGreen, -255); + + // Pre-calculate the RGB modification values + int b1 = 256 - cBlue; + int g1 = 256 - cGreen; + int r1 = 256 - cRed; + + int b2 = 256 + cBlue; + int g2 = 256 + cGreen; + int r2 = 256 + cRed; + + // Modify the colour + int yOffset = 0; + int xOffset; + int Index; + for (int Row=0; Row < pbmi->bmiHeader.biHeight; Row++) + { + xOffset = 0; + + for (int Column=0; Column < pbmi->bmiHeader.biWidth; Column++) + { + // Calculate Index + Index = yOffset + xOffset; + + // Adjust the colour values + if (cBlue > 0) + pByteArray[Index] = (BYTE)(cBlue + (((pByteArray[Index] *b1)) >>8)); + else if (cBlue < 0) + pByteArray[Index] = (BYTE)((pByteArray[Index] *b2) >>8); + + if (cGreen > 0) + pByteArray[Index+1] = (BYTE)(cGreen + (((pByteArray[Index+1] *g1)) >>8)); + else if (cGreen < 0) + pByteArray[Index+1] = (BYTE)((pByteArray[Index+1] *g2) >>8); + + if (cRed > 0) + pByteArray[Index+2] = (BYTE)(cRed + (((pByteArray[Index+2] *r1)) >>8)); + else if (cRed < 0) + pByteArray[Index+2] = (BYTE)((pByteArray[Index+2] *r2) >>8); + + // Increment the horizontal offset + xOffset += pbmi->bmiHeader.biBitCount >> 3; + } + + // Increment vertical offset + yOffset += nWidthBytes; + } + + // Save the modified colour back into our source DDB + MemDC.SetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, pByteArray, pbmi, DIB_RGB_COLORS); + } + + inline void GrayScaleBitmap(CBitmap* pbmSource) + { + // Create our LPBITMAPINFO object + CBitmapInfoPtr pbmi(pbmSource); + + // Create the reference DC for GetDIBits to use + CMemDC MemDC(NULL); + + // Use GetDIBits to create a DIB from our DDB, and extract the colour data + MemDC.GetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, NULL, pbmi, DIB_RGB_COLORS); + std::vector<byte> vBits(pbmi->bmiHeader.biSizeImage, 0); + byte* pByteArray = &vBits[0]; + + MemDC.GetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, pByteArray, pbmi, DIB_RGB_COLORS); + UINT nWidthBytes = pbmi->bmiHeader.biSizeImage/pbmi->bmiHeader.biHeight; + + int yOffset = 0; + int xOffset; + int Index; + + for (int Row=0; Row < pbmi->bmiHeader.biHeight; Row++) + { + xOffset = 0; + + for (int Column=0; Column < pbmi->bmiHeader.biWidth; Column++) + { + // Calculate Index + Index = yOffset + xOffset; + + BYTE byGray = (BYTE) ((pByteArray[Index] + pByteArray[Index+1]*6 + pByteArray[Index+2] *3)/10); + pByteArray[Index] = byGray; + pByteArray[Index+1] = byGray; + pByteArray[Index+2] = byGray; + + // Increment the horizontal offset + xOffset += pbmi->bmiHeader.biBitCount >> 3; + } + + // Increment vertical offset + yOffset += nWidthBytes; + } + + // Save the modified colour back into our source DDB + MemDC.SetDIBits(pbmSource, 0, pbmi->bmiHeader.biHeight, pByteArray, pbmi, DIB_RGB_COLORS); + } + + inline HIMAGELIST CreateDisabledImageList(HIMAGELIST himlNormal) + // Returns a greyed image list, created from hImageList + { + int cx, cy; + int nCount = ImageList_GetImageCount(himlNormal); + if (0 == nCount) + return NULL; + + ImageList_GetIconSize(himlNormal, &cx, &cy); + + // Create the disabled ImageList + HIMAGELIST himlDisabled = ImageList_Create(cx, cy, ILC_COLOR24 | ILC_MASK, nCount, 0); + + // Process each image in the ImageList + for (int i = 0 ; i < nCount; ++i) + { + CClientDC DesktopDC(NULL); + CMemDC MemDC(NULL); + CBitmap* pOldBitmap = MemDC.CreateCompatibleBitmap(&DesktopDC, cx, cx); + CRect rc; + rc.SetRect(0, 0, cx, cx); + + // Set the mask color to grey for the new ImageList + COLORREF crMask = RGB(200, 199, 200); + if ( GetDeviceCaps(DesktopDC, BITSPIXEL) < 24) + { + HPALETTE hPal = (HPALETTE)GetCurrentObject(DesktopDC, OBJ_PAL); + UINT Index = GetNearestPaletteIndex(hPal, crMask); + if (Index != CLR_INVALID) crMask = PALETTEINDEX(Index); + } + + MemDC.SolidFill(crMask, rc); + + // Draw the image on the memory DC + ImageList_SetBkColor(himlNormal, crMask); + ImageList_Draw(himlNormal, i, MemDC, 0, 0, ILD_NORMAL); + + // Convert colored pixels to gray + for (int x = 0 ; x < cx; ++x) + { + for (int y = 0; y < cy; ++y) + { + COLORREF clr = ::GetPixel(MemDC, x, y); + + if (clr != crMask) + { + BYTE byGray = (BYTE) (95 + (GetRValue(clr) *3 + GetGValue(clr)*6 + GetBValue(clr))/20); + MemDC.SetPixel(x, y, RGB(byGray, byGray, byGray)); + } + + } + } + + // Detach the bitmap so we can use it. + CBitmap* pBitmap = MemDC.SelectObject(pOldBitmap); + ImageList_AddMasked(himlDisabled, *pBitmap, crMask); + } + + return himlDisabled; + } +#endif + + //////////////////////////////////////////// + // Global Function Definitions + // + + inline CDC* FromHandle(HDC hDC) + // Returns the CDC object associated with the device context handle + // If a CDC object doesn't already exist, a temporary CDC object is created. + // The HDC belonging to a temporary CDC is not released or destroyed when the + // temporary CDC is deconstructed. + { + assert( GetApp() ); + CDC* pDC = GetApp()->GetCDCFromMap(hDC); + if (hDC != 0 && pDC == 0) + { + pDC = new CDC; + GetApp()->AddTmpDC(pDC); + pDC->m_pData->hDC = hDC; + pDC->m_pData->bRemoveHDC = FALSE; + } + return pDC; + } + + inline CBitmap* FromHandle(HBITMAP hBitmap) + // Returns the CBitmap associated with the Bitmap handle + // If a CBitmap object doesn't already exist, a temporary CBitmap object is created. + // The HBITMAP belonging to a temporary CBitmap is not released or destroyed + // when the temporary CBitmap is deconstructed. + { + assert( GetApp() ); + CBitmap* pBitmap = (CBitmap*)GetApp()->GetCGDIObjectFromMap(hBitmap); + if (hBitmap != 0 && pBitmap == 0) + { + pBitmap = new CBitmap; + GetApp()->AddTmpGDI(pBitmap); + pBitmap->m_pData->hGDIObject = hBitmap; + pBitmap->m_pData->bRemoveObject = FALSE; + } + return pBitmap; + } + + inline CBrush* FromHandle(HBRUSH hBrush) + // Returns the CBrush associated with the Brush handle + // If a CBrush object doesn't already exist, a temporary CBrush object is created. + // The HBRUSH belonging to a temporary CBrush is not released or destroyed + // when the temporary CBrush is deconstructed. + { + assert( GetApp() ); + CBrush* pBrush = (CBrush*)GetApp()->GetCGDIObjectFromMap(hBrush); + if (hBrush != 0 && pBrush == 0) + { + pBrush = new CBrush; + GetApp()->AddTmpGDI(pBrush); + pBrush->m_pData->hGDIObject = hBrush; + pBrush->m_pData->bRemoveObject = FALSE; + } + return pBrush; + } + + inline CFont* FromHandle(HFONT hFont) + // Returns the CFont associated with the Font handle + // If a CFont object doesn't already exist, a temporary CFont object is created. + // The HFONT belonging to a temporary CFont is not released or destroyed + // when the temporary CFont is deconstructed. + { + assert( GetApp() ); + CFont* pFont = (CFont*)GetApp()->GetCGDIObjectFromMap(hFont); + if (hFont != 0 && pFont == 0) + { + pFont = new CFont; + GetApp()->AddTmpGDI(pFont); + pFont->m_pData->hGDIObject = hFont; + pFont->m_pData->bRemoveObject = FALSE; + } + return pFont; + } + + inline CPalette* FromHandle(HPALETTE hPalette) + // Returns the CPalette associated with the palette handle + // If a CPalette object doesn't already exist, a temporary CPalette object is created. + // The HPALETTE belonging to a temporary CPalette is not released or destroyed + // when the temporary CPalette is deconstructed. + { + assert( GetApp() ); + CPalette* pPalette = (CPalette*)GetApp()->GetCGDIObjectFromMap(hPalette); + if (hPalette != 0 && pPalette == 0) + { + pPalette = new CPalette; + GetApp()->AddTmpGDI(pPalette); + pPalette->m_pData->hGDIObject = hPalette; + pPalette->m_pData->bRemoveObject = FALSE; + } + return pPalette; + } + + inline CPen* FromHandle(HPEN hPen) + // Returns the CPen associated with the HPEN. + // If a CPen object doesn't already exist, a temporary CPen object is created. + // The HPEN belonging to a temporary CPen is not released or destroyed + // when the temporary CPen is deconstructed. + { + assert( GetApp() ); + CPen* pPen = (CPen*)GetApp()->GetCGDIObjectFromMap(hPen); + if (hPen != 0 && pPen == 0) + { + pPen = new CPen; + GetApp()->AddTmpGDI(pPen); + pPen->m_pData->hGDIObject = hPen; + pPen->m_pData->bRemoveObject = FALSE; + } + return pPen; + } + + inline CRgn* FromHandle(HRGN hRgn) + // Returns the CRgn associated with the HRGN. + // If a CRgn object doesn't already exist, a temporary CRgn object is created. + // The HRGN belonging to a temporary CRgn is not released or destroyed + // when the temporary CRgn is deconstructed. + { + assert( GetApp() ); + CRgn* pRgn = (CRgn*)GetApp()->GetCGDIObjectFromMap(hRgn); + if (hRgn != 0 && pRgn == 0) + { + pRgn = new CRgn; + GetApp()->AddTmpGDI(pRgn); + pRgn->m_pData->hGDIObject = hRgn; + pRgn->m_pData->bRemoveObject = FALSE; + } + return pRgn; + } + + + +} // namespace Win32xx + +#endif // _WIN32XX_GDI_H_ + diff --git a/mmc_updater/depends/win32cpp/info.txt b/mmc_updater/depends/win32cpp/info.txt new file mode 100644 index 00000000..a4dbda8e --- /dev/null +++ b/mmc_updater/depends/win32cpp/info.txt @@ -0,0 +1,205 @@ +Generic Information about Win32++ Projects +========================================== +The various directories may contain the following types of files: + +Extension | Description +----------+------------ +cbp | A project file used by CodeBlocks +dsp | A project file used by Visual Studio 6 +dsw | A project file used by Visual Studio 6 +sln | A project file used by Visual Studio 2003, VS2005 or VS2008 +vcproj | A project file used by Visual Studio 2003, VS2005 or VS2008 +vcxproj | A project file used by Visual Studio 2010 +filters | A supplementary project file used by Visual Studio 2010 +bdsproj | A project file used by Borland Developer Studio 2006 +bpf | A project file used by Borland Developer Studio 2006 +vcp | A project file used by eMbedded Visual C++ +vcw | A project file used by eMbedded Visual C++ +dev | A project file used by Dev-C++ +cpp | A C++ source file +h | A C++ header file +rc | A C++ resouce script file +jpg | A jpeg resource file +ico | An icon resource file +bmp | A bitmap resource file +cur | A cursor resource file +manifest | A manifest resource file +txt | A text file +xml | An Extensible Markup Language file (defines the ribbon UI) + +Supported Compilers and Integrated Development Environments (IDEs) +================================================================== +Win32++ supports the following: +* Borland Compiler Version 5.5 +* Borland Developer Studio 2006 +* Borland Turbo C++ 2006 +* CodeBlocks +* Dev-C++ +* MinGW GCC Compiler +* Visual Studio 6 +* Visual Studio.net 2003 +* Visual C++ Toolkit 2003 +* Visual Studio.net 2005 +* Visual Studio.net 2005 Express +* Visual Studio.net 2008 +* Visual Studio.net 2008 Express +* Visual Studio.net 2010 + +CodeBlocks is an IDE. The project files are configured for the following +compilers: +* Borland Compiler Version 5.5 +* MinGW GNU compiler +* Visual C++ Toolkit 2003 + +Dev-C++ is an IDE which supports the MinGW GNU compiler + +Supported Operating Systems +=========================== +The programs compiled with Win32++ can run on the following operating systems: +* Win95 (all versions, with or without Internet Explorer 4 installed) +* Win98 (both versions) +* WinME +* Windows NT 4 +* Windows 2000 +* Windows XP +* Windows XP x64 +* Windows Vista +* Windows Vista x64 +* Windows 7 +* Windows 7 x64 +* Windows Server 2003 +* Windows Server 2003 x64 +* Windows Server 2008 +* Windows Server 2008 x64 +* Windows CE + +Note: Programs compiled with Visual Studio.net 2008 and Visual Studio.net 2008 +Express will not run on Win32 operating systems earlier than Windows 2000. + +Win32++ automatically detects if the operating system is capable of using +rebars. If rebars are not supported by the OS, Win32++ produces a frame without +rebars. + +Win32++ is Unicode compliant and can therefore be used to develop Unicode +applications. Users are advised that older operating systems (namely Win95, +Win98 and WinME) don't support Unicode applications. + +Win32++ supports 64bit compilers, and can be used to develop 64bit code. + +Directory Structure +=================== +When extracting the files from the zip archive, be sure to preserve the +directory structure. The directory structure will typically look like this: + +.\include +.\new projects +.\output +.\samples +.\tools +.\tutorials +.\WCE samples + +The files which form the Win32++ library are contained in the include +subdirectory. + +Components of Win32++ +===================== + + Files | Classes | Operating Systems | Description +==================+==================+===================+===================== +controls.h | CAnimation | Win32, Win64 | Adds support for the + | CComboBox | and WinCE | following controls: + | CComboBoxEx | | Animation, ComboBox, + | CProgressBar | | ComboBoxEx, Progress + | CScrollBar | | bar, Scroll bar, + | CSlider | | Slider, Spin button. + | CSpinButton | | +------------------+------------------+-------------------+--------------------- +dialog.h | CDialog | Win32, Win64 | Adds dialog support. + | CResizer | WinCE for CDialog | +------------------+------------------+-------------------+--------------------- +docking.h | CDocker | Win32, Win64 | Adds support for + | CDockContainer | | docking windows and + | | | splitter windows. +------------------+------------------+-------------------+--------------------- +frame.h | CMenubar | Win32, Win64 | Adds support for + | CFrame | | frames. Frames use a + | | | toolbar and menubar + | | | inside a rebar, and + | | | a statusbar. +------------------+------------------+-------------------+--------------------- +gdi.h | CDC | Win32, Win64 | A helper class for + | CBitmap | and WinCE | GDI graphics. + | CBrush | | + | CFont | | + | CPalette | | + | CPen | | + | CRgn | | +------------------+------------------+-------------------+--------------------- +listView.h | CListView | Win32, Win64 | Adds support for a + | | and WinCE | ListView control. +------------------+------------------+-------------------+--------------------- +mdi.h | CMDIFrame | Win32, Win64 | Adds support for MDI + | CMDIChild | | frames. +------------------+------------------+-------------------+--------------------- +propertysheet.h | CPropertySheet | Win32, Win64 | Adds property sheet + | CPropertyPage | and WinCE | support. +------------------+------------------+-------------------+--------------------- +rebar.h | CRebar | Win32, Win64 | Adds support for a + | | and WinCE | Rebar control. +------------------+------------------+-------------------+--------------------- +ribbon.h | CRibbon | Win32, Win64 | Adds support for the + | CRibbonFrame | | Windows 7 ribbon. +------------------+------------------+-------------------+--------------------- +shared_ptr.h | Shared_Ptr | Win32, Win64, | Add a smart pointer + | | and WinCE | for use in vectors. +------------------+------------------+-------------------+--------------------- +socket.h | CSocket | Win32, Win64 | Adds network + | | and WinCE | support. +------------------+------------------+-------------------+--------------------- +splitter.h | CSplitter | Win32, Win64 | Adds splitter support + | | | (depreciated) +------------------+------------------+-------------------+---------------------- +statusbar.h | CStatusbar | Win32, Win64 | Adds support for a + | | and WinCE | Status bar control. +------------------+------------------+-------------------+--------------------- +stdcontrols.h | CButton | Win32, Win64 | Adds support for + | CEdit | and WinCE | Button, Edit, + | CListBox | | ListBox and Static + | CStatic | | controls. +------------------+------------------+-------------------+--------------------- +tab.h | CTab | Win32, Win64 | Adds support for tab + | CMDITab | | controls, and MDI + | | | tab windows. +------------------+------------------+-------------------+--------------------- +taskdialog.h | CTaskDialog | Win32, Win64 | Adds support for tab + | | | task dialogs. +------------------+------------------+-------------------+--------------------- +thread.h | CThread | Win32, Win64 | Adds support for + | | and WinCE | threads. +------------------+------------------+-------------------+--------------------- +toolbar.h | CToolbar | Win32, Win64 | Adds support for a + | | and WinCE | Toolbar control. +------------------+------------------+-------------------+--------------------- +treeview.h | CTreeView | Win32, Win64 | Adds support for a + | | and WinCE | TreeView control. +------------------+------------------+-------------------+--------------------- +wceframe.h | CWceFrame | WinCE only | Adds support for + | CCmdbar | | frames in WinCE. +------------------+------------------+-------------------+--------------------- +webbrowser.h | CAXWindow | Win32, Win64 | Adds support for a + | CWebBrowser | and WinCE | ActiveX container and + | | | a WebBrowser window. +------------------+------------------+-------------------+--------------------- +wincore.h | CCriticalSection | Win32, Win64, | The core set of + | CWinApp | and WinCE | classes required for + | CWinException | | all Win32++ + | CWnd | | applications. +------------------+------------------+-------------------+--------------------- +winutils.h | CPoint | Win32, Win64, | Additional utility + | CRect | and WinCE | classes. + | CSize | | +------------------+------------------+-------------------+--------------------- + +Refer to the help documentation that ships with Win32++ for more information on +using Win32++.
\ No newline at end of file diff --git a/mmc_updater/depends/win32cpp/listview.h b/mmc_updater/depends/win32cpp/listview.h new file mode 100644 index 00000000..810e7627 --- /dev/null +++ b/mmc_updater/depends/win32cpp/listview.h @@ -0,0 +1,867 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + + +#ifndef _WIN32XX_LISTVIEW_H_ +#define _WIN32XX_LISTVIEW_H_ + +#include "wincore.h" +#include "commctrl.h" + +namespace Win32xx +{ + + class CListView : public CWnd + { + public: + CListView() {} + virtual ~CListView() {} + virtual void PreRegisterClass(WNDCLASS &wc); + + // Attributes + CSize ApproximateViewRect(CSize sz = CSize(-1, -1), int iCount = -1) const; + COLORREF GetBkColor( ) const; + BOOL GetBkImage( LVBKIMAGE& lvbkImage ) const; + UINT GetCallbackMask( ) const; + BOOL GetCheckState( UINT nItem ) const; + BOOL GetColumn( int iCol, LVCOLUMN& Column ) const; + BOOL GetColumnOrderArray( LPINT piArray, int iCount = -1 ); + int GetColumnWidth( int iCol ) const; + int GetCountPerPage( ) const; + HWND GetEditControl( ) const; + DWORD GetExtendedStyle( ) const; + HWND GetHeader( ) const; + HCURSOR GetHotCursor( ); + int GetHotItem( ) const; + DWORD GetHoverTime( ) const; + HIMAGELIST GetImageList( int nImageType ) const; + BOOL GetItem( LVITEM& lvItem ) const; + int GetItemCount( ) const; + DWORD_PTR GetItemData( int iItem ) const; + BOOL GetItemPosition( int iItem, CPoint& pt ) const; + BOOL GetItemRect( int iItem, CRect& rc, UINT nCode ) const; + UINT GetItemState( int iItem, UINT nMask ) const; + tString GetItemText( int iItem, int iSubItem, UINT nTextMax = 260 ) const; + int GetNextItem( int iItem, int iFlags ) const; + UINT GetNumberOfWorkAreas( ) const; + BOOL GetOrigin( CPoint& pt ) const; + UINT GetSelectedCount( ) const; + int GetSelectionMark( ) const; + int GetStringWidth( LPCTSTR pszString ) const; + BOOL GetSubItemRect( int iItem, int iSubItem, int iCode, CRect& rc ) const; + COLORREF GetTextBkColor( ) const; + COLORREF GetTextColor( ) const; + HWND GetToolTips( ) const; + int GetTopIndex( ) const; + BOOL GetViewRect( CRect& rc ) const; + void GetWorkAreas( int iWorkAreas, LPRECT pRectArray ) const; + BOOL SetBkColor( COLORREF clrBk ) const; + BOOL SetBkImage( LVBKIMAGE& plvbkImage ) const; + BOOL SetCallbackMask( UINT nMask ) const; + void SetCheckState( int iItem, BOOL fCheck = TRUE ) const; + BOOL SetColumn( int iCol, const LVCOLUMN& pColumn ) const; + BOOL SetColumnOrderArray( int iCount, LPINT piArray ) const; + BOOL SetColumnWidth( int iCol, int cx ) const; + DWORD SetExtendedStyle( DWORD dwNewStyle ) const; + HCURSOR SetHotCursor( HCURSOR hCursor ) const; + int SetHotItem( int nIndex ) const; + DWORD SetHoverTime( DWORD dwHoverTime = (DWORD)-1 ) const; + CSize SetIconSpacing( int cx, int cy ) const; + CSize SetIconSpacing( CSize sz ) const; + HIMAGELIST SetImageList( HIMAGELIST himl, int iImageListType ) const; + BOOL SetItem( LVITEM& pItem ) const; + BOOL SetItem( int iItem, int iSubItem, UINT nMask, LPCTSTR pszText, int iImage, + UINT nState, UINT nStateMask, LPARAM lParam, int iIndent ) const; + void SetItemCount( int iCount ) const; + void SetItemCountEx( int iCount, DWORD dwFlags = LVSICF_NOINVALIDATEALL ) const; + BOOL SetItemData( int iItem, DWORD_PTR dwData ) const; + BOOL SetItemPosition( int iItem, CPoint& pt ) const; + BOOL SetItemState( int iItem, LVITEM& Item ) const; + void SetItemState( int iItem, UINT nState, UINT nMask ) const; + void SetItemText( int iItem, int iSubItem, LPCTSTR pszText ) const; + int SetSelectionMark( int iIndex ) const; + BOOL SetTextBkColor( COLORREF clrBkText ) const; + BOOL SetTextColor( COLORREF clrText ) const; + HWND SetToolTips( HWND hWndToolTip ) const; + void SetWorkAreas( int nWorkAreas, CRect& pRectArray ) const; + int SubItemHitTest( LVHITTESTINFO& htInfo ) const; + + // Operations + BOOL Arrange( UINT nCode ) const; + HIMAGELIST CreateDragImage( int iItem, CPoint& pt ) const; + BOOL DeleteAllItems( ) const; + BOOL DeleteColumn( int iCol ) const; + BOOL DeleteItem( int iItem ) const; + HWND EditLabel( int iItem ) const; + BOOL EnsureVisible( int iItem, BOOL fPartialOK ) const; + int FindItem( LVFINDINFO& FindInfo, int iStart = -1 ) const; + int HitTest( LVHITTESTINFO& HitTestInfo ) const; + int HitTest( CPoint pt, UINT* pFlags = NULL ) const; + int InsertColumn( int iCol, const LVCOLUMN& pColumn ) const; + int InsertColumn( int iCol, LPCTSTR pszColumnHeading, int iFormat = LVCFMT_LEFT, + int iWidth = -1, int iSubItem = -1 ) const; + int InsertItem( const LVITEM& pItem ) const; + int InsertItem( int iItem, LPCTSTR pszText ) const; + int InsertItem( int iItem, LPCTSTR pszText, int iImage ) const; + BOOL RedrawItems( int iFirst, int iLast ) const; + BOOL Scroll( CSize sz ) const; + BOOL SortItems( PFNLVCOMPARE pfnCompare, DWORD_PTR dwData ) const; + BOOL Update( int iItem ) const; + + private: + CListView(const CListView&); // Disable copy construction + CListView& operator = (const CListView&); // Disable assignment operator + }; + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +namespace Win32xx +{ + + inline void CListView::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = WC_LISTVIEW; + } + + inline CSize CListView::ApproximateViewRect(CSize sz /*= CSize(-1, -1)*/, int iCount /* = -1*/) const + // Calculates the approximate width and height required to display a given number of items. + { + assert(::IsWindow(m_hWnd)); + return CSize( ListView_ApproximateViewRect( m_hWnd, sz.cx, sz.cy, iCount ) ); + } + + inline COLORREF CListView::GetBkColor( ) const + // Retrieves the background color of a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetBkColor( m_hWnd ); + } + + inline BOOL CListView::GetBkImage( LVBKIMAGE& lvbkImage ) const + // Retrieves the background image in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetBkImage( m_hWnd, &lvbkImage ); + } + + inline UINT CListView::GetCallbackMask( ) const + // Retrieves the callback mask for a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetCallbackMask( m_hWnd ); + } + + inline BOOL CListView::GetCheckState( UINT nItem ) const + // Determines if an item in a list-view control is selected. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetCheckState( m_hWnd, nItem ); + } + + inline BOOL CListView::GetColumn( int iCol, LVCOLUMN& Column ) const + // Retrieves the attributes of a list-view control's column. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetColumn( m_hWnd, iCol, &Column ); + } + + inline BOOL CListView::GetColumnOrderArray( LPINT piArray, int iCount /*= -1*/ ) + // Retrieves the current left-to-right order of columns in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetColumnOrderArray( m_hWnd, iCount, piArray ); + } + + inline int CListView::GetColumnWidth( int iCol ) const + // Retrieves the width of a column in report or list view. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetColumnWidth( m_hWnd, iCol ); + } + + inline int CListView::GetCountPerPage( ) const + // Calculates the number of items that can fit vertically in the visible area of a + // list-view control when in list or report view. Only fully visible items are counted. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetCountPerPage( m_hWnd ); + } + + inline HWND CListView::GetEditControl( ) const + // Retrieves the handle to the edit control being used to edit a list-view item's text. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetEditControl( m_hWnd ); + } + + inline DWORD CListView::GetExtendedStyle( ) const + // Retrieves the extended styles that are currently in use for a given list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetExtendedListViewStyle( m_hWnd ); + } + + inline HWND CListView::GetHeader( ) const + // Retrieves the handle to the header control used by a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetHeader( m_hWnd ); + } + + inline HCURSOR CListView::GetHotCursor( ) + // Retrieves the HCURSOR used when the pointer is over an item while hot tracking is enabled. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetHotCursor( m_hWnd ); + } + + inline int CListView::GetHotItem( ) const + // Retrieves the index of the hot item. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetHotItem( m_hWnd ); + } + + inline DWORD CListView::GetHoverTime( ) const + // Retrieves the amount of time that the mouse cursor must hover over an item before it is selected. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetHoverTime( m_hWnd ); + } + + inline HIMAGELIST CListView::GetImageList( int nImageType ) const + // Retrieves the handle to an image list used for drawing list-view items. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetImageList( m_hWnd, nImageType ); + } + + inline BOOL CListView::GetItem( LVITEM& Item ) const + // Retrieves some or all of a list-view item's attributes. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetItem( m_hWnd, &Item ); + } + + inline int CListView::GetItemCount( ) const + // Retrieves the number of items in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetItemCount( m_hWnd ); + } + + inline DWORD_PTR CListView::GetItemData( int iItem ) const + // Retrieves the value(lParam) specific to the item. + { + assert(::IsWindow(m_hWnd)); + + LVITEM lvi = {0}; + lvi.iItem = iItem; + lvi.mask = LVIF_PARAM; + ListView_GetItem(m_hWnd, &lvi); + return lvi.lParam; + } + + inline BOOL CListView::GetItemPosition( int iItem, CPoint& pt ) const + // Retrieves the position of a list-view item. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetItemPosition( m_hWnd, iItem, &pt ); + } + + inline BOOL CListView::GetItemRect( int iItem, CRect& rc, UINT nCode ) const + // Retrieves the bounding rectangle for all or part of an item in the current view. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetItemRect( m_hWnd, iItem, &rc, nCode ); + } + + inline UINT CListView::GetItemState( int iItem, UINT nMask ) const + // Retrieves the state of a list-view item. + + // Possible values of nMask: + // LVIS_CUT The item is marked for a cut-and-paste operation. + // LVIS_DROPHILITED The item is highlighted as a drag-and-drop target. + // LVIS_FOCUSED The item has the focus, so it is surrounded by a standard focus rectangle. + // LVIS_SELECTED The item is selected. + // LVIS_OVERLAYMASK Use this mask to retrieve the item's overlay image index. + // LVIS_STATEIMAGEMASK Use this mask to retrieve the item's state image index. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetItemState( m_hWnd, iItem, nMask ); + } + + inline tString CListView::GetItemText( int iItem, int iSubItem, UINT nTextMax /* = 260 */ ) const + // Retrieves the text of a list-view item. + // Note: Although the list-view control allows any length string to be stored + // as item text, only the first 260 characters are displayed. + { + assert(::IsWindow(m_hWnd)); + + tString t; + if (nTextMax > 0) + { + std::vector<TCHAR> vTChar(nTextMax +1, _T('\0')); + TCHAR* pszText = &vTChar.front(); + LVITEM lvi = {0}; + lvi.iItem = iItem; + lvi.iSubItem = iSubItem; + lvi.mask = LVIF_TEXT; + lvi.cchTextMax = nTextMax; + lvi.pszText = pszText; + ListView_GetItem( m_hWnd, &lvi ); + t = lvi.pszText; + } + return t; + } + + inline int CListView::GetNextItem( int iItem, int iFlags ) const + // Searches for a list-view item that has the specified properties and + // bears the specified relationship to a specified item. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetNextItem( m_hWnd, iItem, iFlags ); + } + + inline UINT CListView::GetNumberOfWorkAreas( ) const + // Retrieves the working areas from a list-view control. + { + assert(::IsWindow(m_hWnd)); + UINT nWorkAreas = 0; + ListView_GetWorkAreas( m_hWnd, nWorkAreas, NULL ); + return nWorkAreas; + } + + inline BOOL CListView::GetOrigin( CPoint& pt ) const + // Retrieves the current view origin for a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetOrigin( m_hWnd, &pt ); + } + + inline UINT CListView::GetSelectedCount( ) const + // Determines the number of selected items in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return (UINT)::SendMessage( m_hWnd, LVM_GETSELECTEDCOUNT, 0L, 0L ); + } + + inline int CListView::GetSelectionMark( ) const + // Retrieves the selection mark from a list-view control. + { + assert(::IsWindow(m_hWnd)); + return (int)::SendMessage( m_hWnd, LVM_GETSELECTIONMARK, 0L, 0L ); + } + + inline int CListView::GetStringWidth( LPCTSTR pszString ) const + // Determines the width of a specified string using the specified list-view control's current font. + { + assert(::IsWindow(m_hWnd)); + return (int)::SendMessage( m_hWnd, LVM_GETSTRINGWIDTH, 0L, (LPARAM)pszString ); + } + + inline BOOL CListView::GetSubItemRect( int iItem, int iSubItem, int iCode, CRect& rc ) const + // Retrieves information about the rectangle that surrounds a subitem in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetSubItemRect( m_hWnd, iItem, iSubItem, iCode, &rc ); + } + + inline COLORREF CListView::GetTextBkColor( ) const + // Retrieves the text background color of a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetTextBkColor( m_hWnd ); + } + + inline COLORREF CListView::GetTextColor( ) const + // Retrieves the text color of a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetTextColor( m_hWnd ); + } + + inline HWND CListView::GetToolTips( ) const + // Retrieves the ToolTip control that the list-view control uses to display ToolTips. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetToolTips( m_hWnd ); + } + + inline int CListView::GetTopIndex( ) const + // Retrieves the index of the topmost visible item when in list or report view. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetTopIndex( m_hWnd ); + } + + inline BOOL CListView::GetViewRect( CRect& rc ) const + // Retrieves the bounding rectangle of all items in the list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_GetViewRect( m_hWnd, &rc ); + } + + inline void CListView::GetWorkAreas( int iWorkAreas, LPRECT pRectArray ) const + // Retrieves the working areas from a list-view control. + { + assert(::IsWindow(m_hWnd)); + ListView_GetWorkAreas( m_hWnd, iWorkAreas, pRectArray ); + } + + inline BOOL CListView::SetBkColor( COLORREF clrBk ) const + // Sets the background color of a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetBkColor( m_hWnd, clrBk ); + } + + inline BOOL CListView::SetBkImage( LVBKIMAGE& lvbkImage ) const + // Sets the background image in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetBkImage( m_hWnd, &lvbkImage ); + } + + inline BOOL CListView::SetCallbackMask( UINT nMask ) const + // Changes the callback mask for a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetCallbackMask( m_hWnd, nMask ); + } + + inline void CListView::SetCheckState( int iItem, BOOL fCheck /*= TRUE*/ ) const + // Used to select or deselect an item in a list-view control. + // This macro should only be used for list-view controls with the LVS_EX_CHECKBOXES style. + { + assert(::IsWindow(m_hWnd)); + ListView_SetItemState(m_hWnd, iItem, INDEXTOSTATEIMAGEMASK((fCheck==TRUE)?2:1),LVIS_STATEIMAGEMASK); + } + + inline BOOL CListView::SetColumn( int iCol, const LVCOLUMN& Column ) const + // Sets the attributes of a list-view column. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetColumn( m_hWnd, iCol, &Column ); + } + + inline BOOL CListView::SetColumnOrderArray( int iCount, LPINT piArray ) const + // Sets the left-to-right order of columns in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetColumnOrderArray( m_hWnd, iCount, piArray ); + } + + inline BOOL CListView::SetColumnWidth( int iCol, int cx ) const + // Used to change the width of a column in report view or the width of all columns in list-view mode. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetColumnWidth( m_hWnd, iCol, cx ); + } + + inline DWORD CListView::SetExtendedStyle( DWORD dwNewStyle ) const + // Sets extended styles for list-view controls. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetExtendedListViewStyle( m_hWnd, dwNewStyle ); + } + + inline HCURSOR CListView::SetHotCursor( HCURSOR hCursor ) const + // Sets the HCURSOR that the list-view control uses when the pointer is + // over an item while hot tracking is enabled. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetHotCursor( m_hWnd, hCursor ); + } + + inline int CListView::SetHotItem( int nIndex ) const + // Sets the hot item in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetHotItem( m_hWnd, nIndex ); + } + + inline DWORD CListView::SetHoverTime( DWORD dwHoverTime /*= (DWORD)-1*/ ) const + // Sets the amount of time that the mouse cursor must hover over an item before it is selected. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetHoverTime( m_hWnd, dwHoverTime ); + } + + inline CSize CListView::SetIconSpacing( int cx, int cy ) const + // Sets the spacing between icons in list-view controls set to the LVS_ICON style. + { + assert(::IsWindow(m_hWnd)); + return CSize( ListView_SetIconSpacing( m_hWnd, cx, cy ) ); + } + + inline CSize CListView::SetIconSpacing( CSize sz ) const + // Sets the spacing between icons in list-view controls set to the LVS_ICON style. + { + assert(::IsWindow(m_hWnd)); + return CSize( ListView_SetIconSpacing( m_hWnd, sz.cx, sz.cy ) ); + } + + inline HIMAGELIST CListView::SetImageList( HIMAGELIST himl, int iImageListType ) const + // Assigns an image list to a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetImageList( m_hWnd, himl, iImageListType ); + } + + inline BOOL CListView::SetItem( LVITEM& Item ) const + // Sets some or all of a list-view item's attributes. + + // The declaration for TVITEM: + // typedef struct _LVITEM { + // UINT mask; + // int iItem; + // int iSubItem; + // UINT state; + // UINT stateMask; + // LPTSTR pszText; + // int cchTextMax; + // int iImage; + // LPARAM lParam; + // } LVITEM, *LVITEM&; + { + assert(::IsWindow(m_hWnd)); + return ListView_SetItem( m_hWnd, &Item ); + } + + inline BOOL CListView::SetItem( int iItem, int iSubItem, UINT nMask, LPCTSTR pszText, int iImage, + UINT nState, UINT nStateMask, LPARAM lParam, int iIndent ) const + // Sets some or all of a list-view item's attributes. + { + assert(::IsWindow(m_hWnd)); + + LVITEM lvi = {0}; + lvi.iItem = iItem; + lvi.iSubItem = iSubItem; + lvi.mask = nMask; + lvi.pszText = (LPTSTR)pszText; + lvi.iImage = iImage; + lvi.state = nState; + lvi.stateMask = nStateMask; + lvi.lParam = lParam; + lvi.iIndent = iIndent; + + return ListView_SetItem( m_hWnd, &lvi); + } + + inline void CListView::SetItemCount( int iCount ) const + // Causes the list-view control to allocate memory for the specified number of items. + { + assert(::IsWindow(m_hWnd)); + ListView_SetItemCount( m_hWnd, iCount ); + } + + inline void CListView::SetItemCountEx( int iCount, DWORD dwFlags /*= LVSICF_NOINVALIDATEALL*/ ) const + // Sets the virtual number of items in a virtual list view. + { + assert(::IsWindow(m_hWnd)); + ListView_SetItemCountEx( m_hWnd, iCount, dwFlags ); + } + + inline BOOL CListView::SetItemData( int iItem, DWORD_PTR dwData ) const + // Sets the value(lParam) specific to the item. + { + assert(::IsWindow(m_hWnd)); + + LVITEM lvi = {0}; + lvi.iItem = iItem; + lvi.lParam = dwData; + lvi.mask = LVIF_PARAM; + return ListView_SetItem(m_hWnd, &lvi); + } + + inline BOOL CListView::SetItemPosition( int iItem, CPoint& pt ) const + // Moves an item to a specified position in a list-view control (in icon or small icon view). + { + assert(::IsWindow(m_hWnd)); + return ListView_SetItemPosition( m_hWnd, iItem, pt.x, pt.y ); + } + + inline BOOL CListView::SetItemState( int iItem, LVITEM& Item ) const + // Changes the state of an item in a list-view control. + + // Possible values of nMask: + // LVIS_CUT The item is marked for a cut-and-paste operation. + // LVIS_DROPHILITED The item is highlighted as a drag-and-drop target. + // LVIS_FOCUSED The item has the focus, so it is surrounded by a standard focus rectangle. + // LVIS_SELECTED The item is selected. + // LVIS_OVERLAYMASK Use this mask to retrieve the item's overlay image index. + // LVIS_STATEIMAGEMASK Use this mask to retrieve the item's state image index. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)::SendMessage(m_hWnd, LVM_SETITEMSTATE, (WPARAM)iItem, (LPARAM)&Item); + } + + inline void CListView::SetItemState( int iItem, UINT nState, UINT nMask ) const + // Changes the state of an item in a list-view control. + { + assert(::IsWindow(m_hWnd)); + ListView_SetItemState(m_hWnd, iItem, nState, nMask); + } + + inline void CListView::SetItemText( int iItem, int iSubItem, LPCTSTR pszText ) const + // Sets the text color of a list-view control. + { + assert(::IsWindow(m_hWnd)); + ListView_SetItemText(m_hWnd, iItem, iSubItem, (LPTSTR)pszText ); + } + + inline int CListView::SetSelectionMark( int iIndex ) const + // Sets the selection mark in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetSelectionMark( m_hWnd, iIndex ); + } + + inline BOOL CListView::SetTextBkColor( COLORREF clrBkText ) const + // Sets the background color of text in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetTextBkColor( m_hWnd, clrBkText ); + } + + inline BOOL CListView::SetTextColor( COLORREF clrText ) const + // Sets the text color of a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SetTextColor( m_hWnd, clrText ); + } + + inline HWND CListView::SetToolTips( HWND hWndToolTip ) const + // Sets the ToolTip control that the list-view control will use to display ToolTips. + { + assert(::IsWindow(m_hWnd)); + return (HWND)::SendMessage(m_hWnd, LVM_SETTOOLTIPS, (WPARAM)hWndToolTip, 0L); + } + + inline void CListView::SetWorkAreas( int nWorkAreas, CRect& pRectArray ) const + // Sets the working area within a list-view control. + { + assert(::IsWindow(m_hWnd)); + ListView_SetWorkAreas( m_hWnd, nWorkAreas, pRectArray ); + } + + inline int CListView::SubItemHitTest( LVHITTESTINFO& htInfo ) const + // Determines which list-view item or subitem is located at a given position. + { + assert(::IsWindow(m_hWnd)); + return ListView_SubItemHitTest( m_hWnd, &htInfo ); + } + + // Operations + + inline BOOL CListView::Arrange( UINT nCode ) const + // Arranges items in icon view. + { + assert(::IsWindow(m_hWnd)); + return ListView_Arrange( m_hWnd, nCode ); + } + + inline HIMAGELIST CListView::CreateDragImage( int iItem, CPoint& pt ) const + // Creates a drag image list for the specified item. + { + assert(::IsWindow(m_hWnd)); + return ListView_CreateDragImage( m_hWnd, iItem, &pt ); + } + + inline BOOL CListView::DeleteAllItems( ) const + // ListView_DeleteAllItems + { + assert(::IsWindow(m_hWnd)); + return ListView_DeleteAllItems( m_hWnd ); + } + + inline BOOL CListView::DeleteColumn( int iCol ) const + // Removes a column from a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_DeleteColumn( m_hWnd, iCol ); + } + + inline BOOL CListView::DeleteItem( int iItem ) const + // Removes an item from a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_DeleteItem( m_hWnd, iItem ); + } + + inline HWND CListView::EditLabel( int iItem ) const + // Begins in-place editing of the specified list-view item's text. + { + assert(::IsWindow(m_hWnd)); + return ListView_EditLabel( m_hWnd, iItem ); + } + + inline BOOL CListView::EnsureVisible( int iItem, BOOL fPartialOK ) const + // Ensures that a list-view item is either entirely or partially visible, + // scrolling the list-view control if necessary. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(LVM_ENSUREVISIBLE, (WPARAM)iItem, (LPARAM)fPartialOK ); + } + + inline int CListView::FindItem( LVFINDINFO& FindInfo, int iStart /*= -1*/ ) const + // Searches for a list-view item with the specified characteristics. + { + assert(::IsWindow(m_hWnd)); + return ListView_FindItem( m_hWnd, iStart, &FindInfo ); + } + + inline int CListView::HitTest( LVHITTESTINFO& HitTestInfo ) const + // Determines which list-view item, if any, is at a specified position. + { + assert(::IsWindow(m_hWnd)); + return ListView_HitTest( m_hWnd, &HitTestInfo ); + } + + inline int CListView::HitTest( CPoint pt, UINT* pFlags /*= NULL*/ ) const + // Determines which list-view item, if any, is at a specified position. + { + assert(::IsWindow(m_hWnd)); + + LVHITTESTINFO hti = {0}; + hti.flags = *pFlags; + hti.pt = pt; + return ListView_HitTest( m_hWnd, &hti ); + } + + inline int CListView::InsertColumn( int iCol, const LVCOLUMN& Column ) const + // Inserts a new column in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_InsertColumn( m_hWnd, iCol, &Column ); + } + + inline int CListView::InsertColumn( int iCol, LPCTSTR pszColumnHeading, int iFormat /*= LVCFMT_LEFT*/, + int iWidth /*= -1*/, int iSubItem /*= -1*/ ) const + // Inserts a new column in a list-view control. + { + assert(::IsWindow(m_hWnd)); + + LVCOLUMN lvc = {0}; + lvc.mask = LVCF_TEXT|LVCF_ORDER|LVCF_FMT; + if (-1 != iWidth) + { + lvc.mask |= LVCF_WIDTH; + lvc.cx = iWidth; + } + if (-1 != iSubItem) + { + lvc.mask |= LVCF_SUBITEM; + lvc.iSubItem = iSubItem; + } + + lvc.iOrder = iCol; + lvc.pszText = (LPTSTR)pszColumnHeading; + lvc.fmt = iFormat; + lvc.iSubItem = iSubItem; + return ListView_InsertColumn( m_hWnd, iCol, &lvc ); + } + + inline int CListView::InsertItem( const LVITEM& Item ) const + // Inserts a new item in a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_InsertItem( m_hWnd, &Item ); + } + + inline int CListView::InsertItem( int iItem, LPCTSTR pszText ) const + // Inserts a new item in a list-view control. + { + assert(::IsWindow(m_hWnd)); + + LVITEM lvi = {0}; + lvi.iItem = iItem; + lvi.pszText = (LPTSTR)pszText; + lvi.mask = LVIF_TEXT; + return ListView_InsertItem( m_hWnd, &lvi ); + } + + inline int CListView::InsertItem( int iItem, LPCTSTR pszText, int iImage ) const + // Inserts a new item in a list-view control. + { + assert(::IsWindow(m_hWnd)); + + LVITEM lvi = {0}; + lvi.iItem = iItem; + lvi.pszText = (LPTSTR)pszText; + lvi.iImage = iImage; + lvi.mask = LVIF_TEXT | LVIF_IMAGE; + return ListView_InsertItem( m_hWnd, &lvi ); + } + + inline BOOL CListView::RedrawItems( int iFirst, int iLast ) const + // Forces a list-view control to redraw a range of items. + { + assert(::IsWindow(m_hWnd)); + return ListView_RedrawItems( m_hWnd, iFirst, iLast ); + } + + inline BOOL CListView::Scroll( CSize sz ) const + // Scrolls the content of a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_Scroll( m_hWnd, sz.cx, sz.cy ); + } + + inline BOOL CListView::SortItems( PFNLVCOMPARE pfnCompare, DWORD_PTR dwData ) const + // Uses an application-defined comparison function to sort the items of a list-view control. + { + assert(::IsWindow(m_hWnd)); + return ListView_SortItems( m_hWnd, pfnCompare, dwData ); + } + + inline BOOL CListView::Update( int iItem ) const + // Updates a list-view item. If the list-view control has the LVS_AUTOARRANGE style, + // the list-view control is rearranged. + { + assert(::IsWindow(m_hWnd)); + return ListView_Update( m_hWnd, iItem ); + } + +} // namespace Win32xx + +#endif // #ifndef _WIN32XX_LISTVIEW_H_ + diff --git a/mmc_updater/depends/win32cpp/mdi.h b/mmc_updater/depends/win32cpp/mdi.h new file mode 100644 index 00000000..0aa35ffc --- /dev/null +++ b/mmc_updater/depends/win32cpp/mdi.h @@ -0,0 +1,783 @@ +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// mdi.h +// Declaration of the CMDIChild and CMDIFrame classes + +// The classes defined here add MDI frames support to Win32++. MDI +// (Multiple Document Interface) frames host one or more child windows. The +// child windows hosted by a MDI frame can be different types. For example, +// some MDI child windows could be used to edit text, while others could be +// used to display a bitmap. Four classes are defined here to support MDI +// frames: + + +// 1) CMDIFrame. This class inherits from CFrame, and adds the functionality +// required by MDI frames. It keeps track of the MDI children created and +// destroyed, and adjusts the menu when a MDI child is activated. Use the +// AddMDIChild function to add MDI child windows to the MDI frame. Inherit +// from CMDIFrame to create your own MDI frame. +// +// 2) CMDIChild: All MDI child windows (ie. CWnd classes) should inherit from +// this class. Each MDI child type can have a different frame menu. + +// Use the MDIFrame generic application as the starting point for your own MDI +// frame applications. +// Refer to the MDIDemo sample for an example on how to use these classes to +// create a MDI frame application with different types of MDI child windows. + + +#ifndef _WIN32XX_MDI_H_ +#define _WIN32XX_MDI_H_ + +#include "frame.h" +#include <vector> + + + +namespace Win32xx +{ + class CMDIChild; + class CMDIFrame; + typedef Shared_Ptr<CMDIChild> MDIChildPtr; + + ///////////////////////////////////// + // Declaration of the CMDIChild class + // + class CMDIChild : public CWnd + { + friend class CMDIFrame; + public: + CMDIChild(); + virtual ~CMDIChild(); + + // These are the functions you might wish to override + virtual HWND Create(CWnd* pParent = NULL); + virtual void RecalcLayout(); + + // These functions aren't virtual, and shouldn't be overridden + void SetHandles(HMENU MenuName, HACCEL AccelName); + CMDIFrame* GetMDIFrame() const; + CWnd* GetView() const {return m_pView;} + void SetView(CWnd& pwndView); + void MDIActivate() const; + void MDIDestroy() const; + void MDIMaximize() const; + void MDIRestore() const; + + protected: + // Its unlikely you would need to override these functions + virtual LRESULT FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void OnCreate(); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CMDIChild(const CMDIChild&); // Disable copy construction + CMDIChild& operator = (const CMDIChild&); // Disable assignment operator + + CWnd* m_pView; // pointer to the View CWnd object + HMENU m_hChildMenu; + HACCEL m_hChildAccel; + }; + + + ///////////////////////////////////// + // Declaration of the CMDIFrame class + // + class CMDIFrame : public CFrame + { + friend class CMDIChild; // CMDIChild uses m_hOrigMenu + typedef Shared_Ptr<CMDIChild> MDIChildPtr; + + public: + class CMDIClient : public CWnd // a nested class within CMDIFrame + { + public: + CMDIClient() {} + virtual ~CMDIClient() {} + virtual HWND Create(CWnd* pParent = NULL); + virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + CMDIFrame* GetMDIFrame() const { return (CMDIFrame*)GetParent(); } + + private: + CMDIClient(const CMDIClient&); // Disable copy construction + CMDIClient& operator = (const CMDIClient&); // Disable assignment operator + }; + + + CMDIFrame(); + virtual ~CMDIFrame() {} + + virtual CMDIChild* AddMDIChild(MDIChildPtr pMDIChild); + virtual CMDIClient& GetMDIClient() const { return (CMDIClient&)m_MDIClient; } + virtual BOOL IsMDIFrame() const { return TRUE; } + virtual void RemoveMDIChild(HWND hWnd); + virtual BOOL RemoveAllMDIChildren(); + virtual void UpdateCheckMarks(); + + // These functions aren't virtual, so don't override them + std::vector <MDIChildPtr>& GetAllMDIChildren() {return m_vMDIChild;} + CMDIChild* GetActiveMDIChild() const; + BOOL IsMDIChildMaxed() const; + void MDICascade(int nType = 0) const; + void MDIIconArrange() const; + void MDIMaximize() const; + void MDINext() const; + void MDIPrev() const; + void MDIRestore() const; + void MDITile(int nType = 0) const; + void SetActiveMDIChild(CMDIChild* pChild); + + protected: + // These are the functions you might wish to override + virtual void OnClose(); + virtual void OnViewStatusBar(); + virtual void OnViewToolBar(); + virtual void OnWindowPosChanged(); + virtual void RecalcLayout(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CMDIFrame(const CMDIFrame&); // Disable copy construction + CMDIFrame& operator = (const CMDIFrame&); // Disable assignment operator + void AppendMDIMenu(HMENU hMenuWindow); + LRESULT FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + void UpdateFrameMenu(HMENU hMenu); + + CMDIClient m_MDIClient; + std::vector <MDIChildPtr> m_vMDIChild; + HWND m_hActiveMDIChild; + }; + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + ///////////////////////////////////// + // Definitions for the CMDIFrame class + // + inline CMDIFrame::CMDIFrame() : m_hActiveMDIChild(NULL) + { + SetView(GetMDIClient()); + } + + inline CMDIChild* CMDIFrame::AddMDIChild(MDIChildPtr pMDIChild) + { + assert(NULL != pMDIChild.get()); // Cannot add Null MDI Child + + m_vMDIChild.push_back(pMDIChild); + pMDIChild->Create(GetView()); + + return pMDIChild.get(); + } + + inline void CMDIFrame::AppendMDIMenu(HMENU hMenuWindow) + { + // Adds the additional menu items the the "Window" submenu when + // MDI child windows are created + + if (!IsMenu(hMenuWindow)) + return; + + // Delete previously appended items + int nItems = ::GetMenuItemCount(hMenuWindow); + UINT uLastID = ::GetMenuItemID(hMenuWindow, --nItems); + if ((uLastID >= IDW_FIRSTCHILD) && (uLastID < IDW_FIRSTCHILD + 10)) + { + while ((uLastID >= IDW_FIRSTCHILD) && (uLastID < IDW_FIRSTCHILD + 10)) + { + ::DeleteMenu(hMenuWindow, nItems, MF_BYPOSITION); + uLastID = ::GetMenuItemID(hMenuWindow, --nItems); + } + //delete the separator too + ::DeleteMenu(hMenuWindow, nItems, MF_BYPOSITION); + } + + int nWindow = 0; + + // Allocate an iterator for our MDIChild vector + std::vector <MDIChildPtr>::iterator v; + + for (v = GetAllMDIChildren().begin(); v < GetAllMDIChildren().end(); ++v) + { + if ((*v)->GetWindowLongPtr(GWL_STYLE) & WS_VISIBLE) // IsWindowVisible is unreliable here + { + // Add Separator + if (0 == nWindow) + ::AppendMenu(hMenuWindow, MF_SEPARATOR, 0, NULL); + + // Add a menu entry for each MDI child (up to 9) + if (nWindow < 9) + { + tString tsMenuItem ( (*v)->GetWindowText() ); + + if (tsMenuItem.length() > MAX_MENU_STRING -10) + { + // Truncate the string if its too long + tsMenuItem.erase(tsMenuItem.length() - MAX_MENU_STRING +10); + tsMenuItem += _T(" ..."); + } + + TCHAR szMenuString[MAX_MENU_STRING+1]; + wsprintf(szMenuString, _T("&%d %s"), nWindow+1, tsMenuItem.c_str()); + + ::AppendMenu(hMenuWindow, MF_STRING, IDW_FIRSTCHILD + nWindow, szMenuString); + + if (GetActiveMDIChild() == (*v).get()) + ::CheckMenuItem(hMenuWindow, IDW_FIRSTCHILD+nWindow, MF_CHECKED); + + ++nWindow; + } + else if (9 == nWindow) + // For the 10th MDI child, add this menu item and return + { + ::AppendMenu(hMenuWindow, MF_STRING, IDW_FIRSTCHILD + nWindow, _T("&Windows...")); + return; + } + } + } + } + + inline LRESULT CMDIFrame::FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return ::DefFrameProc(m_hWnd, GetMDIClient(), uMsg, wParam, lParam); + } + + inline CMDIChild* CMDIFrame::GetActiveMDIChild() const + { + return (CMDIChild*)FromHandle(m_hActiveMDIChild); + } + + inline BOOL CMDIFrame::IsMDIChildMaxed() const + { + BOOL bMaxed = FALSE; + GetMDIClient().SendMessage(WM_MDIGETACTIVE, 0L, (LPARAM)&bMaxed); + return bMaxed; + } + + inline void CMDIFrame::MDICascade(int nType /* = 0*/) const + { + // Possible values for nType are: + // MDITILE_SKIPDISABLED Prevents disabled MDI child windows from being cascaded. + + assert(::IsWindow(m_hWnd)); + GetView()->SendMessage(WM_MDICASCADE, (WPARAM)nType, 0L); + } + + inline void CMDIFrame::MDIIconArrange() const + { + assert(::IsWindow(m_hWnd)); + GetView()->SendMessage(WM_MDIICONARRANGE, 0L, 0L); + } + + inline void CMDIFrame::MDIMaximize() const + { + assert(::IsWindow(m_hWnd)); + GetView()->SendMessage(WM_MDIMAXIMIZE, 0L, 0L); + } + + inline void CMDIFrame::MDINext() const + { + assert(::IsWindow(m_hWnd)); + HWND hMDIChild = GetActiveMDIChild()->GetHwnd(); + GetView()->SendMessage(WM_MDINEXT, (WPARAM)hMDIChild, FALSE); + } + + inline void CMDIFrame::MDIPrev() const + { + assert(::IsWindow(m_hWnd)); + HWND hMDIChild = GetActiveMDIChild()->GetHwnd(); + GetView()->SendMessage(WM_MDINEXT, (WPARAM)hMDIChild, TRUE); + } + + inline void CMDIFrame::MDIRestore() const + { + assert(::IsWindow(m_hWnd)); + GetView()->SendMessage(WM_MDIRESTORE, 0L, 0L); + } + + inline void CMDIFrame::MDITile(int nType /* = 0*/) const + { + // Possible values for nType are: + // MDITILE_HORIZONTAL Tiles MDI child windows so that one window appears above another. + // MDITILE_SKIPDISABLED Prevents disabled MDI child windows from being tiled. + // MDITILE_VERTICAL Tiles MDI child windows so that one window appears beside another. + + assert(::IsWindow(m_hWnd)); + GetView()->SendMessage(WM_MDITILE, (WPARAM)nType, 0L); + } + + inline void CMDIFrame::OnClose() + { + if (RemoveAllMDIChildren()) + { + CFrame::OnClose(); + Destroy(); + } + } + + inline void CMDIFrame::OnViewStatusBar() + { + CFrame::OnViewStatusBar(); + UpdateCheckMarks(); + GetView()->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); + } + + inline void CMDIFrame::OnViewToolBar() + { + CFrame::OnViewToolBar(); + UpdateCheckMarks(); + GetView()->RedrawWindow(NULL, NULL, RDW_FRAME | RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); + } + + inline void CMDIFrame::OnWindowPosChanged() + { + if (IsMenuBarUsed()) + { + // Refresh MenuBar Window + HMENU hMenu= GetMenuBar().GetMenu(); + GetMenuBar().SetMenu(hMenu); + UpdateCheckMarks(); + } + } + + inline BOOL CMDIFrame::PreTranslateMessage(MSG* pMsg) + { + if (WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST) + { + if (TranslateMDISysAccel(GetView()->GetHwnd(), pMsg)) + return TRUE; + } + + return CFrame::PreTranslateMessage(pMsg); + } + + inline void CMDIFrame::RecalcLayout() + { + CFrame::RecalcLayout(); + + if (GetView()->IsWindow()) + MDIIconArrange(); + } + + inline BOOL CMDIFrame::RemoveAllMDIChildren() + { + BOOL bResult = TRUE; + int Children = (int)m_vMDIChild.size(); + + // Remove the children in reverse order + for (int i = Children-1; i >= 0; --i) + { + if (IDNO == m_vMDIChild[i]->SendMessage(WM_CLOSE, 0L, 0L)) // Also removes the MDI child + bResult = FALSE; + } + + return bResult; + } + + inline void CMDIFrame::RemoveMDIChild(HWND hWnd) + { + // Allocate an iterator for our HWND map + std::vector <MDIChildPtr>::iterator v; + + for (v = m_vMDIChild.begin(); v!= m_vMDIChild.end(); ++v) + { + if ((*v)->GetHwnd() == hWnd) + { + m_vMDIChild.erase(v); + break; + } + } + + if (GetActiveMDIChild()) + { + if (GetActiveMDIChild()->m_hChildMenu) + UpdateFrameMenu(GetActiveMDIChild()->m_hChildMenu); + if (GetActiveMDIChild()->m_hChildAccel) + GetApp()->SetAccelerators(GetActiveMDIChild()->m_hChildAccel, this); + } + else + { + if (IsMenuBarUsed()) + GetMenuBar().SetMenu(GetFrameMenu()); + else + SetMenu(FromHandle(GetFrameMenu())); + + GetApp()->SetAccelerators(GetFrameAccel(), this); + } + } + + inline void CMDIFrame::SetActiveMDIChild(CMDIChild* pChild) + { + assert ( pChild->IsWindow() ); + + GetMDIClient().SendMessage(WM_MDIACTIVATE, (WPARAM)pChild->GetHwnd(), 0L); + + // Verify + assert ( m_hActiveMDIChild == pChild->GetHwnd() ); + } + + inline void CMDIFrame::UpdateCheckMarks() + { + if ((GetActiveMDIChild()) && GetActiveMDIChild()->m_hChildMenu) + { + HMENU hMenu = GetActiveMDIChild()->m_hChildMenu; + + UINT uCheck = GetToolBar().IsWindowVisible()? MF_CHECKED : MF_UNCHECKED; + ::CheckMenuItem(hMenu, IDW_VIEW_TOOLBAR, uCheck); + + uCheck = GetStatusBar().IsWindowVisible()? MF_CHECKED : MF_UNCHECKED; + ::CheckMenuItem (hMenu, IDW_VIEW_STATUSBAR, uCheck); + } + } + + inline void CMDIFrame::UpdateFrameMenu(HMENU hMenu) + { + int nMenuItems = GetMenuItemCount(hMenu); + if (nMenuItems > 0) + { + // The Window menu is typically second from the right + int nWindowItem = MAX (nMenuItems -2, 0); + HMENU hMenuWindow = ::GetSubMenu (hMenu, nWindowItem); + + if (hMenuWindow) + { + if (IsMenuBarUsed()) + { + AppendMDIMenu(hMenuWindow); + GetMenuBar().SetMenu(hMenu); + } + else + { + GetView()->SendMessage (WM_MDISETMENU, (WPARAM) hMenu, (LPARAM)hMenuWindow); + DrawMenuBar(); + } + } + } + UpdateCheckMarks(); + } + + inline LRESULT CMDIFrame::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_CLOSE: + OnClose(); + return 0; + + case WM_WINDOWPOSCHANGED: + // MDI Child or MDI frame has been resized + OnWindowPosChanged(); + break; // Continue with default processing + + } // switch uMsg + return CFrame::WndProcDefault(uMsg, wParam, lParam); + } + + inline HWND CMDIFrame::CMDIClient::Create(CWnd* pParent) + { + assert(pParent != 0); + + CLIENTCREATESTRUCT clientcreate ; + clientcreate.hWindowMenu = m_hWnd; + clientcreate.idFirstChild = IDW_FIRSTCHILD ; + DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | MDIS_ALLCHILDSTYLES; + + // Create the view window + CreateEx(WS_EX_CLIENTEDGE, _T("MDICLient"), TEXT(""), dwStyle, 0, 0, 0, 0, pParent, NULL, (PSTR) &clientcreate); + + return m_hWnd; + } + + inline LRESULT CMDIFrame::CMDIClient::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_MDIDESTROY: + { + // Do default processing first + CallWindowProc(GetPrevWindowProc(), uMsg, wParam, lParam); + + // Now remove MDI child + GetMDIFrame()->RemoveMDIChild((HWND) wParam); + } + return 0; // Discard message + + case WM_MDISETMENU: + { + if (GetMDIFrame()->IsMenuBarUsed()) + { + return 0L; + } + } + break; + + case WM_MDIACTIVATE: + { + // Suppress redraw to avoid flicker when activating maximised MDI children + SendMessage(WM_SETREDRAW, FALSE, 0L); + LRESULT lr = CallWindowProc(GetPrevWindowProc(), WM_MDIACTIVATE, wParam, lParam); + SendMessage(WM_SETREDRAW, TRUE, 0L); + RedrawWindow(0, 0, RDW_FRAME | RDW_INVALIDATE | RDW_ALLCHILDREN); + + return lr; + } + } + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + + ///////////////////////////////////// + //Definitions for the CMDIChild class + // + inline CMDIChild::CMDIChild() : m_pView(NULL), m_hChildMenu(NULL) + { + // Set the MDI Child's menu and accelerator in the constructor, like this ... + // HMENU hChildMenu = LoadMenu(GetApp()->GetResourceHandle(), _T("MdiMenuView")); + // HACCEL hChildAccel = LoadAccelerators(GetApp()->GetResourceHandle(), _T("MDIAccelView")); + // SetHandles(hChildMenu, hChildAccel); + } + + inline CMDIChild::~CMDIChild() + { + if (IsWindow()) + GetParent()->SendMessage(WM_MDIDESTROY, (WPARAM)m_hWnd, 0L); + + if (m_hChildMenu) + ::DestroyMenu(m_hChildMenu); + } + + inline HWND CMDIChild::Create(CWnd* pParent /*= NULL*/) + // We create the MDI child window and then maximize if required. + // This technique avoids unnecessary flicker when creating maximized MDI children. + { + //Call PreCreate in case its overloaded + PreCreate(*m_pcs); + + //Determine if the window should be created maximized + BOOL bMax = FALSE; + pParent->SendMessage(WM_MDIGETACTIVE, 0L, (LPARAM)&bMax); + bMax = bMax | (m_pcs->style & WS_MAXIMIZE); + + // Set the Window Class Name + TCHAR szClassName[MAX_STRING_SIZE + 1] = _T("Win32++ MDI Child"); + if (m_pcs->lpszClass) + lstrcpyn(szClassName, m_pcs->lpszClass, MAX_STRING_SIZE); + + // Set the window style + DWORD dwStyle; + dwStyle = m_pcs->style & ~WS_MAXIMIZE; + dwStyle |= WS_VISIBLE | WS_OVERLAPPEDWINDOW ; + + // Set window size and position + int x = CW_USEDEFAULT; + int y = CW_USEDEFAULT; + int cx = CW_USEDEFAULT; + int cy = CW_USEDEFAULT; + if(m_pcs->cx && m_pcs->cy) + { + x = m_pcs->x; + y = m_pcs->y; + cx = m_pcs->cx; + cy = m_pcs->cy; + } + + // Set the extended style + DWORD dwExStyle = m_pcs->dwExStyle | WS_EX_MDICHILD; + + // Turn off redraw while creating the window + pParent->SendMessage(WM_SETREDRAW, FALSE, 0L); + + // Create the window + if (!CreateEx(dwExStyle, szClassName, m_pcs->lpszName, dwStyle, x, y, + cx, cy, pParent, FromHandle(m_pcs->hMenu), m_pcs->lpCreateParams)) + throw CWinException(_T("CMDIChild::Create ... CreateEx failed")); + + if (bMax) + ShowWindow(SW_MAXIMIZE); + + // Turn redraw back on + pParent->SendMessage(WM_SETREDRAW, TRUE, 0L); + pParent->RedrawWindow(NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); + + // Ensure bits revealed by round corners (XP themes) are redrawn + SetWindowPos(NULL, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED); + + if (m_hChildMenu) + GetMDIFrame()->UpdateFrameMenu(m_hChildMenu); + if (m_hChildAccel) + GetApp()->SetAccelerators(m_hChildAccel, this); + + return m_hWnd; + } + + inline CMDIFrame* CMDIChild::GetMDIFrame() const + { + CMDIFrame* pMDIFrame = (CMDIFrame*)GetParent()->GetParent(); + assert(dynamic_cast<CMDIFrame*>(pMDIFrame)); + return pMDIFrame; + } + + inline LRESULT CMDIChild::FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + return ::DefMDIChildProc(m_hWnd, uMsg, wParam, lParam); + } + + inline void CMDIChild::MDIActivate() const + { + GetParent()->SendMessage(WM_MDIACTIVATE, (WPARAM)m_hWnd, 0L); + } + + inline void CMDIChild::MDIDestroy() const + { + GetParent()->SendMessage(WM_MDIDESTROY, (WPARAM)m_hWnd, 0L); + } + + inline void CMDIChild::MDIMaximize() const + { + GetParent()->SendMessage(WM_MDIMAXIMIZE, (WPARAM)m_hWnd, 0L); + } + + inline void CMDIChild::MDIRestore() const + { + GetParent()->SendMessage(WM_MDIRESTORE, (WPARAM)m_hWnd, 0L); + } + + inline void CMDIChild::OnCreate() + { + // Create the view window + assert(GetView()); // Use SetView in CMDIChild's constructor to set the view window + GetView()->Create(this); + RecalcLayout(); + } + + inline void CMDIChild::RecalcLayout() + { + // Resize the View window + CRect rc = GetClientRect(); + m_pView->SetWindowPos( NULL, rc.left, rc.top, rc.Width(), rc.Height(), SWP_SHOWWINDOW ); + } + + inline void CMDIChild::SetHandles(HMENU hMenu, HACCEL hAccel) + { + m_hChildMenu = hMenu; + m_hChildAccel = hAccel; + + // Note: It is valid to call SetChildMenu before the window is created + if (IsWindow()) + { + CWnd* pWnd = GetMDIFrame()->GetActiveMDIChild(); + if (pWnd == this) + { + if (m_hChildMenu) + GetMDIFrame()->UpdateFrameMenu(m_hChildMenu); + + if (m_hChildAccel) + GetApp()->SetAccelerators(m_hChildAccel, GetMDIFrame()); + } + } + } + + inline void CMDIChild::SetView(CWnd& wndView) + // Sets or changes the View window displayed within the frame + { + if (m_pView != &wndView) + { + // Destroy the existing view window (if any) + if (m_pView) m_pView->Destroy(); + + // Assign the view window + m_pView = &wndView; + + if (m_hWnd) + { + // The frame is already created, so create and position the new view too + assert(GetView()); // Use SetView in CMDIChild's constructor to set the view window + GetView()->Create(this); + RecalcLayout(); + } + } + } + + inline LRESULT CMDIChild::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_MDIACTIVATE: + { + // This child is being activated + if (lParam == (LPARAM) m_hWnd) + { + GetMDIFrame()->m_hActiveMDIChild = m_hWnd; + // Set the menu to child default menu + if (m_hChildMenu) + GetMDIFrame()->UpdateFrameMenu(m_hChildMenu); + if (m_hChildAccel) + GetApp()->SetAccelerators(m_hChildAccel, this); + } + + // No child is being activated + if (0 == lParam) + { + GetMDIFrame()->m_hActiveMDIChild = NULL; + // Set the menu to frame's original menu + GetMDIFrame()->UpdateFrameMenu(GetMDIFrame()->GetFrameMenu()); + GetApp()->SetAccelerators(GetMDIFrame()->GetFrameAccel(), this); + } + } + return 0L ; + + case WM_WINDOWPOSCHANGED: + { + RecalcLayout(); + break; + } + } + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + +} // namespace Win32xx + +#endif // _WIN32XX_MDI_H_ + diff --git a/mmc_updater/depends/win32cpp/menu.h b/mmc_updater/depends/win32cpp/menu.h new file mode 100644 index 00000000..136bafa3 --- /dev/null +++ b/mmc_updater/depends/win32cpp/menu.h @@ -0,0 +1,600 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// menu.h +// Declaration of the CMenu class + +// Notes +// 1) Owner-drawn menus send the WM_MEASUREITEM and WM_DRAWITEM messages +// to the window that owns the menu. To manage owner drawing for menus, +// handle these two messages in the CWnd's WndProc function. +// +// 2) The CMenu pointer returned by FromHandle might be a temporary pointer. It +// should be used immediately, not saved for later use. +// +// 3) The CMenu pointers returned by FromHandle or GetSubMenu do not need +// to be deleted. They are automatically deleted by the Win32++. +// +// 4) CMenu pointers returned by GetSubMenu are deleted when the parent CMenu is +// detached, destroyed or deconstructed. +// +// 5) The HMENU that is attached to a CMenu object (using the attach function) is +// automatically deleted when the CMenu object goes out of scope. Detach the +// HMENU to stop it being deleted when CMenu's destructor is called. +// +// 6) Pass CMenu objects by reference or by pointer when passing them as function +// arguments. +// +// 7) In those functions that use a MENUITEMINFO structure, its cbSize member is +// automatically set to the correct value. + +// Program sample +// -------------- +// void CView::CreatePopup() +// { +// CPoint pt = GetCursorPos(); +// +// // Create the menu +// CMenu Popup; +// Popup.CreatePopupMenu(); +// +// // Add some menu items +// Popup.AppendMenu(MF_STRING, 101, _T("Menu Item &1")); +// Popup.AppendMenu(MF_STRING, 102, _T("Menu Item &2")); +// Popup.AppendMenu(MF_STRING, 103, _T("Menu Item &3")); +// Popup.AppendMenu(MF_SEPARATOR); +// Popup.AppendMenu(MF_STRING, 104, _T("Menu Item &4")); +// +// // Set menu item states +// Popup.CheckMenuRadioItem(101, 101, 101, MF_BYCOMMAND); +// Popup.CheckMenuItem(102, MF_BYCOMMAND | MF_CHECKED); +// Popup.EnableMenuItem(103, MF_BYCOMMAND | MF_GRAYED); +// Popup.SetDefaultItem(104); +// +// // Display the popup menu +// Popup.TrackPopupMenu(0, pt.x, pt.y, this); +// } + + + +#if !defined(_WIN32XX_MENU_H_) && !defined(_WIN32_WCE) +#define _WIN32XX_MENU_H_ + + +#include "wincore.h" +#include "gdi.h" + + +namespace Win32xx +{ + + // Forward declarations + class CBitmap; + + class CMenu + { + friend class CWinApp; + + public: + //Construction + CMenu() : m_hMenu(0), m_IsTmpMenu(FALSE) {} + CMenu(UINT nID) : m_IsTmpMenu(FALSE) + { + m_hMenu = ::LoadMenu(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(nID)); + } + ~CMenu(); + + //Initialization + void Attach(HMENU hMenu); + void CreateMenu(); + void CreatePopupMenu(); + void DestroyMenu(); + HMENU Detach(); + HMENU GetHandle() const; + BOOL LoadMenu(LPCTSTR lpszResourceName); + BOOL LoadMenu(UINT uIDResource); + BOOL LoadMenuIndirect(const void* lpMenuTemplate); + + //Menu Operations + BOOL TrackPopupMenu(UINT uFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect = 0); + BOOL TrackPopupMenuEx(UINT uFlags, int x, int y, CWnd* pWnd, LPTPMPARAMS lptpm); + + //Menu Item Operations + BOOL AppendMenu(UINT uFlags, UINT_PTR uIDNewItem = 0, LPCTSTR lpszNewItem = NULL); + BOOL AppendMenu(UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp); + UINT CheckMenuItem(UINT uIDCheckItem, UINT uCheck); + BOOL CheckMenuRadioItem(UINT uIDFirst, UINT uIDLast, UINT uIDItem, UINT uFlags); + BOOL DeleteMenu(UINT uPosition, UINT uFlags); + UINT EnableMenuItem(UINT uIDEnableItem, UINT uEnable); + UINT GetDefaultItem(UINT gmdiFlags, BOOL fByPos = FALSE); + DWORD GetMenuContextHelpId() const; + +#if(WINVER >= 0x0500) // Minimum OS required is Win2000 + BOOL GetMenuInfo(LPMENUINFO lpcmi) const; + BOOL SetMenuInfo(LPCMENUINFO lpcmi); +#endif + + UINT GetMenuItemCount() const; + UINT GetMenuItemID(int nPos) const; + BOOL GetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos = FALSE); + UINT GetMenuState(UINT uID, UINT uFlags) const; + int GetMenuString(UINT uIDItem, LPTSTR lpString, int nMaxCount, UINT uFlags) const; + int GetMenuString(UINT uIDItem, CString& rString, UINT uFlags) const; + CMenu* GetSubMenu(int nPos); + BOOL InsertMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem = 0, LPCTSTR lpszNewItem = NULL); + BOOL InsertMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp); + BOOL InsertMenuItem(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos = FALSE); + BOOL ModifyMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem = 0, LPCTSTR lpszNewItem = NULL); + BOOL ModifyMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp); + BOOL RemoveMenu(UINT uPosition, UINT uFlags); + BOOL SetDefaultItem(UINT uItem, BOOL fByPos = FALSE); + BOOL SetMenuContextHelpId(DWORD dwContextHelpId); + BOOL SetMenuItemBitmaps(UINT uPosition, UINT uFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked); + BOOL SetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos = FALSE); + + //Operators + BOOL operator != (const CMenu& menu) const; + BOOL operator == (const CMenu& menu) const; + operator HMENU () const; + + private: + CMenu(const CMenu&); // Disable copy construction + CMenu& operator = (const CMenu&); // Disable assignment operator + void AddToMap(); + BOOL RemoveFromMap(); + std::vector<MenuPtr> m_vSubMenus; // A vector of smart pointers to CMenu + HMENU m_hMenu; + BOOL m_IsTmpMenu; + }; + + inline CMenu::~CMenu() + { + if (m_hMenu) + { + if (!m_IsTmpMenu) + { + ::DestroyMenu(m_hMenu); + } + + RemoveFromMap(); + } + + m_vSubMenus.clear(); + } + + inline void CMenu::AddToMap() + // Store the HMENU and CMenu pointer in the HMENU map + { + assert( GetApp() ); + assert(m_hMenu); + + GetApp()->m_csMapLock.Lock(); + GetApp()->m_mapHMENU.insert(std::make_pair(m_hMenu, this)); + GetApp()->m_csMapLock.Release(); + } + + inline BOOL CMenu::RemoveFromMap() + { + BOOL Success = FALSE; + + if (GetApp()) + { + // Allocate an iterator for our HDC map + std::map<HMENU, CMenu*, CompareHMENU>::iterator m; + + CWinApp* pApp = GetApp(); + if (pApp) + { + // Erase the CDC pointer entry from the map + pApp->m_csMapLock.Lock(); + for (m = pApp->m_mapHMENU.begin(); m != pApp->m_mapHMENU.end(); ++m) + { + if (this == m->second) + { + pApp->m_mapHMENU.erase(m); + Success = TRUE; + break; + } + } + + pApp->m_csMapLock.Release(); + } + } + + return Success; + } + + + inline BOOL CMenu::AppendMenu(UINT uFlags, UINT_PTR uIDNewItem /*= 0*/, LPCTSTR lpszNewItem /*= NULL*/) + // Appends a new item to the end of the specified menu bar, drop-down menu, submenu, or shortcut menu. + { + assert(IsMenu(m_hMenu)); + return ::AppendMenu(m_hMenu, uFlags, uIDNewItem, lpszNewItem); + } + + inline BOOL CMenu::AppendMenu(UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp) + // Appends a new item to the end of the specified menu bar, drop-down menu, submenu, or shortcut menu. + { + assert(IsMenu(m_hMenu)); + assert(pBmp); + return ::AppendMenu(m_hMenu, uFlags, uIDNewItem, (LPCTSTR)pBmp->GetHandle()); + } + + inline void CMenu::Attach(HMENU hMenu) + // Attaches an existing menu to this CMenu + { + if (m_hMenu != NULL && m_hMenu != hMenu) + { + ::DestroyMenu(Detach()); + } + + if (hMenu) + { + m_hMenu = hMenu; + AddToMap(); + } + } + + inline UINT CMenu::CheckMenuItem(UINT uIDCheckItem, UINT uCheck) + // Sets the state of the specified menu item's check-mark attribute to either selected or clear. + { + assert(IsMenu(m_hMenu)); + return ::CheckMenuItem(m_hMenu, uIDCheckItem, uCheck); + } + + inline BOOL CMenu::CheckMenuRadioItem(UINT uIDFirst, UINT uIDLast, UINT uIDItem, UINT uFlags) + // Checks a specified menu item and makes it a radio item. At the same time, the function clears + // all other menu items in the associated group and clears the radio-item type flag for those items. + { + assert(IsMenu(m_hMenu)); + return ::CheckMenuRadioItem(m_hMenu, uIDFirst, uIDLast, uIDItem, uFlags); + } + + inline void CMenu::CreateMenu() + // Creates an empty menu. + { + assert(NULL == m_hMenu); + m_hMenu = ::CreateMenu(); + AddToMap(); + } + + inline void CMenu::CreatePopupMenu() + // Creates a drop-down menu, submenu, or shortcut menu. The menu is initially empty. + { + assert(NULL == m_hMenu); + m_hMenu = ::CreatePopupMenu(); + AddToMap(); + } + + inline BOOL CMenu::DeleteMenu(UINT uPosition, UINT uFlags) + // Deletes an item from the specified menu. + { + assert(IsMenu(m_hMenu)); + return ::DeleteMenu(m_hMenu, uPosition, uFlags); + } + + inline void CMenu::DestroyMenu() + // Destroys the menu and frees any memory that the menu occupies. + { + if (::IsMenu(m_hMenu)) + ::DestroyMenu(m_hMenu); + + m_hMenu = 0; + RemoveFromMap(); + m_vSubMenus.clear(); + } + + inline HMENU CMenu::Detach() + // Detaches the HMENU from this CMenu. If the HMENU is not detached it will be + // destroyed when this CMenu is deconstructed. + { + assert(IsMenu(m_hMenu)); + HMENU hMenu = m_hMenu; + m_hMenu = 0; + RemoveFromMap(); + m_vSubMenus.clear(); + return hMenu; + } + + inline HMENU CMenu::GetHandle() const + // Returns the HMENU assigned to this CMenu + { + return m_hMenu; + } + + inline UINT CMenu::EnableMenuItem(UINT uIDEnableItem, UINT uEnable) + // Enables, disables, or grays the specified menu item. + // The uEnable parameter must be a combination of either MF_BYCOMMAND or MF_BYPOSITION + // and MF_ENABLED, MF_DISABLED, or MF_GRAYED. + { + assert(IsMenu(m_hMenu)); + return ::EnableMenuItem(m_hMenu, uIDEnableItem, uEnable); + } + + inline UINT CMenu::GetDefaultItem(UINT gmdiFlags, BOOL fByPos /*= FALSE*/) + // Determines the default menu item. + // The gmdiFlags parameter specifies how the function searches for menu items. + // This parameter can be zero or more of the following values: GMDI_GOINTOPOPUPS; GMDI_USEDISABLED. + { + assert(IsMenu(m_hMenu)); + return ::GetMenuDefaultItem(m_hMenu, fByPos, gmdiFlags); + } + + inline DWORD CMenu::GetMenuContextHelpId() const + // Retrieves the Help context identifier associated with the menu. + { + assert(IsMenu(m_hMenu)); + return ::GetMenuContextHelpId(m_hMenu); + } + +#if(WINVER >= 0x0500) +// minimum OS required : Win2000 + + inline BOOL CMenu::GetMenuInfo(LPMENUINFO lpcmi) const + // Retrieves the menu information. + { + assert(IsMenu(m_hMenu)); + return ::GetMenuInfo(m_hMenu, lpcmi); + } + + inline BOOL CMenu::SetMenuInfo(LPCMENUINFO lpcmi) + // Sets the menu information from the specified MENUINFO structure. + { + assert(IsMenu(m_hMenu)); + return ::SetMenuInfo(m_hMenu, lpcmi); + } + +#endif + + inline UINT CMenu::GetMenuItemCount() const + // Retrieves the number of menu items. + { + assert(IsMenu(m_hMenu)); + return ::GetMenuItemCount(m_hMenu); + } + + inline UINT CMenu::GetMenuItemID(int nPos) const + // Retrieves the menu item identifier of a menu item located at the specified position + { + assert(IsMenu(m_hMenu)); + return ::GetMenuItemID(m_hMenu, nPos); + } + + inline BOOL CMenu::GetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos /*= FALSE*/) + // retrieves information about the specified menu item. + { + assert(IsMenu(m_hMenu)); + assert(lpMenuItemInfo); + lpMenuItemInfo->cbSize = GetSizeofMenuItemInfo(); + return ::GetMenuItemInfo(m_hMenu, uItem, fByPos, lpMenuItemInfo); + } + + inline UINT CMenu::GetMenuState(UINT uID, UINT uFlags) const + // Retrieves the menu flags associated with the specified menu item. + // Possible values for uFlags are: MF_BYCOMMAND (default) or MF_BYPOSITION. + { + assert(IsMenu(m_hMenu)); + return ::GetMenuState(m_hMenu, uID, uFlags); + } + + inline int CMenu::GetMenuString(UINT uIDItem, LPTSTR lpString, int nMaxCount, UINT uFlags) const + // Copies the text string of the specified menu item into the specified buffer. + { + assert(IsMenu(m_hMenu)); + assert(lpString); + return ::GetMenuString(m_hMenu, uIDItem, lpString, nMaxCount, uFlags); + } + + inline int CMenu::GetMenuString(UINT uIDItem, CString& rString, UINT uFlags) const + // Copies the text string of the specified menu item into the specified buffer. + { + assert(IsMenu(m_hMenu)); + return ::GetMenuString(m_hMenu, uIDItem, (LPTSTR)rString.c_str(), rString.GetLength(), uFlags); + } + + inline CMenu* CMenu::GetSubMenu(int nPos) + // Retrieves the CMenu object of a pop-up menu. + { + assert(IsMenu(m_hMenu)); + CMenu* pMenu = new CMenu; + pMenu->m_hMenu = ::GetSubMenu(m_hMenu, nPos); + pMenu->m_IsTmpMenu = TRUE; + m_vSubMenus.push_back(pMenu); + return pMenu; + } + + inline BOOL CMenu::InsertMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem /*= 0*/, LPCTSTR lpszNewItem /*= NULL*/) + // Inserts a new menu item into a menu, moving other items down the menu. + { + assert(IsMenu(m_hMenu)); + return ::InsertMenu(m_hMenu, uPosition, uFlags, uIDNewItem, lpszNewItem); + } + + inline BOOL CMenu::InsertMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp) + // Inserts a new menu item into a menu, moving other items down the menu. + { + assert(IsMenu(m_hMenu)); + return ::InsertMenu(m_hMenu, uPosition, uFlags, uIDNewItem, (LPCTSTR)pBmp->GetHandle()); + } + + inline BOOL CMenu::InsertMenuItem(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos /*= FALSE*/) + // Inserts a new menu item at the specified position in a menu. + { + assert(IsMenu(m_hMenu)); + assert(lpMenuItemInfo); + lpMenuItemInfo->cbSize = GetSizeofMenuItemInfo(); + return ::InsertMenuItem(m_hMenu, uItem, fByPos, lpMenuItemInfo); + } + + inline BOOL CMenu::LoadMenu(LPCTSTR lpszResourceName) + // Loads the menu from the specified windows resource. + { + assert(NULL == m_hMenu); + assert(lpszResourceName); + m_hMenu = ::LoadMenu(GetApp()->GetResourceHandle(), lpszResourceName); + if (m_hMenu) AddToMap(); + return NULL != m_hMenu; + } + + inline BOOL CMenu::LoadMenu(UINT uIDResource) + // Loads the menu from the specified windows resource. + { + assert(NULL == m_hMenu); + m_hMenu = ::LoadMenu(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(uIDResource)); + if (m_hMenu) AddToMap(); + return NULL != m_hMenu; + } + + inline BOOL CMenu::LoadMenuIndirect(const void* lpMenuTemplate) + // Loads the specified menu template and assigns it to this CMenu. + { + assert(NULL == m_hMenu); + assert(lpMenuTemplate); + m_hMenu = ::LoadMenuIndirect(lpMenuTemplate); + if (m_hMenu) AddToMap(); + return NULL != m_hMenu; + } + + inline BOOL CMenu::ModifyMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem /*= 0*/, LPCTSTR lpszNewItem /*= NULL*/) + // Changes an existing menu item. This function is used to specify the content, appearance, and behavior of the menu item. + { + assert(IsMenu(m_hMenu)); + return ::ModifyMenu(m_hMenu, uPosition, uFlags, uIDNewItem, lpszNewItem); + } + + inline BOOL CMenu::ModifyMenu(UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, const CBitmap* pBmp) + // Changes an existing menu item. This function is used to specify the content, appearance, and behavior of the menu item. + { + assert(IsMenu(m_hMenu)); + assert(pBmp); + return ::ModifyMenu(m_hMenu, uPosition, uFlags, uIDNewItem, (LPCTSTR)pBmp->GetHandle()); + } + + inline BOOL CMenu::RemoveMenu(UINT uPosition, UINT uFlags) + // Deletes a menu item or detaches a submenu from the menu. + { + assert(IsMenu(m_hMenu)); + return ::RemoveMenu(m_hMenu, uPosition, uFlags); + } + + inline BOOL CMenu::SetDefaultItem(UINT uItem, BOOL fByPos /*= FALSE*/) + // sets the default menu item for the menu. + { + assert(IsMenu(m_hMenu)); + return ::SetMenuDefaultItem(m_hMenu, uItem, fByPos); + } + + inline BOOL CMenu::SetMenuContextHelpId(DWORD dwContextHelpId) + // Associates a Help context identifier with the menu. + { + assert(IsMenu(m_hMenu)); + return ::SetMenuContextHelpId(m_hMenu, dwContextHelpId); + } + + inline BOOL CMenu::SetMenuItemBitmaps(UINT uPosition, UINT uFlags, const CBitmap* pBmpUnchecked, const CBitmap* pBmpChecked) + // Associates the specified bitmap with a menu item. + { + assert(IsMenu(m_hMenu)); + return ::SetMenuItemBitmaps(m_hMenu, uPosition, uFlags, *pBmpUnchecked, *pBmpChecked); + } + + inline BOOL CMenu::SetMenuItemInfo(UINT uItem, LPMENUITEMINFO lpMenuItemInfo, BOOL fByPos /*= FALSE*/) + // Changes information about a menu item. + { + assert(IsMenu(m_hMenu)); + assert(lpMenuItemInfo); + lpMenuItemInfo->cbSize = GetSizeofMenuItemInfo(); + return ::SetMenuItemInfo(m_hMenu, uItem, fByPos, lpMenuItemInfo); + } + + inline BOOL CMenu::TrackPopupMenu(UINT uFlags, int x, int y, CWnd* pWnd, LPCRECT lpRect /*= 0*/) + // Displays a shortcut menu at the specified location and tracks the selection of items on the menu. + { + assert(IsMenu(m_hMenu)); + HWND hWnd = pWnd? pWnd->GetHwnd() : 0; + return ::TrackPopupMenu(m_hMenu, uFlags, x, y, 0, hWnd, lpRect); + } + + inline BOOL CMenu::TrackPopupMenuEx(UINT uFlags, int x, int y, CWnd* pWnd, LPTPMPARAMS lptpm) + // Displays a shortcut menu at the specified location and tracks the selection of items on the shortcut menu. + { + assert(IsMenu(m_hMenu)); + HWND hWnd = pWnd? pWnd->GetHwnd() : 0; + return ::TrackPopupMenuEx(m_hMenu, uFlags, x, y, hWnd, lptpm); + } + + inline BOOL CMenu::operator != (const CMenu& menu) const + // Returns TRUE if the two menu objects are not equal. + { + return menu.m_hMenu != m_hMenu; + } + + inline BOOL CMenu::operator == (const CMenu& menu) const + // Returns TRUE of the two menu object are equal + { + return menu.m_hMenu == m_hMenu; + } + + inline CMenu::operator HMENU () const + // Retrieves the menu's handle. + { + return m_hMenu; + } + + + /////////////////////////////////////// + // Global functions + // + + inline CMenu* FromHandle(HMENU hMenu) + // Returns the CMenu object associated with the menu handle (HMENU). + { + assert( GetApp() ); + CMenu* pMenu = GetApp()->GetCMenuFromMap(hMenu); + if (::IsMenu(hMenu) && pMenu == 0) + { + GetApp()->AddTmpMenu(hMenu); + pMenu = GetApp()->GetCMenuFromMap(hMenu); + } + return pMenu; + } + +} // namespace Win32xx + +#endif // _WIN32XX_MENU_H_ + diff --git a/mmc_updater/depends/win32cpp/propertysheet.h b/mmc_updater/depends/win32cpp/propertysheet.h new file mode 100644 index 00000000..4048942c --- /dev/null +++ b/mmc_updater/depends/win32cpp/propertysheet.h @@ -0,0 +1,960 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// propertysheet.h +// Declaration of the following classes: +// CPropertyPage and CPropertySheet + +// These classes add support for property sheets to Win32++. A property sheet +// will have one or more property pages. These pages are much like dialogs +// which are presented within a tabbed dialog or within a wizard. The data +// on a property page can be validated before the next page is presented. +// Property sheets have three modes of use: Modal, Modeless, and Wizard. +// +// Refer to the PropertySheet demo program for an example of how propert sheets +// can be used. + + +#ifndef _WIN32XX_PROPERTYSHEET_H_ +#define _WIN32XX_PROPERTYSHEET_H_ + +#include "dialog.h" + +#define ID_APPLY_NOW 0x3021 +#define ID_WIZBACK 0x3023 +#define ID_WIZNEXT 0x3024 +#define ID_WIZFINISH 0x3025 +#define ID_HELP 0xE146 + +#ifndef PROPSHEETHEADER_V1_SIZE + #define PROPSHEETHEADER_V1_SIZE 40 +#endif + +namespace Win32xx +{ + class CPropertyPage; + typedef Shared_Ptr<CPropertyPage> PropertyPagePtr; + + class CPropertyPage : public CWnd + { + public: + CPropertyPage (UINT nIDTemplate, LPCTSTR szTitle = NULL); + virtual ~CPropertyPage() {} + + virtual INT_PTR DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual INT_PTR DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual int OnApply(); + virtual void OnCancel(); + virtual void OnHelp(); + virtual BOOL OnInitDialog(); + virtual BOOL OnKillActive(); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual int OnOK(); + virtual BOOL OnQueryCancel(); + virtual BOOL OnQuerySiblings(WPARAM wParam, LPARAM lParam); + virtual int OnSetActive(); + virtual int OnWizardBack(); + virtual INT_PTR OnWizardFinish(); + virtual int OnWizardNext(); + virtual BOOL PreTranslateMessage(MSG* pMsg); + + static UINT CALLBACK StaticPropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp); + static INT_PTR CALLBACK StaticDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + + void CancelToClose() const; + PROPSHEETPAGE GetPSP() const {return m_PSP;} + BOOL IsButtonEnabled(int iButton) const; + LRESULT QuerySiblings(WPARAM wParam, LPARAM lParam) const; + void SetModified(BOOL bChanged) const; + void SetTitle(LPCTSTR szTitle); + void SetWizardButtons(DWORD dwFlags) const; + + protected: + PROPSHEETPAGE m_PSP; + + private: + CPropertyPage(const CPropertyPage&); // Disable copy construction + CPropertyPage& operator = (const CPropertyPage&); // Disable assignment operator + + tString m_Title; + }; + + class CPropertySheet : public CWnd + { + public: + CPropertySheet(UINT nIDCaption, CWnd* pParent = NULL); + CPropertySheet(LPCTSTR pszCaption = NULL, CWnd* pParent = NULL); + virtual ~CPropertySheet() {} + + // Operations + virtual CPropertyPage* AddPage(CPropertyPage* pPage); + virtual HWND Create(CWnd* pParent = 0); + virtual INT_PTR CreatePropertySheet(LPCPROPSHEETHEADER ppsph); + virtual void DestroyButton(int iButton); + virtual void Destroy(); + virtual int DoModal(); + virtual void RemovePage(CPropertyPage* pPage); + + // State functions + BOOL IsModeless() const; + BOOL IsWizard() const; + + //Attributes + CPropertyPage* GetActivePage() const; + int GetPageCount() const; + int GetPageIndex(CPropertyPage* pPage) const; + HWND GetTabControl() const; + virtual BOOL SetActivePage(int nPage); + virtual BOOL SetActivePage(CPropertyPage* pPage); + virtual void SetIcon(UINT idIcon); + virtual void SetTitle(LPCTSTR szTitle); + virtual void SetWizardMode(BOOL bWizard); + + protected: + virtual BOOL PreTranslateMessage(MSG* pMsg); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CPropertySheet(const CPropertySheet&); // Disable copy construction + CPropertySheet& operator = (const CPropertySheet&); // Disable assignment operator + void BuildPageArray(); + static void CALLBACK Callback(HWND hwnd, UINT uMsg, LPARAM lParam); + + tString m_Title; + std::vector<PropertyPagePtr> m_vPages; // vector of CPropertyPage + std::vector<PROPSHEETPAGE> m_vPSP; // vector of PROPSHEETPAGE + BOOL m_bInitialUpdate; + PROPSHEETHEADER m_PSH; + }; + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// + +namespace Win32xx +{ + + ////////////////////////////////////////// + // Definitions for the CPropertyPage class + // + inline CPropertyPage::CPropertyPage(UINT nIDTemplate, LPCTSTR szTitle /* = NULL*/) + { + ZeroMemory(&m_PSP, sizeof(PROPSHEETPAGE)); + SetTitle(szTitle); + + m_PSP.dwSize = sizeof(PROPSHEETPAGE); + m_PSP.dwFlags |= PSP_USECALLBACK; + m_PSP.hInstance = GetApp()->GetResourceHandle(); + m_PSP.pszTemplate = MAKEINTRESOURCE(nIDTemplate); + m_PSP.pszTitle = m_Title.c_str(); + m_PSP.pfnDlgProc = (DLGPROC)CPropertyPage::StaticDialogProc; + m_PSP.lParam = (LPARAM)this; + m_PSP.pfnCallback = CPropertyPage::StaticPropSheetPageProc; + } + + inline void CPropertyPage::CancelToClose() const + // Disables the Cancel button and changes the text of the OK button to "Close." + { + assert(::IsWindow(m_hWnd)); + SendMessage(PSM_CANCELTOCLOSE, 0L, 0L); + } + + + inline INT_PTR CPropertyPage::DialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + // Override this function in your class derrived from CPropertyPage if you wish to handle messages + // A typical function might look like this: + + // switch (uMsg) + // { + // case MESSAGE1: // Some Win32 API message + // OnMessage1(); // A user defined function + // break; // Also do default processing + // case MESSAGE2: + // OnMessage2(); + // return x; // Don't do default processing, but instead return + // // a value recommended by the Win32 API documentation + // } + + // Always pass unhandled messages on to DialogProcDefault + return DialogProcDefault(uMsg, wParam, lParam); + } + + inline INT_PTR CPropertyPage::DialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + // All DialogProc functions should pass unhandled messages to this function + { + LRESULT lr = 0L; + + switch (uMsg) + { + case UWM_CLEANUPTEMPS: + { + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + pTLSData->vTmpWnds.clear(); + } + break; + + case WM_INITDIALOG: + return OnInitDialog(); + + case PSM_QUERYSIBLINGS: + return (BOOL)OnQuerySiblings(wParam, lParam); + + case WM_COMMAND: + { + // Refelect this message if it's from a control + CWnd* pWnd = GetApp()->GetCWndFromMap((HWND)lParam); + if (pWnd != NULL) + lr = pWnd->OnCommand(wParam, lParam); + + // Handle user commands + if (!lr) + lr = OnCommand(wParam, lParam); + + if (lr) return 0L; + } + break; + + case WM_NOTIFY: + { + // Do Notification reflection if it came from a CWnd object + HWND hwndFrom = ((LPNMHDR)lParam)->hwndFrom; + CWnd* pWndFrom = GetApp()->GetCWndFromMap(hwndFrom); + + if (pWndFrom != NULL) + lr = pWndFrom->OnNotifyReflect(wParam, lParam); + else + { + // Some controls (eg ListView) have child windows. + // Reflect those notifications too. + CWnd* pWndFromParent = GetApp()->GetCWndFromMap(::GetParent(hwndFrom)); + if (pWndFromParent != NULL) + lr = pWndFromParent->OnNotifyReflect(wParam, lParam); + } + + // Handle user notifications + if (!lr) lr = OnNotify(wParam, lParam); + + // Set the return code for notifications + if (IsWindow()) + SetWindowLongPtr(DWLP_MSGRESULT, (LONG_PTR)lr); + + return (BOOL)lr; + } + + case WM_PAINT: + { + if (::GetUpdateRect(m_hWnd, NULL, FALSE)) + { + CPaintDC dc(this); + OnDraw(&dc); + } + else + // RedrawWindow can require repainting without an update rect + { + CClientDC dc(this); + OnDraw(&dc); + } + + break; + } + + case WM_ERASEBKGND: + { + CDC dc((HDC)wParam); + BOOL bResult = OnEraseBkgnd(&dc); + dc.Detach(); + if (bResult) return TRUE; + } + break; + + // A set of messages to be reflected back to the control that generated them + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORDLG: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + case WM_DRAWITEM: + case WM_MEASUREITEM: + case WM_DELETEITEM: + case WM_COMPAREITEM: + case WM_CHARTOITEM: + case WM_VKEYTOITEM: + case WM_HSCROLL: + case WM_VSCROLL: + case WM_PARENTNOTIFY: + return MessageReflect(m_hWnd, uMsg, wParam, lParam); + + } // switch(uMsg) + return FALSE; + + } // INT_PTR CALLBACK CPropertyPage::DialogProc(...) + + inline BOOL CPropertyPage::IsButtonEnabled(int iButton) const + { + assert(::IsWindow(m_hWnd)); + return GetParent()->GetDlgItem(iButton)->IsWindowEnabled(); + } + + inline int CPropertyPage::OnApply() + { + // This function is called for each page when the Apply button is pressed + // Override this function in your derived class if required. + + // The possible return values are: + // PSNRET_NOERROR. The changes made to this page are valid and have been applied + // PSNRET_INVALID. The property sheet will not be destroyed, and focus will be returned to this page. + // PSNRET_INVALID_NOCHANGEPAGE. The property sheet will not be destroyed, and focus will be returned; + + return PSNRET_NOERROR; + } + + inline void CPropertyPage::OnCancel() + { + // This function is called for each page when the Cancel button is pressed + // Override this function in your derived class if required. + } + + inline void CPropertyPage::OnHelp() + { + // This function is called in response to the PSN_HELP notification. + SendMessage(m_hWnd, WM_COMMAND, ID_HELP, 0L); + } + + inline BOOL CPropertyPage::OnQueryCancel() + { + // Called when the cancel button is pressed, and before the cancel has taken place + // Returns TRUE to prevent the cancel operation, or FALSE to allow it. + + return FALSE; // Allow cancel to proceed + } + + inline BOOL CPropertyPage::OnQuerySiblings(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + // Responds to a query request from the Property Sheet. + // The values for wParam and lParam are the ones set by + // the CPropertySheet::QuerySiblings call + + // return FALSE to allow other siblings to be queried, or + // return TRUE to stop query at this page. + + return FALSE; + } + + inline BOOL CPropertyPage::OnInitDialog() + { + // Called when the property page is created + // Override this function in your derived class if required. + + return TRUE; // Pass Keyboard control to handle in WPARAM + } + + inline BOOL CPropertyPage::OnKillActive() + { + // This is called in response to a PSN_KILLACTIVE notification, which + // is sent whenever the OK or Apply button is pressed. + // It provides an opportunity to validate the page contents before it's closed. + // Return TRUE to prevent the page from losing the activation, or FALSE to allow it. + + return FALSE; + } + + inline int CPropertyPage::OnOK() + { + // Called for each page when the OK button is pressed + // Override this function in your derived class if required. + + // The possible return values are: + // PSNRET_NOERROR. The changes made to this page are valid and have been applied + // PSNRET_INVALID. The property sheet will not be destroyed, and focus will be returned to this page. + // PSNRET_INVALID_NOCHANGEPAGE. The property sheet will not be destroyed, and focus will be returned; + + return PSNRET_NOERROR; + } + + inline LRESULT CPropertyPage::OnNotify(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + LPPSHNOTIFY pNotify = (LPPSHNOTIFY)lParam; + switch(pNotify->hdr.code) + { + case PSN_SETACTIVE: + return OnSetActive(); + case PSN_KILLACTIVE: + return OnKillActive(); + case PSN_APPLY: + if (pNotify->lParam) + return OnOK(); + else + return OnApply(); + case PSN_RESET: + OnCancel(); + return FALSE; + case PSN_QUERYCANCEL: + return OnQueryCancel(); + case PSN_WIZNEXT: + return OnWizardNext(); + case PSN_WIZBACK: + return OnWizardBack(); + case PSN_WIZFINISH: + return OnWizardFinish(); + case PSN_HELP: + OnHelp(); + return TRUE; + } + return FALSE; + } + + inline int CPropertyPage::OnSetActive() + { + // Called when a page becomes active + // Override this function in your derived class if required. + + // Returns zero to accept the activation, or -1 to activate the next or the previous page (depending + // on whether the user clicked the Next or Back button). To set the activation to a particular page, + // return the resource identifier of the page. + + return 0; + } + + inline int CPropertyPage::OnWizardBack() + { + // This function is called when the Back button is pressed on a wizard page + // Override this function in your derived class if required. + + // Returns 0 to allow the wizard to go to the previous page. Returns -1 to prevent the wizard + // from changing pages. To display a particular page, return its dialog resource identifier. + + return 0; + } + + inline INT_PTR CPropertyPage::OnWizardFinish() + { + // This function is called when the Finish button is pressed on a wizard page + // Override this function in your derived class if required. + + // Return Value: + // Return non-zero to prevent the wizard from finishing. + // Version 5.80. and later. Return a window handle to prevent the wizard from finishing. The wizard will set the focus to that window. The window must be owned by the wizard page. + // Return 0 to allow the wizard to finish. + + return 0; // Allow wizard to finish + } + + inline int CPropertyPage::OnWizardNext() + { + // This function is called when the Next button is pressed on a wizard page + // Override this function in your derived class if required. + + // Return 0 to allow the wizard to go to the next page. Return -1 to prevent the wizard from + // changing pages. To display a particular page, return its dialog resource identifier. + + return 0; + } + + inline BOOL CPropertyPage::PreTranslateMessage(MSG* pMsg) + { + // allow the tab control to translate keyboard input + if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && + (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT)) + { + CWnd* pWndParent = GetParent(); + if (pWndParent->SendMessage(PSM_ISDIALOGMESSAGE, 0L, (LPARAM)pMsg)) + return TRUE; + } + + // allow the dialog to translate keyboard input + if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST)) + { + if (IsDialogMessage(pMsg)) + return TRUE; + } + + return CWnd::PreTranslateMessage(pMsg); + } + + inline LRESULT CPropertyPage::QuerySiblings(WPARAM wParam, LPARAM lParam) const + { + // Sent to a property sheet, which then forwards the message to each of its pages. + // Set wParam and lParam to values you want passed to the property pages. + // Returns the nonzero value from a page in the property sheet, or zero if no page returns a nonzero value. + + assert(::IsWindow(m_hWnd)); + return GetParent()->SendMessage(PSM_QUERYSIBLINGS, wParam, lParam); + } + + inline void CPropertyPage::SetModified(BOOL bChanged) const + { + // The property sheet will enable the Apply button if bChanged is TRUE. + + assert(::IsWindow(m_hWnd)); + + if (bChanged) + GetParent()->SendMessage(PSM_CHANGED, (WPARAM)m_hWnd, 0L); + else + GetParent()->SendMessage(PSM_UNCHANGED, (WPARAM)m_hWnd, 0L); + } + + inline void CPropertyPage::SetTitle(LPCTSTR szTitle) + { + if (szTitle) + { + m_Title = szTitle; + m_PSP.dwFlags |= PSP_USETITLE; + } + else + { + m_Title.erase(); + m_PSP.dwFlags &= ~PSP_USETITLE; + } + + m_PSP.pszTitle = m_Title.c_str(); + } + + inline void CPropertyPage::SetWizardButtons(DWORD dwFlags) const + { + // dwFlags: A value that specifies which wizard buttons are enabled. You can combine one or more of the following flags. + // PSWIZB_BACK Enable the Back button. If this flag is not set, the Back button is displayed as disabled. + // PSWIZB_DISABLEDFINISH Display a disabled Finish button. + // PSWIZB_FINISH Display an enabled Finish button. + // PSWIZB_NEXT Enable the Next button. If this flag is not set, the Next button is displayed as disabled. + + assert (::IsWindow(m_hWnd)); + PropSheet_SetWizButtons(::GetParent(m_hWnd), dwFlags); + } + + inline UINT CALLBACK CPropertyPage::StaticPropSheetPageProc(HWND hwnd, UINT uMsg, LPPROPSHEETPAGE ppsp) + { + assert( GetApp() ); + UNREFERENCED_PARAMETER(hwnd); + + // Note: the hwnd is always NULL + + switch (uMsg) + { + case PSPCB_CREATE: + { + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + assert(pTLSData); + + // Store the CPropertyPage pointer in Thread Local Storage + pTLSData->pCWnd = (CWnd*)ppsp->lParam; + } + break; + } + + return TRUE; + } + + inline INT_PTR CALLBACK CPropertyPage::StaticDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + assert( GetApp() ); + + // Find matching CWnd pointer for this HWND + CPropertyPage* pPage = (CPropertyPage*)GetApp()->GetCWndFromMap(hwndDlg); + if (0 == pPage) + { + // matching CWnd pointer not found, so add it to HWNDMap now + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + pPage = (CPropertyPage*)pTLSData->pCWnd; + + // Set the hWnd members and call DialogProc for this message + pPage->m_hWnd = hwndDlg; + pPage->AddToMap(); + } + + return pPage->DialogProc(uMsg, wParam, lParam); + } + + + /////////////////////////////////////////// + // Definitions for the CPropertySheet class + // + inline CPropertySheet::CPropertySheet(UINT nIDCaption, CWnd* pParent /* = NULL*/) + { + ZeroMemory(&m_PSH, sizeof (PROPSHEETHEADER)); + SetTitle(LoadString(nIDCaption)); + m_bInitialUpdate = FALSE; + +#ifdef _WIN32_WCE + m_PSH.dwSize = sizeof(PROPSHEETHEADER); +#else + if (GetComCtlVersion() >= 471) + m_PSH.dwSize = sizeof(PROPSHEETHEADER); + else + m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE; +#endif + + m_PSH.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK; + m_PSH.hwndParent = pParent? pParent->GetHwnd() : 0; + m_PSH.hInstance = GetApp()->GetInstanceHandle(); + m_PSH.pfnCallback = (PFNPROPSHEETCALLBACK)CPropertySheet::Callback; + } + + inline CPropertySheet::CPropertySheet(LPCTSTR pszCaption /*= NULL*/, CWnd* pParent /* = NULL*/) + { + ZeroMemory(&m_PSH, sizeof (PROPSHEETHEADER)); + SetTitle(pszCaption); + m_bInitialUpdate = FALSE; + +#ifdef _WIN32_WCE + m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE; +#else + if (GetComCtlVersion() >= 471) + m_PSH.dwSize = sizeof(PROPSHEETHEADER); + else + m_PSH.dwSize = PROPSHEETHEADER_V1_SIZE; +#endif + + m_PSH.dwFlags = PSH_PROPSHEETPAGE | PSH_USECALLBACK; + m_PSH.hwndParent = pParent? pParent->GetHwnd() : 0;; + m_PSH.hInstance = GetApp()->GetInstanceHandle(); + m_PSH.pfnCallback = (PFNPROPSHEETCALLBACK)CPropertySheet::Callback; + } + + inline CPropertyPage* CPropertySheet::AddPage(CPropertyPage* pPage) + // Adds a Property Page to the Property Sheet + { + assert(NULL != pPage); + + m_vPages.push_back(PropertyPagePtr(pPage)); + + if (m_hWnd) + { + // property sheet already exists, so add page to it + PROPSHEETPAGE psp = pPage->GetPSP(); + HPROPSHEETPAGE hpsp = ::CreatePropertySheetPage(&psp); + PropSheet_AddPage(m_hWnd, hpsp); + } + + m_PSH.nPages = (int)m_vPages.size(); + + return pPage; + } + + inline void CPropertySheet::BuildPageArray() + // Builds the PROPSHEETPAGE array + { + m_vPSP.clear(); + std::vector<PropertyPagePtr>::iterator iter; + for (iter = m_vPages.begin(); iter < m_vPages.end(); ++iter) + m_vPSP.push_back((*iter)->GetPSP()); + + PROPSHEETPAGE* pPSPArray = &m_vPSP.front(); // Array of PROPSHEETPAGE + m_PSH.ppsp = pPSPArray; + } + + inline void CALLBACK CPropertySheet::Callback(HWND hwnd, UINT uMsg, LPARAM lParam) + { + assert( GetApp() ); + + switch(uMsg) + { + //called before the dialog is created, hwnd = NULL, lParam points to dialog resource + case PSCB_PRECREATE: + { + LPDLGTEMPLATE lpTemplate = (LPDLGTEMPLATE)lParam; + + if(!(lpTemplate->style & WS_SYSMENU)) + { + lpTemplate->style |= WS_SYSMENU; + } + } + break; + + //called after the dialog is created + case PSCB_INITIALIZED: + { + // Retrieve pointer to CWnd object from Thread Local Storage + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + assert(pTLSData); + + CPropertySheet* w = (CPropertySheet*)pTLSData->pCWnd; + assert(w); + + w->Attach(hwnd); + w->OnCreate(); + } + break; + } + } + + + inline HWND CPropertySheet::Create(CWnd* pParent /*= 0*/) + // Creates a modeless Property Sheet + { + assert( GetApp() ); + + if (pParent) + { + m_PSH.hwndParent = pParent->GetHwnd(); + } + + BuildPageArray(); + PROPSHEETPAGE* pPSPArray = &m_vPSP.front(); + m_PSH.ppsp = pPSPArray; + + // Create a modeless Property Sheet + m_PSH.dwFlags &= ~PSH_WIZARD; + m_PSH.dwFlags |= PSH_MODELESS; + HWND hWnd = (HWND)CreatePropertySheet(&m_PSH); + + return hWnd; + } + + inline INT_PTR CPropertySheet::CreatePropertySheet(LPCPROPSHEETHEADER ppsph) + { + assert( GetApp() ); + + INT_PTR ipResult = 0; + + // Only one window per CWnd instance allowed + assert(!::IsWindow(m_hWnd)); + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + + // Store the 'this' pointer in Thread Local Storage + pTLSData->pCWnd = this; + + // Create the property sheet + ipResult = PropertySheet(ppsph); + + return ipResult; + } + + inline void CPropertySheet::DestroyButton(int IDButton) + { + assert(::IsWindow(m_hWnd)); + + HWND hwndButton = ::GetDlgItem(m_hWnd, IDButton); + if (hwndButton != NULL) + { + // Hide and disable the button + ::ShowWindow(hwndButton, SW_HIDE); + ::EnableWindow(hwndButton, FALSE); + } + } + + inline void CPropertySheet::Destroy() + { + CWnd::Destroy(); + m_vPages.clear(); + } + + inline int CPropertySheet::DoModal() + { + assert( GetApp() ); + + BuildPageArray(); + PROPSHEETPAGE* pPSPArray = &m_vPSP.front(); + m_PSH.ppsp = pPSPArray; + + // Create the Property Sheet + int nResult = (int)CreatePropertySheet(&m_PSH); + + m_vPages.clear(); + + return nResult; + } + + inline CPropertyPage* CPropertySheet::GetActivePage() const + { + assert(::IsWindow(m_hWnd)); + + CPropertyPage* pPage = NULL; + if (m_hWnd != NULL) + { + HWND hPage = (HWND)SendMessage(PSM_GETCURRENTPAGEHWND, 0L, 0L); + pPage = (CPropertyPage*)FromHandle(hPage); + } + + return pPage; + } + + inline int CPropertySheet::GetPageCount() const + // Returns the number of Property Pages in this Property Sheet + { + assert(::IsWindow(m_hWnd)); + return (int)m_vPages.size(); + } + + inline int CPropertySheet::GetPageIndex(CPropertyPage* pPage) const + { + assert(::IsWindow(m_hWnd)); + + for (int i = 0; i < GetPageCount(); i++) + { + if (m_vPages[i].get() == pPage) + return i; + } + return -1; + } + + inline HWND CPropertySheet::GetTabControl() const + // Returns the handle to the Property Sheet's tab control + { + assert(::IsWindow(m_hWnd)); + return (HWND)SendMessage(PSM_GETTABCONTROL, 0L, 0L); + } + + inline BOOL CPropertySheet::IsModeless() const + { + return (m_PSH.dwFlags & PSH_MODELESS); + } + + inline BOOL CPropertySheet::IsWizard() const + { + return (m_PSH.dwFlags & PSH_WIZARD); + } + + inline void CPropertySheet::RemovePage(CPropertyPage* pPage) + // Removes a Property Page from the Property Sheet + { + assert(::IsWindow(m_hWnd)); + + int nPage = GetPageIndex(pPage); + if (m_hWnd != NULL) + SendMessage(m_hWnd, PSM_REMOVEPAGE, nPage, 0L); + + m_vPages.erase(m_vPages.begin() + nPage, m_vPages.begin() + nPage+1); + m_PSH.nPages = (int)m_vPages.size(); + } + + inline BOOL CPropertySheet::PreTranslateMessage(MSG* pMsg) + { + // allow sheet to translate Ctrl+Tab, Shift+Ctrl+Tab, Ctrl+PageUp, and Ctrl+PageDown + if (pMsg->message == WM_KEYDOWN && GetAsyncKeyState(VK_CONTROL) < 0 && + (pMsg->wParam == VK_TAB || pMsg->wParam == VK_PRIOR || pMsg->wParam == VK_NEXT)) + { + if (SendMessage(PSM_ISDIALOGMESSAGE, 0L, (LPARAM)pMsg)) + return TRUE; + } + + // allow the dialog to translate keyboard input + if ((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST)) + { + return GetActivePage()->PreTranslateMessage(pMsg); + } + + return CWnd::PreTranslateMessage(pMsg); + } + + inline BOOL CPropertySheet::SetActivePage(int nPage) + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(m_hWnd, PSM_SETCURSEL, nPage, 0L); + } + + inline BOOL CPropertySheet::SetActivePage(CPropertyPage* pPage) + { + assert(::IsWindow(m_hWnd)); + int nPage = GetPageIndex(pPage); + if ((nPage >= 0)) + return SetActivePage(nPage); + + return FALSE; + } + + inline void CPropertySheet::SetIcon(UINT idIcon) + { + m_PSH.pszIcon = MAKEINTRESOURCE(idIcon); + m_PSH.dwFlags |= PSH_USEICONID; + } + + inline void CPropertySheet::SetTitle(LPCTSTR szTitle) + { + if (szTitle) + m_Title = szTitle; + else + m_Title.erase(); + + m_PSH.pszCaption = m_Title.c_str(); + } + + inline void CPropertySheet::SetWizardMode(BOOL bWizard) + { + if (bWizard) + m_PSH.dwFlags |= PSH_WIZARD; + else + m_PSH.dwFlags &= ~PSH_WIZARD; + } + + inline LRESULT CPropertySheet::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + + case WM_WINDOWPOSCHANGED: + { + LPWINDOWPOS lpWinPos = (LPWINDOWPOS)lParam; + if (lpWinPos->flags & SWP_SHOWWINDOW) + { + if (!m_bInitialUpdate) + // The first window positioning with the window visible + OnInitialUpdate(); + m_bInitialUpdate = TRUE; + } + } + break; + + case WM_DESTROY: + m_bInitialUpdate = FALSE; + break; + + case WM_SYSCOMMAND: + if ((SC_CLOSE == wParam) && (m_PSH.dwFlags & PSH_MODELESS)) + { + Destroy(); + return 0L; + } + break; + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + +} + +#endif // _WIN32XX_PROPERTYSHEET_H_ diff --git a/mmc_updater/depends/win32cpp/rebar.h b/mmc_updater/depends/win32cpp/rebar.h new file mode 100644 index 00000000..1a339dca --- /dev/null +++ b/mmc_updater/depends/win32cpp/rebar.h @@ -0,0 +1,709 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +#ifndef _WIN32XX_REBAR_H_ +#define _WIN32XX_REBAR_H_ + +#include "wincore.h" +#include "gdi.h" + + +namespace Win32xx +{ + + struct ReBarTheme + { + BOOL UseThemes; // TRUE if themes are used + COLORREF clrBkgnd1; // Colour 1 for rebar background + COLORREF clrBkgnd2; // Colour 2 for rebar background + COLORREF clrBand1; // Colour 1 for rebar band background. Use NULL if not required + COLORREF clrBand2; // Colour 2 for rebar band background. Use NULL if not required + BOOL FlatStyle; // Bands are rendered with flat rather than raised style + BOOL BandsLeft; // Position bands left on rearrange + BOOL LockMenuBand; // Lock MenuBar's band in dedicated top row, without gripper + BOOL RoundBorders; // Use rounded band borders + BOOL ShortBands; // Allows bands to be shorter than maximum available width + BOOL UseLines; // Displays horizontal lines between bands + }; + + //////////////////////////////////// + // Declaration of the CReBar class + // + class CReBar : public CWnd + { + public: + CReBar(); + virtual ~CReBar(); + + // Operations + BOOL DeleteBand(const int nBand) const; + int HitTest(RBHITTESTINFO& rbht); + HWND HitTest(POINT pt); + int IDToIndex(UINT uBandID) const; + BOOL InsertBand(const int nBand, REBARBANDINFO& rbbi) const; + BOOL IsBandVisible(int nBand) const; + void MaximizeBand(UINT uBand, BOOL fIdeal = FALSE); + void MinimizeBand(UINT uBand); + BOOL MoveBand(UINT uFrom, UINT uTo); + void MoveBandsLeft(); + BOOL ResizeBand(const int nBand, const CSize& sz) const; + BOOL ShowGripper(int nBand, BOOL fShow) const; + BOOL ShowBand(int nBand, BOOL fShow) const; + BOOL SizeToRect(CRect& rect) const; + + // Attributes + int GetBand(const HWND hWnd) const; + CRect GetBandBorders(int nBand) const; + int GetBandCount() const; + BOOL GetBandInfo(const int nBand, REBARBANDINFO& rbbi) const; + CRect GetBandRect(int i) const; + UINT GetBarHeight() const; + BOOL GetBarInfo(REBARINFO& rbi) const; + HWND GetMenuBar() {return m_hMenuBar;} + ReBarTheme& GetReBarTheme() {return m_Theme;} + UINT GetRowCount() const; + int GetRowHeight(int nRow) const; + UINT GetSizeofRBBI() const; + HWND GetToolTips() const; + BOOL SetBandBitmap(const int nBand, const CBitmap* pBackground) const; + BOOL SetBandColor(const int nBand, const COLORREF clrFore, const COLORREF clrBack) const; + BOOL SetBandInfo(const int nBand, REBARBANDINFO& rbbi) const; + BOOL SetBarInfo(REBARINFO& rbi) const; + void SetMenuBar(HWND hMenuBar) {m_hMenuBar = hMenuBar;} + void SetReBarTheme(ReBarTheme& Theme); + + protected: + //Overridables + virtual BOOL OnEraseBkgnd(CDC* pDC); + virtual void PreCreate(CREATESTRUCT& cs); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CReBar(const CReBar&); // Disable copy construction + CReBar& operator = (const CReBar&); // Disable assignment operator + + ReBarTheme m_Theme; + BOOL m_bIsDragging; + HWND m_hMenuBar; + LPARAM m_Orig_lParam; + }; + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + /////////////////////////////////// + // Definitions for the CReBar class + // + inline CReBar::CReBar() : m_bIsDragging(FALSE), m_hMenuBar(0), m_Orig_lParam(0L) + { + ZeroMemory(&m_Theme, sizeof(ReBarTheme)); + } + + inline CReBar::~CReBar() + { + } + + inline BOOL CReBar::DeleteBand(int nBand) const + // Deletes a band from a rebar control. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(RB_DELETEBAND, nBand, 0L); + } + + inline int CReBar::GetBand(HWND hWnd) const + // Returns the zero based band number for this window handle + { + assert(::IsWindow(m_hWnd)); + + int nResult = -1; + if (NULL == hWnd) return nResult; + + for (int nBand = 0; nBand < GetBandCount(); ++nBand) + { + REBARBANDINFO rbbi = {0}; + rbbi.cbSize = GetSizeofRBBI(); + rbbi.fMask = RBBIM_CHILD; + GetBandInfo(nBand, rbbi); + if (rbbi.hwndChild == hWnd) + nResult = nBand; + } + + return nResult; + } + + inline CRect CReBar::GetBandBorders(int nBand) const + // Retrieves the borders of a band. + { + assert(::IsWindow(m_hWnd)); + + CRect rc; + SendMessage(RB_GETBANDBORDERS, nBand, (LPARAM)&rc); + return rc; + } + + inline int CReBar::GetBandCount() const + // Retrieves the count of bands currently in the rebar control. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(RB_GETBANDCOUNT, 0L, 0L); + } + + inline BOOL CReBar::GetBandInfo(int nBand, REBARBANDINFO& rbbi) const + // Retrieves information about a specified band in a rebar control. + { + assert(::IsWindow(m_hWnd)); + assert(nBand >= 0); + + // REBARBANDINFO describes individual BAND characteristics + rbbi.cbSize = GetSizeofRBBI(); + return (BOOL)SendMessage(RB_GETBANDINFO, nBand, (LPARAM)&rbbi); + } + + inline CRect CReBar::GetBandRect(int i) const + // Retrieves the bounding rectangle for a given band in a rebar control. + { + assert(::IsWindow(m_hWnd)); + CRect rc; + SendMessage(RB_GETRECT, i, (LPARAM)&rc); + return rc; + } + + inline UINT CReBar::GetBarHeight() const + // Retrieves the height of the rebar control. + { + assert(::IsWindow(m_hWnd)); + return (UINT)SendMessage(RB_GETBARHEIGHT, 0L, 0L); + } + + inline BOOL CReBar::GetBarInfo(REBARINFO& rbi) const + // Retrieves information about the rebar control and the image list it uses. + { + assert(::IsWindow(m_hWnd)); + + // REBARINFO describes overall rebar control characteristics + rbi.cbSize = GetSizeofRBBI(); + return (BOOL)SendMessage(RB_GETBARINFO, 0L, (LPARAM)&rbi); + } + + inline UINT CReBar::GetRowCount() const + // Retrieves the number of rows of bands in a rebar control. + { + assert(::IsWindow(m_hWnd)); + return (UINT)SendMessage(RB_GETROWCOUNT, 0L, 0L); + } + + inline int CReBar::GetRowHeight(int nRow) const + // Retrieves the height of a specified row in a rebar control. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(RB_GETROWHEIGHT, nRow, 0L); + } + + inline UINT CReBar::GetSizeofRBBI() const + // The size of the REBARBANDINFO struct changes according to _WIN32_WINNT + // sizeof(REBARBANDINFO) can report an incorrect size for older Window versions, + // or newer Window version without XP themes enabled. + // Use this function to get a safe size for REBARBANDINFO. + { + assert(::IsWindow(m_hWnd)); + + UINT uSizeof = sizeof(REBARBANDINFO); + + #if defined REBARBANDINFO_V6_SIZE // only defined for VS2008 or higher + #if !defined (_WIN32_WINNT) || _WIN32_WINNT >= 0x0600 + if ((GetWinVersion() < 2600) || (GetComCtlVersion() < 610)) // Vista and Vista themes? + uSizeof = REBARBANDINFO_V6_SIZE; + #endif + #endif + + return uSizeof; + } + + inline HWND CReBar::GetToolTips() const + // Retrieves the handle to any ToolTip control associated with the rebar control. + { + assert(::IsWindow(m_hWnd)); + return (HWND)SendMessage(RB_GETTOOLTIPS, 0L, 0L); + } + + inline int CReBar::HitTest(RBHITTESTINFO& rbht) + // Determines which portion of a rebar band is at a given point on the screen, + // if a rebar band exists at that point. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(RB_HITTEST, 0L, (LPARAM)&rbht); + } + + inline HWND CReBar::HitTest(POINT pt) + // Return the child HWND at the given point + { + assert(::IsWindow(m_hWnd)); + + // Convert the point to client co-ordinates + ScreenToClient(pt); + + // Get the rebar band with the point + RBHITTESTINFO rbhti = {0}; + rbhti.pt = pt; + int iBand = HitTest(rbhti); + + if (iBand >= 0) + { + // Get the rebar band's hWnd + REBARBANDINFO rbbi = {0}; + rbbi.cbSize = GetSizeofRBBI(); + rbbi.fMask = RBBIM_CHILD; + GetBandInfo(iBand, rbbi); + + return rbbi.hwndChild; + } + else + return NULL; + } + + inline int CReBar::IDToIndex(UINT uBandID) const + // Converts a band identifier to a band index in a rebar control. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(RB_IDTOINDEX, (WPARAM)uBandID, 0L); + } + + inline BOOL CReBar::InsertBand(int nBand, REBARBANDINFO& rbbi) const + // Inserts a new band in a rebar control. + { + assert(::IsWindow(m_hWnd)); + + rbbi.cbSize = GetSizeofRBBI(); + return (BOOL)SendMessage(RB_INSERTBAND, nBand, (LPARAM)&rbbi); + } + + inline BOOL CReBar::IsBandVisible(int nBand) const + // Returns true if the band is visible + { + assert(::IsWindow(m_hWnd)); + + REBARBANDINFO rbbi = {0}; + rbbi.cbSize = GetSizeofRBBI(); + rbbi.fMask = RBBIM_STYLE; + GetBandInfo(nBand, rbbi); + + return !(rbbi.fStyle & RBBS_HIDDEN); + } + + inline BOOL CReBar::OnEraseBkgnd(CDC* pDC) + { + BOOL Erase = TRUE; + if (!m_Theme.UseThemes) + Erase = FALSE; + + if (!m_Theme.clrBkgnd1 && !m_Theme.clrBkgnd2 && !m_Theme.clrBand1 && !m_Theme.clrBand2) + Erase = FALSE; + + if (Erase) + { + CRect rcReBar = GetClientRect(); + int BarWidth = rcReBar.Width(); + int BarHeight = rcReBar.Height(); + + // Create and set up our memory DC + CMemDC MemDC(pDC); + MemDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight); + + // Draw to ReBar background to the memory DC + rcReBar.right = 600; + MemDC.GradientFill(m_Theme.clrBkgnd1, m_Theme.clrBkgnd2, rcReBar, TRUE); + if (BarWidth >= 600) + { + rcReBar.left = 600; + rcReBar.right = BarWidth; + MemDC.SolidFill(m_Theme.clrBkgnd2, rcReBar); + } + + if (m_Theme.clrBand1 || m_Theme.clrBand2) + { + // Draw the individual band backgrounds + for (int nBand = 0 ; nBand < GetBandCount(); ++nBand) + { + if (IsBandVisible(nBand)) + { + if (nBand != GetBand(m_hMenuBar)) + { + // Determine the size of this band + CRect rcBand = GetBandRect(nBand); + + // Determine the size of the child window + REBARBANDINFO rbbi = {0}; + rbbi.cbSize = GetSizeofRBBI(); + rbbi.fMask = RBBIM_CHILD ; + GetBandInfo(nBand, rbbi); + CRect rcChild; + ::GetWindowRect(rbbi.hwndChild, &rcChild); + int ChildWidth = rcChild.right - rcChild.left; + + // Determine our drawing rectangle + CRect rcDraw = rcBand; + rcDraw.bottom = rcDraw.top + (rcBand.bottom - rcBand.top)/2; + int xPad = IsXPThemed()? 2: 0; + rcDraw.left -= xPad; + + // Fill the Source CDC with the band's background + CMemDC SourceDC(pDC); + SourceDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight); + CRect rcBorder = GetBandBorders(nBand); + rcDraw.right = rcBand.left + ChildWidth + rcBorder.left; + SourceDC.SolidFill(m_Theme.clrBand1, rcDraw); + rcDraw.top = rcDraw.bottom; + rcDraw.bottom = rcBand.bottom; + SourceDC.GradientFill(m_Theme.clrBand1, m_Theme.clrBand2, rcDraw, FALSE); + + // Set Curve amount for rounded edges + int Curve = m_Theme.RoundBorders? 12 : 0; + + // Create our mask for rounded edges using RoundRect + CMemDC MaskDC(pDC); + MaskDC.CreateCompatibleBitmap(pDC, BarWidth, BarHeight); + + rcDraw.top = rcBand.top; + if (!m_Theme.FlatStyle) + ::InflateRect(&rcDraw, 1, 1); + + int left = rcDraw.left; + int right = rcDraw.right; + int top = rcDraw.top; + int bottom = rcDraw.bottom; + int cx = rcDraw.right - rcBand.left + xPad; + int cy = rcDraw.bottom - rcBand.top; + + if (m_Theme.FlatStyle) + { + MaskDC.SolidFill(RGB(0,0,0), rcDraw); + MaskDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, PATINVERT); + MaskDC.RoundRect(left, top, right, bottom, Curve, Curve); + } + else + { + MaskDC.SolidFill(RGB(0,0,0), rcDraw); + MaskDC.RoundRect(left, top, right, bottom, Curve, Curve); + MaskDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, PATINVERT); + } + + // Copy Source DC to Memory DC using the RoundRect mask + MemDC.BitBlt(left, top, cx, cy, &SourceDC, left, top, SRCINVERT); + MemDC.BitBlt(left, top, cx, cy, &MaskDC, left, top, SRCAND); + MemDC.BitBlt(left, top, cx, cy, &SourceDC, left, top, SRCINVERT); + + // Extra drawing to prevent jagged edge while moving bands + if (m_bIsDragging) + { + CClientDC ReBarDC(this); + ReBarDC.BitBlt(rcDraw.right - ChildWidth, rcDraw.top, ChildWidth, cy, &MemDC, rcDraw.right - ChildWidth, rcDraw.top, SRCCOPY); + } + } + } + } + } + + if (m_Theme.UseLines) + { + // Draw lines between bands + for (int j = 0; j < GetBandCount()-1; ++j) + { + rcReBar = GetBandRect(j); + rcReBar.left = MAX(0, rcReBar.left - 4); + rcReBar.bottom +=2; + MemDC.DrawEdge(rcReBar, EDGE_ETCHED, BF_BOTTOM | BF_ADJUST); + } + } + + // Copy the Memory DC to the window's DC + pDC->BitBlt(0, 0, BarWidth, BarHeight, &MemDC, 0, 0, SRCCOPY); + } + + return Erase; + } + + inline void CReBar::PreCreate(CREATESTRUCT &cs) + // Sets the CREATESTRUCT paramaters prior to window creation + { + cs.style = WS_VISIBLE | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | + CCS_NODIVIDER | RBS_VARHEIGHT | RBS_BANDBORDERS ; + + cs.cy = 100; + } + + inline void CReBar::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = REBARCLASSNAME; + } + + inline void CReBar::MaximizeBand(UINT uBand, BOOL fIdeal /*= FALSE*/) + // Resizes a band in a rebar control to either its ideal or largest size. + { + assert(::IsWindow(m_hWnd)); + SendMessage(RB_MAXIMIZEBAND, (WPARAM)uBand, (LPARAM)fIdeal); + } + + inline void CReBar::MinimizeBand(UINT uBand) + // Resizes a band in a rebar control to its smallest size. + { + assert(::IsWindow(m_hWnd)); + SendMessage(RB_MINIMIZEBAND, (WPARAM)uBand, 0L); + } + + inline BOOL CReBar::MoveBand(UINT uFrom, UINT uTo) + // Moves a band from one index to another. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(RB_MOVEBAND, (WPARAM)uFrom, (LPARAM)uTo); + } + + inline void CReBar::MoveBandsLeft() + // Repositions the bands so they are left justified + { + assert(::IsWindow(m_hWnd)); + + int OldrcTop = -1; + for (int nBand = GetBandCount() -1; nBand >= 0; --nBand) + { + CRect rc = GetBandRect(nBand); + if (rc.top != OldrcTop) + { + // Maximize the last band on each row + if (IsBandVisible(nBand)) + { + ::SendMessage(GetHwnd(), RB_MAXIMIZEBAND, nBand, 0L); + OldrcTop = rc.top; + } + } + } + } + + inline BOOL CReBar::ResizeBand(int nBand, const CSize& sz) const + // Sets a band's size + { + assert(::IsWindow(m_hWnd)); + + REBARBANDINFO rbbi = {0}; + rbbi.cbSize = GetSizeofRBBI(); + rbbi.fMask = RBBIM_CHILDSIZE | RBBIM_SIZE; + + GetBandInfo(nBand, rbbi); + rbbi.cx = sz.cx + 2; + rbbi.cxMinChild = sz.cx + 2; + rbbi.cyMinChild = sz.cy; + rbbi.cyMaxChild = sz.cy; + + return SetBandInfo(nBand, rbbi ); + } + + inline BOOL CReBar::SetBandBitmap(int nBand, const CBitmap* pBackground) const + // Sets the band's bitmaps + { + assert(::IsWindow(m_hWnd)); + assert(pBackground); + + REBARBANDINFO rbbi = {0}; + rbbi.cbSize = GetSizeofRBBI(); + rbbi.fMask = RBBIM_STYLE; + GetBandInfo(nBand, rbbi); + rbbi.fMask |= RBBIM_BACKGROUND; + rbbi.hbmBack = *pBackground; + + return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi); + } + + inline BOOL CReBar::SetBandColor(int nBand, COLORREF clrFore, COLORREF clrBack) const + // Sets the band's color + // Note: No effect with XP themes enabled + // No effect if a bitmap has been set + { + assert(::IsWindow(m_hWnd)); + + REBARBANDINFO rbbi = {0}; + rbbi.cbSize = GetSizeofRBBI(); + rbbi.fMask = RBBIM_COLORS; + rbbi.clrFore = clrFore; + rbbi.clrBack = clrBack; + + return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi); + } + + inline BOOL CReBar::SetBandInfo(int nBand, REBARBANDINFO& rbbi) const + // Sets the characteristics of a rebar control. + { + assert(::IsWindow(m_hWnd)); + assert(nBand >= 0); + + // REBARBANDINFO describes individual BAND characteristics0 + rbbi.cbSize = GetSizeofRBBI(); + return (BOOL)SendMessage(RB_SETBANDINFO, nBand, (LPARAM)&rbbi); + } + + inline BOOL CReBar::SetBarInfo(REBARINFO& rbi) const + // REBARINFO associates an image list with the rebar + // A band will also need to set RBBIM_IMAGE + { + assert(::IsWindow(m_hWnd)); + + rbi.cbSize = GetSizeofRBBI(); + return (BOOL)SendMessage(RB_SETBARINFO, 0L, (LPARAM)&rbi); + } + + inline void CReBar::SetReBarTheme(ReBarTheme& Theme) + { + m_Theme.UseThemes = Theme.UseThemes; + m_Theme.clrBkgnd1 = Theme.clrBkgnd1; + m_Theme.clrBkgnd2 = Theme.clrBkgnd2; + m_Theme.clrBand1 = Theme.clrBand1; + m_Theme.clrBand2 = Theme.clrBand2; + m_Theme.BandsLeft = Theme.BandsLeft; + m_Theme.LockMenuBand = Theme.LockMenuBand; + m_Theme.ShortBands = Theme.ShortBands; + m_Theme.UseLines = Theme.UseLines; + m_Theme.FlatStyle = Theme.FlatStyle; + m_Theme.RoundBorders = Theme.RoundBorders; + + if (IsWindow()) + { + if (m_Theme.LockMenuBand) + ShowGripper(GetBand(m_hMenuBar), FALSE); + else + ShowGripper(GetBand(m_hMenuBar), TRUE); + + Invalidate(); + } + } + + inline BOOL CReBar::ShowBand(int nBand, BOOL fShow) const + // Show or hide a band + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(RB_SHOWBAND, (WPARAM)nBand, (LPARAM)fShow); + } + + inline BOOL CReBar::ShowGripper(int nBand, BOOL fShow) const + // Show or hide the band's gripper + { + assert(::IsWindow(m_hWnd)); + + REBARBANDINFO rbbi = {0}; + rbbi.cbSize = GetSizeofRBBI(); + rbbi.fMask = RBBIM_STYLE; + GetBandInfo(nBand, rbbi); + if (fShow) + { + rbbi.fStyle |= RBBS_GRIPPERALWAYS; + rbbi.fStyle &= ~RBBS_NOGRIPPER; + } + else + { + rbbi.fStyle &= ~RBBS_GRIPPERALWAYS; + rbbi.fStyle |= RBBS_NOGRIPPER; + } + + return SetBandInfo(nBand, rbbi); + } + + inline BOOL CReBar::SizeToRect(CRect& rect) const + // Attempts to find the best layout of the bands for the given rectangle. + // The rebar bands will be arranged and wrapped as necessary to fit the rectangle. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(RB_SIZETORECT, 0, (LPARAM) (LPRECT)rect); + } + + inline LRESULT CReBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_MOUSEMOVE: + if (m_Theme.UseThemes && m_Theme.LockMenuBand) + { + // We want to lock the first row in place, but allow other bands to move! + // Use move messages to limit the resizing of bands + int y = GET_Y_LPARAM(lParam); + + if (y <= GetRowHeight(0)) + return 0L; // throw this message away + } + break; + case WM_LBUTTONDOWN: + m_Orig_lParam = lParam; // Store the x,y position + m_bIsDragging = TRUE; + break; + case WM_LBUTTONUP: + if (m_Theme.UseThemes && m_Theme.LockMenuBand) + { + // Use move messages to limit the resizing of bands + int y = GET_Y_LPARAM(lParam); + + if (y <= GetRowHeight(0)) + { + // Use x,y from WM_LBUTTONDOWN for WM_LBUTTONUP position + lParam = m_Orig_lParam; + } + } + m_bIsDragging = FALSE; + break; + case UWM_GETREBARTHEME: + { + ReBarTheme& rm = GetReBarTheme(); + return (LRESULT)&rm; + } + case UWM_TOOLBAR_RESIZE: + { + HWND hToolBar = (HWND)wParam; + LPSIZE pToolBarSize = (LPSIZE)lParam; + ResizeBand(GetBand(hToolBar), *pToolBarSize); + } + break; + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + +} // namespace Win32xx + +#endif // #ifndef _WIN32XX_REBAR_H_ diff --git a/mmc_updater/depends/win32cpp/release notes.txt b/mmc_updater/depends/win32cpp/release notes.txt new file mode 100644 index 00000000..bc3114da --- /dev/null +++ b/mmc_updater/depends/win32cpp/release notes.txt @@ -0,0 +1,116 @@ +About Win32++ +------------- +Win32++ is simple and easy to understand framework for developing Win32 +applications using C++. It brings an object oriented approach to programming +directly with the Win32 API. Each window created is a C++ class object capable +of having its own window procedure for routing messages. + +Win32++ supports the following compilers and development environments: +* Borland C++ Compiler 5.5 +* Borland Developer Studio 2006 +* Dev-C++ +* Microsoft Visual C++ Toolkit 2003 +* Microsoft Visual C++ 2005 Express Edition +* Microsoft Visual C++ 2008 Express Edition +* Microsoft Visual C++ 2010 Express Edition +* Microsoft Visual Studio 6.0 +* Microsoft Visual Studio.net 2003 +* Microsoft Visual Studio.net 2005 +* Microsoft Visual Studio.net 2008 +* Microsoft Visual Studio.net 2010 +* MinGW compiler + +Win32++ supports the following operating systems +* Windows 95 +* Windows 98 +* Windows ME +* Windows NT 4 +* Windows 2000 +* Windows XP (32bit and 64bit) +* Windows 2003 Server (32bit and 64bit) +* Windows Vista (32bit and 64bit) +* Windows 2008 Server (32bit and 64bit) +* Windows 7 (32 bit and 64 bit) +* Windows CE from WCE400 (Windows mobile 2003) to WCE600 (Windows mobile 6) + + +Features +-------- +Win32++ code has the following features + * Object Orientated + * Subclassing support + * Notification reflection and message reflection + * Unicode compliant, with multilingual support + * Multi-threaded support. + * Tracing + * 64 bit support + * Windows 7 ribbon support + * Themes support + * Network support (including IP version 6) + * Docking windows + * Tabbed MDIs + +Frames produced by Win32++ include the following: + * Rebar + * Menubar + * Toolbar + * Status bar + * Tool tips + +About the file downloads +------------------------ +The file download from Sourceforge includes the following: + * The Win32++ library itself + * Help for the library + * A set of tutorials + * A collection of sample applications + +The sample applications include: + * Browser - An Internet browser application with an event sink. + * Dialog - An example of a simple dialog application. + * DialogDemo - An interative dialog application demonstrating slider controls and progress bars. + * DialogResizing - An example of a resizable dialog. + * DialogTab - A dialog application with a tab control. + * DirectX - A simple DirectX application. + * DLL - Shows how to run Win32++ from within a DLL + * Dock - An example of a simple docking application. + * DockContainer - An example of a docking application which incorporates containers. + * DockTabbedMDI - An example of a docking application with containers and a tabbed MDI. + * Explorer - A Windows Explorer-like application. + * FastGDI - An application which demonstrates direct manipulation of a bitmap's colour. + * FormDemo - An example of a modeless dialog within a frame. + * Frame - A simple frame application. + * GDIPlus - Demonstrates how to use GDI+ with Win32++. + * MDIFrame - A simple MDI frame application. + * MDIFrameDemo - Demonstrates some additional features of MDI frames. + * MDIFrameSplitter - Demonstrates how to implement splitter windows in MDI Child windows. + * Networking - Demonstrates the use of networking. + * Notepad - A simple text editor with printing. + * Performance - Measures Win32++'s message handling speed. + * Picture - A simple picture rendering application. + * PropertySheets - A demonstration of property sheets. + * RibbonFrame - Demonstrates how to use the Windows 7 ribbon with a frame. + * RibbonSimple - Demonstrates how to use the Windwos 7 ribbon with a simple window. + * Scribble - A simple drawing application. + * Simple - Creates a simple window. + * Splitter - Demonstrates how to use dockers to create splitter windows. + * TabDemo - Demonstrates the use of a CTab control in a frame. + * TaskDialog - Demonstrates the use of task dialogs (available on Vista and above). + * Themes - Demonstrates how to customise the colours for rebar and toolbar controls. + * Threads - Demonstrates multi-threaded Windows. + * Tray - Demonstrates how to "minimise" an application to the system tray. + * WinCE samples - A small collection of samples for Windows CE + +Getting Started +--------------- +Each file download includes the project files for Dev-C++, CodeBlocks and the +various compilers from Microsoft. CodeBlocks is an IDE (Integrated Development +Environment) that supports GNU GCC, Borland Developer Studio 2006 and Microsoft +C++ Toolkit 2003. + +You can start with one of the sample programs, and add your code. Alternatively +you can start with the projects and sample code provided in the "new projects" +folder. + +For additional information on getting started, refer to the help included +in the documentation.
\ No newline at end of file diff --git a/mmc_updater/depends/win32cpp/ribbon.h b/mmc_updater/depends/win32cpp/ribbon.h new file mode 100644 index 00000000..9f6dac5c --- /dev/null +++ b/mmc_updater/depends/win32cpp/ribbon.h @@ -0,0 +1,527 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// ribbon.h +// Declaration of the following classes: +// CRibbon and CRibbonFrame +// + +#ifndef _WIN32XX_RIBBON_H_ +#define _WIN32XX_RIBBON_H_ + + +// Notes: 1) The Windows 7 SDK must be installed and its directories added to the IDE +// 2) The ribbon only works on OS Windows 7 and above + +//#include <strsafe.h> +#include <UIRibbon.h> // Contained within the Windows 7 SDK +#include <UIRibbonPropertyHelpers.h> + +namespace Win32xx +{ + // Defines the callback entry-point methods for the Ribbon framework. + class CRibbon : public IUICommandHandler, public IUIApplication + { + public: + CRibbon() : m_cRef(1), m_pRibbonFramework(NULL) {} + ~CRibbon(); + + // IUnknown methods. + STDMETHOD_(ULONG, AddRef()); + STDMETHOD_(ULONG, Release()); + STDMETHOD(QueryInterface(REFIID iid, void** ppv)); + + // IUIApplication methods + STDMETHOD(OnCreateUICommand)(UINT nCmdID, __in UI_COMMANDTYPE typeID, + __deref_out IUICommandHandler** ppCommandHandler); + + STDMETHOD(OnDestroyUICommand)(UINT32 commandId, __in UI_COMMANDTYPE typeID, + __in_opt IUICommandHandler* commandHandler); + + STDMETHOD(OnViewChanged)(UINT viewId, __in UI_VIEWTYPE typeId, __in IUnknown* pView, + UI_VIEWVERB verb, INT uReasonCode); + + // IUICommandHandle methods + STDMETHODIMP Execute(UINT nCmdID, UI_EXECUTIONVERB verb, __in_opt const PROPERTYKEY* key, __in_opt const PROPVARIANT* ppropvarValue, + __in_opt IUISimplePropertySet* pCommandExecutionProperties); + + STDMETHODIMP UpdateProperty(UINT nCmdID, __in REFPROPERTYKEY key, __in_opt const PROPVARIANT* ppropvarCurrentValue, + __out PROPVARIANT* ppropvarNewValue); + + bool virtual CreateRibbon(CWnd* pWnd); + void virtual DestroyRibbon(); + IUIFramework* GetRibbonFramework() { return m_pRibbonFramework; } + + private: + IUIFramework* m_pRibbonFramework; + LONG m_cRef; // Reference count. + + }; + + + class CRibbonFrame : public CFrame, public CRibbon + { + public: + // A nested class for the MRU item properties + class CRecentFiles : public IUISimplePropertySet + { + public: + CRecentFiles(PWSTR wszFullPath); + ~CRecentFiles() {} + + // IUnknown methods. + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + + // IUISimplePropertySet methods + STDMETHODIMP GetValue(__in REFPROPERTYKEY key, __out PROPVARIANT *value); + + private: + LONG m_cRef; // Reference count. + WCHAR m_wszDisplayName[MAX_PATH]; + WCHAR m_wszFullPath[MAX_PATH]; + }; + + typedef Shared_Ptr<CRecentFiles> RecentFilesPtr; + + CRibbonFrame() : m_uRibbonHeight(0) {} + virtual ~CRibbonFrame() {} + virtual CRect GetViewRect() const; + virtual void OnCreate(); + virtual void OnDestroy(); + virtual STDMETHODIMP OnViewChanged(UINT32 viewId, UI_VIEWTYPE typeId, IUnknown* pView, UI_VIEWVERB verb, INT32 uReasonCode); + virtual HRESULT PopulateRibbonRecentItems(__deref_out PROPVARIANT* pvarValue); + virtual void UpdateMRUMenu(); + + UINT GetRibbonHeight() const { return m_uRibbonHeight; } + + private: + std::vector<RecentFilesPtr> m_vRecentFiles; + void SetRibbonHeight(UINT uRibbonHeight) { m_uRibbonHeight = uRibbonHeight; } + UINT m_uRibbonHeight; + }; + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + ////////////////////////////////////////////// + // Definitions for the CRibbon class + // + + inline CRibbon::~CRibbon() + { + // Reference count must be 1 or we have a leak! + assert(m_cRef == 1); + } + + // IUnknown method implementations. + inline STDMETHODIMP_(ULONG) CRibbon::AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + inline STDMETHODIMP_(ULONG) CRibbon::Release() + { + LONG cRef = InterlockedDecrement(&m_cRef); + return cRef; + } + + inline STDMETHODIMP CRibbon::Execute(UINT nCmdID, UI_EXECUTIONVERB verb, __in_opt const PROPERTYKEY* key, __in_opt const PROPVARIANT* ppropvarValue, + __in_opt IUISimplePropertySet* pCommandExecutionProperties) + { + UNREFERENCED_PARAMETER (nCmdID); + UNREFERENCED_PARAMETER (verb); + UNREFERENCED_PARAMETER (key); + UNREFERENCED_PARAMETER (ppropvarValue); + UNREFERENCED_PARAMETER (pCommandExecutionProperties); + + return E_NOTIMPL; + } + + inline STDMETHODIMP CRibbon::QueryInterface(REFIID iid, void** ppv) + { + if (iid == __uuidof(IUnknown)) + { + *ppv = static_cast<IUnknown*>(static_cast<IUIApplication*>(this)); + } + else if (iid == __uuidof(IUICommandHandler)) + { + *ppv = static_cast<IUICommandHandler*>(this); + } + else if (iid == __uuidof(IUIApplication)) + { + *ppv = static_cast<IUIApplication*>(this); + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + + // Called by the Ribbon framework for each command specified in markup, to bind the Command to an IUICommandHandler. + inline STDMETHODIMP CRibbon::OnCreateUICommand(UINT nCmdID, __in UI_COMMANDTYPE typeID, + __deref_out IUICommandHandler** ppCommandHandler) + { + UNREFERENCED_PARAMETER(typeID); + UNREFERENCED_PARAMETER(nCmdID); + + // By default we use the single command handler provided as part of CRibbon. + // Override this function to account for multiple command handlers. + + return QueryInterface(IID_PPV_ARGS(ppCommandHandler)); + } + + // Called when the state of the Ribbon changes, for example, created, destroyed, or resized. + inline STDMETHODIMP CRibbon::OnViewChanged(UINT viewId, __in UI_VIEWTYPE typeId, __in IUnknown* pView, + UI_VIEWVERB verb, INT uReasonCode) + { + UNREFERENCED_PARAMETER(viewId); + UNREFERENCED_PARAMETER(typeId); + UNREFERENCED_PARAMETER(pView); + UNREFERENCED_PARAMETER(verb); + UNREFERENCED_PARAMETER(uReasonCode); + + + return E_NOTIMPL; + } + + // Called by the Ribbon framework for each command at the time of ribbon destruction. + inline STDMETHODIMP CRibbon::OnDestroyUICommand(UINT32 nCmdID, __in UI_COMMANDTYPE typeID, + __in_opt IUICommandHandler* commandHandler) + { + UNREFERENCED_PARAMETER(commandHandler); + UNREFERENCED_PARAMETER(typeID); + UNREFERENCED_PARAMETER(nCmdID); + + return E_NOTIMPL; + } + + // Called by the Ribbon framework when a command property (PKEY) needs to be updated. + inline STDMETHODIMP CRibbon::UpdateProperty(UINT nCmdID, __in REFPROPERTYKEY key, __in_opt const PROPVARIANT* ppropvarCurrentValue, + __out PROPVARIANT* ppropvarNewValue) + { + UNREFERENCED_PARAMETER(nCmdID); + UNREFERENCED_PARAMETER(key); + UNREFERENCED_PARAMETER(ppropvarCurrentValue); + UNREFERENCED_PARAMETER(ppropvarNewValue); + + return E_NOTIMPL; + } + + inline bool CRibbon::CreateRibbon(CWnd* pWnd) + { + ::CoInitialize(NULL); + + // Instantiate the Ribbon framework object. + ::CoCreateInstance(CLSID_UIRibbonFramework, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pRibbonFramework)); + + // Connect the host application to the Ribbon framework. + HRESULT hr = m_pRibbonFramework->Initialize(pWnd->GetHwnd(), this); + if (FAILED(hr)) + { + return false; + } + + // Load the binary markup. APPLICATION_RIBBON is the default name generated by uicc. + hr = m_pRibbonFramework->LoadUI(GetModuleHandle(NULL), L"APPLICATION_RIBBON"); + if (FAILED(hr)) + { + return false; + } + + return true; + } + + inline void CRibbon::DestroyRibbon() + { + if (m_pRibbonFramework) + { + m_pRibbonFramework->Destroy(); + m_pRibbonFramework->Release(); + m_pRibbonFramework = NULL; + } + } + + + ////////////////////////////////////////////// + // Definitions for the CRibbonFrame class + // + + inline CRect CRibbonFrame::GetViewRect() const + { + // Get the frame's client area + CRect rcFrame = GetClientRect(); + + // Get the statusbar's window area + CRect rcStatus; + if (GetStatusBar().IsWindowVisible() || !IsWindowVisible()) + rcStatus = GetStatusBar().GetWindowRect(); + + // Get the top rebar or toolbar's window area + CRect rcTop; + if (IsReBarSupported() && m_bUseReBar) + rcTop = GetReBar().GetWindowRect(); + else + if (m_bUseToolBar && GetToolBar().IsWindowVisible()) + rcTop = GetToolBar().GetWindowRect(); + + // Return client size less the rebar and status windows + int top = rcFrame.top + rcTop.Height() + m_uRibbonHeight; + int left = rcFrame.left; + int right = rcFrame.right; + int bottom = rcFrame.Height() - (rcStatus.Height()); + if ((bottom <= top) ||( right <= left)) + top = left = right = bottom = 0; + + CRect rcView(left, top, right, bottom); + return rcView; + } + + inline void CRibbonFrame::OnCreate() + { + // OnCreate is called automatically during window creation when a + // WM_CREATE message received. + + // Tasks such as setting the icon, creating child windows, or anything + // associated with creating windows are normally performed here. + + if (GetWinVersion() >= 2601) // WinVersion >= Windows 7 + { + m_bUseReBar = FALSE; // Don't use rebars + m_bUseToolBar = FALSE; // Don't use a toolbar + + CFrame::OnCreate(); + + if (CreateRibbon(this)) + TRACE(_T("Ribbon Created Succesfully\n")); + else + throw CWinException(_T("Failed to create ribbon")); + } + else + { + CFrame::OnCreate(); + } + } + + inline void CRibbonFrame::OnDestroy() + { + DestroyRibbon(); + CFrame::OnDestroy(); + } + + inline STDMETHODIMP CRibbonFrame::OnViewChanged(UINT32 viewId, UI_VIEWTYPE typeId, IUnknown* pView, UI_VIEWVERB verb, INT32 uReasonCode) + { + UNREFERENCED_PARAMETER(viewId); + UNREFERENCED_PARAMETER(uReasonCode); + + HRESULT hr = E_NOTIMPL; + + // Checks to see if the view that was changed was a Ribbon view. + if (UI_VIEWTYPE_RIBBON == typeId) + { + switch (verb) + { + // The view was newly created. + case UI_VIEWVERB_CREATE: + hr = S_OK; + break; + + // The view has been resized. For the Ribbon view, the application should + // call GetHeight to determine the height of the ribbon. + case UI_VIEWVERB_SIZE: + { + IUIRibbon* pRibbon = NULL; + UINT uRibbonHeight; + + hr = pView->QueryInterface(IID_PPV_ARGS(&pRibbon)); + if (SUCCEEDED(hr)) + { + // Call to the framework to determine the desired height of the Ribbon. + hr = pRibbon->GetHeight(&uRibbonHeight); + SetRibbonHeight(uRibbonHeight); + pRibbon->Release(); + + RecalcLayout(); + // Use the ribbon height to position controls in the client area of the window. + } + } + break; + // The view was destroyed. + case UI_VIEWVERB_DESTROY: + hr = S_OK; + break; + } + } + + return hr; + } + + inline HRESULT CRibbonFrame::PopulateRibbonRecentItems(__deref_out PROPVARIANT* pvarValue) + { + LONG iCurrentFile = 0; + std::vector<tString> FileNames = GetMRUEntries(); + std::vector<tString>::iterator iter; + int iFileCount = FileNames.size(); + HRESULT hr = E_FAIL; + SAFEARRAY* psa = SafeArrayCreateVector(VT_UNKNOWN, 0, iFileCount); + m_vRecentFiles.clear(); + + if (psa != NULL) + { + for (iter = FileNames.begin(); iter < FileNames.end(); ++iter) + { + tString strCurrentFile = (*iter); + WCHAR wszCurrentFile[MAX_PATH] = {0L}; + lstrcpynW(wszCurrentFile, T2W(strCurrentFile.c_str()), MAX_PATH); + + CRecentFiles* pRecentFiles = new CRecentFiles(wszCurrentFile); + m_vRecentFiles.push_back(RecentFilesPtr(pRecentFiles)); + hr = SafeArrayPutElement(psa, &iCurrentFile, static_cast<void*>(pRecentFiles)); + ++iCurrentFile; + } + + SAFEARRAYBOUND sab = {iCurrentFile,0}; + SafeArrayRedim(psa, &sab); + hr = UIInitPropertyFromIUnknownArray(UI_PKEY_RecentItems, psa, pvarValue); + + SafeArrayDestroy(psa); // Calls release for each element in the array + } + + return hr; + } + + inline void CRibbonFrame::UpdateMRUMenu() + { + // Suppress UpdateMRUMenu when ribbon is used + if (0 != GetRibbonFramework()) return; + + CFrame::UpdateMRUMenu(); + } + + + //////////////////////////////////////////////////////// + // Declaration of the nested CRecentFiles class + // + inline CRibbonFrame::CRecentFiles::CRecentFiles(PWSTR wszFullPath) : m_cRef(1) + { + SHFILEINFOW sfi; + DWORD_PTR dwPtr = NULL; + m_wszFullPath[0] = L'\0'; + m_wszDisplayName[0] = L'\0'; + + if (NULL != lstrcpynW(m_wszFullPath, wszFullPath, MAX_PATH)) + { + dwPtr = ::SHGetFileInfoW(wszFullPath, FILE_ATTRIBUTE_NORMAL, &sfi, sizeof(sfi), SHGFI_DISPLAYNAME | SHGFI_USEFILEATTRIBUTES); + + if (dwPtr != NULL) + { + lstrcpynW(m_wszDisplayName, sfi.szDisplayName, MAX_PATH); + } + else // Provide a reasonable fallback. + { + lstrcpynW(m_wszDisplayName, m_wszFullPath, MAX_PATH); + } + } + } + + inline STDMETHODIMP_(ULONG) CRibbonFrame::CRecentFiles::AddRef() + { + return InterlockedIncrement(&m_cRef); + } + + inline STDMETHODIMP_(ULONG) CRibbonFrame::CRecentFiles::Release() + { + return InterlockedDecrement(&m_cRef); + } + + inline STDMETHODIMP CRibbonFrame::CRecentFiles::QueryInterface(REFIID iid, void** ppv) + { + if (!ppv) + { + return E_POINTER; + } + + if (iid == __uuidof(IUnknown)) + { + *ppv = static_cast<IUnknown*>(this); + } + else if (iid == __uuidof(IUISimplePropertySet)) + { + *ppv = static_cast<IUISimplePropertySet*>(this); + } + else + { + *ppv = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + + // IUISimplePropertySet methods. + inline STDMETHODIMP CRibbonFrame::CRecentFiles::GetValue(__in REFPROPERTYKEY key, __out PROPVARIANT *ppropvar) + { + HRESULT hr = HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED); + + if (key == UI_PKEY_Label) + { + hr = UIInitPropertyFromString(key, m_wszDisplayName, ppropvar); + } + else if (key == UI_PKEY_LabelDescription) + { + hr = UIInitPropertyFromString(key, m_wszDisplayName, ppropvar); + } + + return hr; + } + +} // namespace Win32xx + +#endif // _WIN32XX_RIBBON_H_ + diff --git a/mmc_updater/depends/win32cpp/shared_ptr.h b/mmc_updater/depends/win32cpp/shared_ptr.h new file mode 100644 index 00000000..0d2f8b0c --- /dev/null +++ b/mmc_updater/depends/win32cpp/shared_ptr.h @@ -0,0 +1,199 @@ +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +// This software was developed from code available in the public domain +// and has no copyright. + + +// About Shared_Ptr: +// Shared_Ptr wraps a reference-counted smart pointer around a dynamically +// allocated object. Unlike auto_ptr, the Shared_Ptr can be used as a smart +// pointer for objects stored in containers like std::vector. Do not use +// Shared_Ptr (or shared_ptr or auto_ptr) for dynamically allocated arrays. +// See below for advice on how to wrap dynamically allocated arrays in a +// vector. +// +// The next standard of C++ will also contain a shared_ptr. Some modern +// compilers already have a shared_ptr available as std::tr1::shared_ptr. If +// your compiler already provides a shared_ptr, or if you have Boost, you +// should use that smart pointer instead. This class has been provided for +// those users who don't have easy access to an "official" shared_ptr. +// Note that this class is "Shared_Ptr", a slightly different name to the +// future "shared_ptr" to avoid naming conflicts. + +// Advantages of Shared_Ptr (or shared_ptr where available): +// - Shared_Ptr can be safely copied. This makes then suitable for containers. +// - Shared_Ptr automatically calls delete for the wrapped pointer when +// its last copy goes out of scope. +// - Shared_Ptr simplifies exception safety. +// +// Without smart pointers, it can be quite challenging to ensure that every +// dynamically allocated pointer (i.e. use of new) is deleted in the event of +// all possible exceptions. In addition to the exceptions we throw ourselves, +// "new" itself will throw an exception it it fails, as does the STL (Standard +// Template Library which includes vector and string). Without smart pointers +// we often need to resort to additional try/catch blocks simply to avoid +// memory leaks when exceptions occur. + +// Examples: +// Shared_Ptr<CWnd> w1(new CWnd); +// or +// Shared_Ptr<CWnd> w1 = new CWnd; +// or +// typedef Shared_Ptr<CWnd> CWndPtr; +// CWndPtr w1 = new CWnd; +// or +// typedef Shared_Ptr<CWnd> CWndPtr; +// CWndPtr w1(new CWnd); +// +// And with a vector +// typedef Shared_Ptr<CWnd> CWndPtr; +// std::vector<CWndPtr> MyVector; +// MyVector.push_back(new CWnd); +// or +// typedef Shared_Ptr<CWnd> CWndPtr; +// CWnd* pWnd = new CWnd; +// std::vector<CWndPtr> MyVector; +// MyVector.push_back(pWnd); +// + +// How to handle dynamically allocated arrays: +// While we could create a smart pointer for arrays, we don't need to because +// std::vector already handles this for us. Consider the following example: +// int nLength = ::GetWindowTextLength(m_hWnd); +// pTChar = new TCHAR[nLength+1]; +// memset(pTChar, 0, (nLength+1)*sizeof(TCHAR)); +// ::GetWindowText(m_hWnd, m_pTChar, nLength+1); +// .... +// delete[] pTChar; +// +// This can be improved by using a vector instead of an array +// int nLength = ::GetWindowTextLength(m_hWnd); +// std::vector<TCHAR> vTChar( nLength+1, _T('\0') ); +// TCHAR* pTCharArray = &vTChar.front(); +// ::GetWindowText(m_hWnd, pTCharArray, nLength+1); +// +// This works because the memory in a vector is always contiguous. Note that +// this is NOT always true of std::string. + + +// Summing up: +// In my opinion, "naked" pointers for dynamically created objects should be +// avoided in modern C++ code. That's to say that calls to "new" should be +// wrapped in some sort of smart pointer wherever possible. This eliminates +// the possibility of memory leaks (particularly in the event of exceptions). +// It also elminiates the need for delete in user's code. + +#ifndef _WIN32XX_SHARED_PTR_ +#define _WIN32XX_SHARED_PTR_ + +namespace Win32xx +{ + + template <class T1> + class Shared_Ptr + { + public: + Shared_Ptr() : m_ptr(NULL), m_count(NULL) { } + Shared_Ptr(T1 * p) : m_ptr(p), m_count(NULL) + { + try + { + if (m_ptr) m_count = new long(0); + inc_ref(); + } + // catch the unlikely event of 'new long(0)' throwing an exception + catch (const std::bad_alloc&) + { + delete m_ptr; + throw; + } + } + Shared_Ptr(const Shared_Ptr& rhs) : m_ptr(rhs.m_ptr), m_count(rhs.m_count) { inc_ref(); } + ~Shared_Ptr() + { + if(m_count && 0 == dec_ref()) + { + // Note: This code doesn't handle a pointer to an array. + // We would need delete[] m_ptr to handle that. + delete m_ptr; + delete m_count; + } + } + + T1* get() const { return m_ptr; } + long use_count() const { return m_count? *m_count : 0; } + bool unique() const { return (m_count && (*m_count == 1)); } + + void swap(Shared_Ptr& rhs) + { + std::swap(m_ptr, rhs.m_ptr); + std::swap(m_count, rhs.m_count); + } + + Shared_Ptr& operator=(const Shared_Ptr& rhs) + { + Shared_Ptr tmp(rhs); + this->swap(tmp); + return *this; + } + + T1* operator->() const + { + assert(m_ptr); + return m_ptr; + } + + T1& operator*() const + { + assert (m_ptr); + return *m_ptr; + } + + bool operator== (const Shared_Ptr& rhs) const + { + return ( *m_ptr == *rhs.m_ptr); + } + + bool operator!= (const Shared_Ptr& rhs) const + { + return ( *m_ptr != *rhs.m_ptr); + } + + bool operator< (const Shared_Ptr& rhs) const + { + return ( *m_ptr < *rhs.m_ptr ); + } + + bool operator> (const Shared_Ptr& rhs) const + { + return ( *m_ptr > *rhs.m_ptr ); + } + + private: + void inc_ref() + { + if(m_count) + InterlockedIncrement(m_count); + } + + int dec_ref() + { + assert (m_count); + return InterlockedDecrement(m_count); + } + + T1* m_ptr; + long* m_count; + }; + +} + +#endif // _WIN32XX_SHARED_PTR_ diff --git a/mmc_updater/depends/win32cpp/socket.h b/mmc_updater/depends/win32cpp/socket.h new file mode 100644 index 00000000..63a7770f --- /dev/null +++ b/mmc_updater/depends/win32cpp/socket.h @@ -0,0 +1,778 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// socket.h +// Declaration of the CSocket class +// +// The CSocket class represents a network socket. It encapsualtes many of +// the Windows Socket SPI fuctions, providing an object-oriented approach +// to network programming. After StartEvents is called, CSocket monitors +// the socket and responds automatically to network events. This event +// monitoring, for example, automatically calls OnReceive when there is +// data on the socket to be read, and OnAccept when a server should accept +// a connection from a client. + +// Users of this class should be aware that functions like OnReceive, +// OnAccept, etc. are called on a different thread from the one CSocket is +// instanciated on. The thread for these functions needs to respond quickly +// to other network events, so it shouldn't be delayed. It also doesn't run +// a message loop, so it can't be used to create windows. For these reasons +// it might be best to use PostMessage in response to these functions in a +// windows environment. + +// Refer to the network samples for an example of how to use this class to +// create a TCP client & server, and a UDP client and server. + +// To compile programs with CSocket, link with ws3_32.lib for Win32, +// and ws2.lib for Windows CE. Windows 95 systems will need to install the +// "Windows Sockets 2.0 for Windows 95". It's available from: +// http://support.microsoft.com/kb/182108/EN-US/ + +// For a TCP server, inherit a class from CSocket and override OnAccept, OnDisconnect +// and OnRecieve. Create one instance of this class and use it as a listening socket. +// The purpose of the listening socket is to detect connections from clients and accept them. +// For the listening socket, we do the following: +// 1) Create the socket. +// 2) Bind an IP address to the socket. +// 3) Listen on the socket for incoming connection requests. +// 4) Use StartNotifyRevents to receive notification of network events. +// 5) Override OnAccept to accept requests on a newly created data CSocket object. +// 6) Create a new data socket for each client connection accepted. +// 7) The server socket uses the 'accept' function to accept an incoming connection +// from this new data socket. + +// The purpose of the data socket is to send data to, and recieve data from the client. +// There will be one data socket for each client accepted by the server. +// To use it we do the following: +// * To recieve data from the client, override OnReceive and use Receive. +// * To send data to use Send. +// * OnDisconnect can be used to detect when the client is disconnected. + +// For a TCP client, inherit from CSocket and override OnReceive and OnDisconnect. +// Create an instance of this inherited class, and perform the following steps: +// 1) Create the socket. +// 2) Connect to the server. +// 3) Use StartNotifyRevents to receive notification of network events. +// We are now ready to send and recieve data from the server. +// * Use Send to send data to the server. +// * Override OnReceive and use Recieve to receive data from the server +// * OnDisconnect can be used to detect when the client is disconnected from the server. + +// Notes regarding IPv6 support +// * IPv6 is supported on Windows Vista and above. Windows XP with SP2 provides +// "experimental" support, which can be enabled by entering "ipv6 install" +// at a command prompt. +// * IPv6 is not supported by all compilters and devlopment environments. In +// particular, it is not supported by Dev-C++ or Borland 5.5. A modern +// Platform SDK needs to be added to Visual Studio 6 for it to support IPv6. +// * IsIPV6Supported returns false if either the operating system or the +// development environment fails to support IPv6. +// + +#ifndef _WIN32XX_SOCKET_H_ +#define _WIN32XX_SOCKET_H_ + + +#include "wincore.h" +#include <winsock2.h> +#include <ws2tcpip.h> +#include <process.h> + + +#define THREAD_TIMEOUT 100 + + +namespace Win32xx +{ + + typedef int WINAPI GETADDRINFO(LPCSTR, LPCSTR, const struct addrinfo*, struct addrinfo**); + typedef void WINAPI FREEADDRINFO(struct addrinfo*); + + class CSocket + { + public: + CSocket(); + virtual ~CSocket(); + + // Operations + virtual void Accept(CSocket& rClientSock, struct sockaddr* addr, int* addrlen); + virtual int Bind(LPCTSTR addr, LPCTSTR port); + virtual int Bind(const struct sockaddr* name, int namelen); + virtual int Connect(LPCTSTR addr, LPCTSTR port); + virtual int Connect(const struct sockaddr* name, int namelen); + virtual BOOL Create( int family, int type, int protocol = IPPROTO_IP); + virtual void Disconnect(); + virtual void FreeAddrInfo( struct addrinfo* ai ); + virtual int GetAddrInfo( LPCTSTR nodename, LPCTSTR servname, const struct addrinfo* hints, struct addrinfo** res); + virtual LPCTSTR GetLastError(); + virtual int ioCtlSocket(long cmd, u_long* argp); + virtual BOOL IsIPV6Supported(); + virtual int Listen(int backlog = SOMAXCONN); + virtual int Receive(TCHAR* buf, int len, int flags); + virtual int ReceiveFrom(TCHAR* buf, int len, int flags, struct sockaddr* from, int* fromlen); + virtual int Send(LPCTSTR buf, int len, int flags); + virtual int SendTo(LPCTSTR send, int len, int flags, LPCTSTR addr, LPCTSTR port); + virtual int SendTo(LPCTSTR buf, int len, int flags, const struct sockaddr* to, int tolen); + + virtual void StartEvents(); + virtual void StopEvents(); + + // Attributes + virtual int GetPeerName(struct sockaddr* name, int* namelen); + virtual int GetSockName(struct sockaddr* name, int* namelen); + SOCKET& GetSocket() { return m_Socket; } + virtual int GetSockOpt(int level, int optname, char* optval, int* optlen); + virtual int SetSockOpt(int level, int optname, const char* optval, int optlen); + + // Override these functions to monitor events + virtual void OnAccept() {} + virtual void OnAddresListChange() {} + virtual void OnDisconnect() {} + virtual void OnConnect() {} + virtual void OnOutOfBand() {} + virtual void OnQualityOfService() {} + virtual void OnReceive() {} + virtual void OnRoutingChange() {} + virtual void OnSend() {} + + + + // Allow CSocket to be used as a SOCKET + operator SOCKET() const {return m_Socket;} + + private: + CSocket(const CSocket&); // Disable copy construction + CSocket& operator = (const CSocket&); // Disable assignment operator + static UINT WINAPI EventThread(LPVOID thread_data); + + tString m_tsErrorMessage; + SOCKET m_Socket; + HMODULE m_hWS2_32; + HANDLE m_hEventThread; // Handle to the thread + HANDLE m_StopRequest; // An event to signal the event thread should stop + HANDLE m_Stopped; // An event to signal the event thread is stopped + + GETADDRINFO* m_pfnGetAddrInfo; // pointer for the GetAddrInfo function + FREEADDRINFO* m_pfnFreeAddrInfo; // pointer for the FreeAddrInfo function + }; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +namespace Win32xx +{ + + inline CSocket::CSocket() : m_Socket(INVALID_SOCKET), m_hEventThread(0) + { + // Initialise the Windows Socket services + WSADATA wsaData; + + if (0 != ::WSAStartup(MAKEWORD(2,2), &wsaData)) + throw CWinException(_T("WSAStartup failed")); + + m_hWS2_32 = ::LoadLibrary(_T("WS2_32.dll")); + if (0 == m_hWS2_32) + throw CWinException(_T("Failed to load WS2_2.dll")); + + m_pfnGetAddrInfo = (GETADDRINFO*) GetProcAddress(m_hWS2_32, "getaddrinfo"); + m_pfnFreeAddrInfo = (FREEADDRINFO*) GetProcAddress(m_hWS2_32, "freeaddrinfo"); + + m_StopRequest = ::CreateEvent(0, TRUE, FALSE, 0); + m_Stopped = ::CreateEvent(0, TRUE, FALSE, 0); + } + + inline CSocket::~CSocket() + { + Disconnect(); + + // Close handles + ::CloseHandle(m_StopRequest); + ::CloseHandle(m_Stopped); + + // Terminate the Windows Socket services + ::WSACleanup(); + + ::FreeLibrary(m_hWS2_32); + } + + inline void CSocket::Accept(CSocket& rClientSock, struct sockaddr* addr, int* addrlen) + { + // The accept function permits an incoming connection attempt on the socket. + + rClientSock.m_Socket = ::accept(m_Socket, addr, addrlen); + if (INVALID_SOCKET == rClientSock.GetSocket()) + TRACE(_T("Accept failed\n")); + } + + inline int CSocket::Bind(LPCTSTR addr, LPCTSTR port) + // The bind function associates a local address with the socket. + { + int RetVal = 0; + + if (IsIPV6Supported()) + { + +#ifdef GetAddrInfo // Skip the following code block for older development environments + + ADDRINFO Hints= {0}; + Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + ADDRINFO *AddrInfo; + + RetVal = GetAddrInfo(addr, port, &Hints, &AddrInfo); + if (RetVal != 0) + { + TRACE( _T("GetAddrInfo failed\n")); + return RetVal; + } + + // Bind the IP address to the listening socket + RetVal = ::bind( m_Socket, AddrInfo->ai_addr, (int)AddrInfo->ai_addrlen ); + if ( RetVal == SOCKET_ERROR ) + { + TRACE(_T("Bind failed\n")); + return RetVal; + } + + // Free the address information allocated by GetAddrInfo + FreeAddrInfo(AddrInfo); + +#endif + + } + else + { + sockaddr_in clientService; + clientService.sin_family = AF_INET; + clientService.sin_addr.s_addr = inet_addr( T2A(addr) ); + int nPort = -1; + nPort = atoi( T2A(port) ); + if (-1 == nPort) + { + TRACE(_T("Invalid port number\n")); + return SOCKET_ERROR; + } + clientService.sin_port = htons( (u_short)nPort ); + + RetVal = ::bind( m_Socket, (SOCKADDR*) &clientService, sizeof(clientService) ); + if ( 0 != RetVal ) + TRACE(_T("Bind failed\n")); + } + + return RetVal; + } + + inline int CSocket::Bind(const struct sockaddr* name, int namelen) + { + // The bind function associates a local address with the socket. + + int Result = ::bind (m_Socket, name, namelen); + if ( 0 != Result ) + TRACE(_T("Bind failed\n")); + return Result; + } + + inline int CSocket::Connect(LPCTSTR addr, LPCTSTR port) + // The Connect function establishes a connection to the socket. + { + int RetVal = 0; + + if (IsIPV6Supported()) + { + +#ifdef GetAddrInfo // Skip the following code block for older development environments + + ADDRINFO Hints= {0}; + Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + ADDRINFO *AddrInfo; + + RetVal = GetAddrInfo(addr, port, &Hints, &AddrInfo); + if (RetVal != 0) + { + TRACE( _T("getaddrinfo failed\n")); + return SOCKET_ERROR; + } + + // Bind the IP address to the listening socket + RetVal = Connect( AddrInfo->ai_addr, (int)AddrInfo->ai_addrlen ); + if ( RetVal == SOCKET_ERROR ) + { + TRACE(_T("Connect failed\n")); + return RetVal; + } + + // Free the address information allocatied by GetAddrInfo + FreeAddrInfo(AddrInfo); + +#endif + + } + else + { + sockaddr_in clientService; + clientService.sin_family = AF_INET; + clientService.sin_addr.s_addr = inet_addr( T2A(addr) ); + int nPort = -1; + nPort = atoi( T2A(port) ); + if (-1 == nPort) + { + TRACE(_T("Invalid port number\n")); + return SOCKET_ERROR; + } + clientService.sin_port = htons( (u_short)nPort ); + + RetVal = ::connect( m_Socket, (SOCKADDR*) &clientService, sizeof(clientService) ); + if ( 0 != RetVal ) + TRACE(_T("Connect failed\n")); + } + + return RetVal; + } + + inline int CSocket::Connect(const struct sockaddr* name, int namelen) + { + // The Connect function establishes a connection to the socket. + + int Result = ::connect( m_Socket, name, namelen ); + if ( 0 != Result ) + TRACE(_T("Connect failed\n")); + + return Result; + } + + inline BOOL CSocket::Create( int family, int type, int protocol /*= IPPROTO_IP*/) + { + // Creates the socket + + // Valid values: + // family: AF_INET or AF_INET6 + // type: SOCK_DGRAM, SOCK_SEQPACKET, SOCK_STREAM, SOCK_RAW + // protocol: IPPROTO_IP, IPPROTO_TCP, IPPROTO_UDP, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6 + + m_Socket = socket(family, type, protocol); + if(m_Socket == INVALID_SOCKET) + { + TRACE(_T("Failed to create socket\n")); + return FALSE; + } + + return TRUE; + } + + inline void CSocket::Disconnect() + { + ::shutdown(m_Socket, SD_BOTH); + StopEvents(); + ::closesocket(m_Socket); + m_Socket = INVALID_SOCKET; + } + + inline UINT WINAPI CSocket::EventThread(LPVOID thread_data) + { + // These are the possible network event notifications: + // FD_READ Notification of readiness for reading. + // FD_WRITE Motification of readiness for writing. + // FD_OOB Notification of the arrival of Out Of Band data. + // FD_ACCEPT Notification of incoming connections. + // FD_CONNECT Notification of completed connection or multipoint join operation. + // FD_CLOSE Notification of socket closure. + // FD_QOS Notification of socket Quality Of Service changes + // FD_ROUTING_INTERFACE_CHANGE Notification of routing interface changes for the specified destination. + // FD_ADDRESS_LIST_CHANGE Notification of local address list changes for the address family of the socket. + + WSANETWORKEVENTS NetworkEvents; + CSocket* pSocket = (CSocket*)thread_data; + SOCKET sClient = pSocket->m_Socket; + + WSAEVENT AllEvents[2]; + AllEvents[0] = ::WSACreateEvent(); + AllEvents[1] = (WSAEVENT)pSocket->m_StopRequest; + long Events = FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE | + FD_QOS | FD_ROUTING_INTERFACE_CHANGE | FD_ADDRESS_LIST_CHANGE; + + // Associate the network event object (hNetworkEvents) with the + // specified network events (Events) on socket sClient. + if( SOCKET_ERROR == WSAEventSelect(sClient, AllEvents[0], Events)) + { + TRACE(_T("Error in Event Select\n")); + ::SetEvent(pSocket->m_Stopped); + ::WSACloseEvent(AllEvents[0]); + return 0; + } + + // loop until the stop event is set + for (;;) // infinite loop + { + // Wait 100 ms for a network event + DWORD dwResult = ::WSAWaitForMultipleEvents(2, AllEvents, FALSE, THREAD_TIMEOUT, FALSE); + + // Check event for stop thread + if(::WaitForSingleObject(pSocket->m_StopRequest, 0) == WAIT_OBJECT_0) + { + ::WSACloseEvent(AllEvents[0]); + ::SetEvent(pSocket->m_Stopped); + return 0; + } + + if (WSA_WAIT_FAILED == dwResult) + { + TRACE(_T("WSAWaitForMultipleEvents failed\n")); + ::WSACloseEvent(AllEvents[0]); + ::SetEvent(pSocket->m_Stopped); + return 0; + } + + // Proceed if a network event occurred + if (WSA_WAIT_TIMEOUT != dwResult) + { + + if ( SOCKET_ERROR == ::WSAEnumNetworkEvents(sClient, AllEvents[0], &NetworkEvents) ) + { + TRACE(_T("WSAEnumNetworkEvents failed\n")); + ::WSACloseEvent(AllEvents[0]); + ::SetEvent(pSocket->m_Stopped); + return 0; + } + + if (NetworkEvents.lNetworkEvents & FD_ACCEPT) + pSocket->OnAccept(); + + if (NetworkEvents.lNetworkEvents & FD_READ) + pSocket->OnReceive(); + + if (NetworkEvents.lNetworkEvents & FD_WRITE) + pSocket->OnSend(); + + if (NetworkEvents.lNetworkEvents & FD_OOB) + pSocket->OnOutOfBand(); + + if (NetworkEvents.lNetworkEvents & FD_QOS) + pSocket->OnQualityOfService(); + + if (NetworkEvents.lNetworkEvents & FD_CONNECT) + pSocket->OnConnect(); + + if (NetworkEvents.lNetworkEvents & FD_ROUTING_INTERFACE_CHANGE) + pSocket->OnRoutingChange(); + + if (NetworkEvents.lNetworkEvents & FD_ADDRESS_LIST_CHANGE) + pSocket->OnAddresListChange(); + + if (NetworkEvents.lNetworkEvents & FD_CLOSE) + { + ::shutdown(sClient, SD_BOTH); + ::closesocket(sClient); + pSocket->OnDisconnect(); + ::WSACloseEvent(AllEvents[0]); + ::SetEvent(pSocket->m_Stopped); + return 0; + } + } + } + } + + inline int CSocket::GetAddrInfo( LPCTSTR nodename, LPCTSTR servname, const struct addrinfo* hints, struct addrinfo** res) + { + +#ifdef GetAddrInfo + + std::string sNodeName = T2A(nodename); + std::string sServName = T2A(servname); + return (*m_pfnGetAddrInfo)(sNodeName.c_str(), sServName.c_str(), hints, res); + +#else + + UNREFERENCED_PARAMETER(nodename); + UNREFERENCED_PARAMETER(servname); + UNREFERENCED_PARAMETER(hints); + UNREFERENCED_PARAMETER(res); + + throw CWinException(_T("getaddrinfo is not supported")); + +#endif + + } + + inline LPCTSTR CSocket::GetLastError() + { + // Retrieves the most recent network error. + + int ErrorCode = WSAGetLastError(); + LPTSTR Message = NULL; + m_tsErrorMessage = _T(""); + + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, ErrorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)&Message, 1024, NULL); + + if (Message) + { + m_tsErrorMessage = Message; + ::LocalFree(Message); + } + + return m_tsErrorMessage.c_str(); + } + + inline int CSocket::GetPeerName(struct sockaddr* name, int* namelen) + { + int Result = ::getpeername(m_Socket, name, namelen); + if (0 != Result) + TRACE(_T("GetPeerName failed\n")); + + return Result; + } + + inline int CSocket::GetSockName(struct sockaddr* name, int* namelen) + { + int Result = ::getsockname(m_Socket, name, namelen); + if (0 != Result) + TRACE(_T("GetSockName Failed\n")); + + return Result; + } + + inline int CSocket::GetSockOpt(int level, int optname, char* optval, int* optlen) + { + int Result = ::getsockopt(m_Socket, level, optname, optval, optlen); + if (0 != Result) + TRACE(_T("GetSockOpt Failed\n")); + + return Result; + } + + inline void CSocket::FreeAddrInfo( struct addrinfo* ai ) + { + +#ifdef GetAddrInfo + + (*m_pfnFreeAddrInfo)(ai); + +#else + + UNREFERENCED_PARAMETER(ai); + + throw CWinException(_T("getaddrinfo is not supported")); + +#endif + + } + + inline int CSocket::ioCtlSocket(long cmd, u_long* argp) + { + int Result = ::ioctlsocket(m_Socket, cmd, argp); + if (0 != Result) + TRACE(_T("ioCtlSocket Failed\n")); + + return Result; + } + + inline BOOL CSocket::IsIPV6Supported() + { + BOOL IsIPV6Supported = FALSE; + +#ifdef GetAddrInfo + + if (m_pfnGetAddrInfo != 0 && m_pfnFreeAddrInfo != 0) + IsIPV6Supported = TRUE; + +#endif + + return IsIPV6Supported; + } + + inline int CSocket::Listen(int backlog /*= SOMAXCONN*/) + { + int Result = ::listen(m_Socket, backlog); + if (0 != Result) + TRACE(_T("Listen Failed\n")); + + return Result; + } + + inline int CSocket::Receive(TCHAR* buf, int len, int flags) + { + std::vector<char> vChar(len+1, '\0'); + char* pCharArray = &vChar.front(); + int Result = ::recv(m_Socket, pCharArray, len, flags); + if (SOCKET_ERROR == Result) + TRACE(_T("Receive failed\n")); + + lstrcpyn(buf, A2T(pCharArray), len); + + return Result; + } + + inline int CSocket::ReceiveFrom(TCHAR* buf, int len, int flags, struct sockaddr* from, int* fromlen) + //The ReceiveFrom function receives a datagram and stores the source address. + { + std::vector<char> vChar(len+1, '\0'); + char* pCharArray = &vChar.front(); + int Result = ::recvfrom(m_Socket, pCharArray, len, flags, from, fromlen); + if (SOCKET_ERROR == Result) + TRACE(_T("ReceiveFrom failed\n")); + + lstrcpyn(buf, A2T(pCharArray), len); + + return Result; + } + + inline int CSocket::Send(LPCTSTR buf, int len, int flags) + { + int Result = ::send(m_Socket, T2A(buf), len, flags); + if (SOCKET_ERROR == Result) + TRACE(_T("Send failed\n")); + + return Result; + } + + inline int CSocket::SendTo(LPCTSTR send, int len, int flags, LPCTSTR addr, LPCTSTR port) + // The sendto function sends data to a specific destination. + { + int RetVal = 0; + + if (IsIPV6Supported()) + { + +#ifdef GetAddrInfo // Skip the following code block for older development environments + + ADDRINFO Hints= {0}; + Hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + ADDRINFO *AddrInfo; + + RetVal = GetAddrInfo(addr, port, &Hints, &AddrInfo); + if (RetVal != 0) + { + TRACE( _T("GetAddrInfo failed\n")); + return SOCKET_ERROR; + } + + RetVal = ::sendto(m_Socket, T2A(send), len, flags, AddrInfo->ai_addr, (int)AddrInfo->ai_addrlen ); + if ( RetVal == SOCKET_ERROR ) + { + TRACE(_T("SendTo failed\n")); + return RetVal; + } + + // Free the address information allocatied by GetAddrInfo + FreeAddrInfo(AddrInfo); + +#endif + + } + else + { + sockaddr_in clientService; + clientService.sin_family = AF_INET; + clientService.sin_addr.s_addr = inet_addr( T2A(addr) ); + int nPort = -1; + nPort = atoi( T2A(port)); + if (-1 == nPort) + { + TRACE(_T("Invalid port number\n")); + return SOCKET_ERROR; + } + clientService.sin_port = htons( (u_short)nPort ); + + RetVal = ::sendto( m_Socket, T2A(send), len, flags, (SOCKADDR*) &clientService, sizeof(clientService) ); + if ( SOCKET_ERROR != RetVal ) + TRACE(_T("SendTo failed\n")); + } + + return RetVal; + } + + inline int CSocket::SendTo(LPCTSTR buf, int len, int flags, const struct sockaddr* to, int tolen) + // The sendto function sends data to a specific destination. + { + int Result = ::sendto(m_Socket, T2A(buf), len, flags, to, tolen); + if (SOCKET_ERROR == Result) + TRACE(_T("SendTo failed\n")); + + return Result; + } + + inline int CSocket::SetSockOpt(int level, int optname, const char* optval, int optlen) + { + int Result = ::setsockopt(m_Socket, level, optname, optval, optlen); + if (0 != Result) + TRACE(_T("SetSockOpt failed\n")); + + return Result; + } + + inline void CSocket::StartEvents() + { + // This function starts the thread which monitors the socket for events. + StopEvents(); // Ensure the thread isn't already running + UINT ThreadID; // a return variable required for Win95, Win98, WinME + m_hEventThread = (HANDLE)::_beginthreadex(NULL, 0, CSocket::EventThread, (LPVOID) this, 0, &ThreadID); + } + + inline void CSocket::StopEvents() + { + // Terminates the event thread gracefully (if possible) + if (m_hEventThread) + { + ::SetThreadPriority(m_hEventThread, THREAD_PRIORITY_HIGHEST); + ::SetEvent(m_StopRequest); + + for (;;) // infinite loop + { + // wait for the Thread stopping event to be set + if ( WAIT_TIMEOUT == ::WaitForSingleObject(m_Stopped, THREAD_TIMEOUT * 10) ) + { + // Note: An excessive delay in processing any of the notification functions + // can cause us to get here. (Yes one second is an excessive delay. Its a bug!) + TRACE(_T("*** Error: Event Thread won't die ***\n") ); + } + else break; + } + + ::CloseHandle(m_hEventThread); + m_hEventThread = 0; + } + + ::ResetEvent(m_StopRequest); + ::ResetEvent(m_Stopped); + } +} + + +#endif // #ifndef _WIN32XX_SOCKET_H_ + diff --git a/mmc_updater/depends/win32cpp/statusbar.h b/mmc_updater/depends/win32cpp/statusbar.h new file mode 100644 index 00000000..ad9a007b --- /dev/null +++ b/mmc_updater/depends/win32cpp/statusbar.h @@ -0,0 +1,226 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +#ifndef _WIN32XX_STATUSBAR_H_ +#define _WIN32XX_STATUSBAR_H_ + +#include "wincore.h" + +namespace Win32xx +{ + + ////////////////////////////////////// + // Declaration of the CStatusBar class + // + class CStatusBar : public CWnd + { + public: + CStatusBar(); + virtual ~CStatusBar() {} + + // Overridables + virtual void PreCreate(CREATESTRUCT& cs); + virtual void PreRegisterClass(WNDCLASS &wc); + + // Attributes + int GetParts(); + HICON GetPartIcon(int iPart); + CRect GetPartRect(int iPart); + tString GetPartText(int iPart) const; + BOOL IsSimple(); + BOOL SetPartIcon(int iPart, HICON hIcon); + BOOL SetPartText(int iPart, LPCTSTR szText, UINT Style = 0) const; + BOOL SetPartWidth(int iPart, int iWidth) const; + + // Operations + CStatusBar(const CStatusBar&); // Disable copy construction + CStatusBar& operator = (const CStatusBar&); // Disable assignment operator + + BOOL CreateParts(int iParts, const int iPaneWidths[]) const; + void SetSimple(BOOL fSimple = TRUE); + }; + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + ////////////////////////////////////// + // Definitions for the CStatusBar class + // + inline CStatusBar::CStatusBar() + { + } + + inline BOOL CStatusBar::CreateParts(int iParts, const int iPaneWidths[]) const + // Sets the number of parts in a status window and the coordinate of the right edge of each part. + // If an element of iPaneWidths is -1, the right edge of the corresponding part extends + // to the border of the window + { + assert(::IsWindow(m_hWnd)); + assert(iParts <= 256); + + return (BOOL)SendMessage(SB_SETPARTS, iParts, (LPARAM)iPaneWidths); + } + + inline int CStatusBar::GetParts() + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(SB_GETPARTS, 0L, 0L); + } + + inline HICON CStatusBar::GetPartIcon(int iPart) + { + assert(::IsWindow(m_hWnd)); + return (HICON)SendMessage(SB_GETICON, (WPARAM)iPart, 0L); + } + + inline CRect CStatusBar::GetPartRect(int iPart) + { + assert(::IsWindow(m_hWnd)); + + CRect rc; + SendMessage(SB_GETRECT, (WPARAM)iPart, (LPARAM)&rc); + return rc; + } + + inline tString CStatusBar::GetPartText(int iPart) const + { + assert(::IsWindow(m_hWnd)); + tString PaneText; + + // Get size of Text array + int iChars = LOWORD (SendMessage(SB_GETTEXTLENGTH, iPart, 0L)); + + std::vector<TCHAR> Text( iChars +1, _T('\0') ); + TCHAR* pTextArray = &Text[0]; + + SendMessage(SB_GETTEXT, iPart, (LPARAM)pTextArray); + PaneText = pTextArray; + return PaneText; + } + + inline BOOL CStatusBar::IsSimple() + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(SB_ISSIMPLE, 0L, 0L); + } + + inline void CStatusBar::PreCreate(CREATESTRUCT &cs) + { + cs.style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | CCS_BOTTOM | SBARS_SIZEGRIP; + } + + inline void CStatusBar::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = STATUSCLASSNAME; + } + + inline BOOL CStatusBar::SetPartText(int iPart, LPCTSTR szText, UINT Style) const + // Available Styles: Combinations of ... + //0 The text is drawn with a border to appear lower than the plane of the window. + //SBT_NOBORDERS The text is drawn without borders. + //SBT_OWNERDRAW The text is drawn by the parent window. + //SBT_POPOUT The text is drawn with a border to appear higher than the plane of the window. + //SBT_RTLREADING The text will be displayed in the opposite direction to the text in the parent window. + { + assert(::IsWindow(m_hWnd)); + + BOOL bResult = FALSE; + if (SendMessage(SB_GETPARTS, 0L, 0L) >= iPart) + bResult = (BOOL)SendMessage(SB_SETTEXT, iPart | Style, (LPARAM)szText); + + return bResult; + } + + inline BOOL CStatusBar::SetPartIcon(int iPart, HICON hIcon) + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(SB_SETICON, (WPARAM)iPart, (LPARAM) hIcon); + } + + inline BOOL CStatusBar::SetPartWidth(int iPart, int iWidth) const + { + // This changes the width of an existing pane, or creates a new pane + // with the specified width. + // A width of -1 for the last part sets the width to the border of the window. + + assert(::IsWindow(m_hWnd)); + assert(iPart >= 0 && iPart <= 255); + + // Fill the PartWidths vector with the current width of the statusbar parts + int PartsCount = (int)SendMessage(SB_GETPARTS, 0L, 0L); + std::vector<int> PartWidths(PartsCount, 0); + int* pPartWidthArray = &PartWidths[0]; + SendMessage(SB_GETPARTS, PartsCount, (LPARAM)pPartWidthArray); + + // Fill the NewPartWidths vector with the new width of the statusbar parts + int NewPartsCount = MAX(iPart+1, PartsCount); + std::vector<int> NewPartWidths(NewPartsCount, 0);; + NewPartWidths = PartWidths; + int* pNewPartWidthArray = &NewPartWidths[0]; + + if (0 == iPart) + pNewPartWidthArray[iPart] = iWidth; + else + { + if (iWidth >= 0) + pNewPartWidthArray[iPart] = pNewPartWidthArray[iPart -1] + iWidth; + else + pNewPartWidthArray[iPart] = -1; + } + + // Set the statusbar parts with our new parts count and part widths + BOOL bResult = (BOOL)SendMessage(SB_SETPARTS, NewPartsCount, (LPARAM)pNewPartWidthArray); + + return bResult; + } + + inline void CStatusBar::SetSimple(BOOL fSimple /* = TRUE*/) + { + assert(::IsWindow(m_hWnd)); + SendMessage(SB_SIMPLE, (WPARAM)fSimple, 0L); + } + +} // namespace Win32xx + +#endif // #ifndef _WIN32XX_STATUSBAR_H_ diff --git a/mmc_updater/depends/win32cpp/stdcontrols.h b/mmc_updater/depends/win32cpp/stdcontrols.h new file mode 100644 index 00000000..b362f07b --- /dev/null +++ b/mmc_updater/depends/win32cpp/stdcontrols.h @@ -0,0 +1,1000 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// stdcontrols.h +// Declaration of the CButton, CEdit, CListBox and CStatic classes + +// The Button, Edit, ListBox and Static controls are often referred to +// as "standard controls". These set of older controls were originally +// developed for Win16 operating systems (Windows 3.1 and 3.11). They use an +// older form of notification, and send their notifications via a WM_COMMAND +// message. Newer controls send their notifications via a WM_NOTIFY message. + + +#ifndef _WIN32XX_STDCONTROLS_H_ +#define _WIN32XX_STDCONTROLS_H_ + +#include "wincore.h" + + +namespace Win32xx +{ + class CButton : public CWnd + { + public: + CButton() {} + virtual ~CButton() {} + + // Attributes + HBITMAP GetBitmap() const; + UINT GetButtonStyle() const; + int GetCheck() const; + HCURSOR GetCursor() const; + HICON GetIcon() const; + UINT GetState() const; + HBITMAP SetBitmap(HBITMAP hBitmap) const; + void SetButtonStyle(DWORD dwStyle, BOOL bRedraw) const; + void SetCheck(int nCheckState) const; + HCURSOR SetCursor(HCURSOR hCursor) const; + HICON SetIcon(HICON hIcon) const; + void SetState(BOOL bHighlight) const; + + protected: + // Overridables + virtual void PreCreate(CREATESTRUCT& cs); + }; + + class CEdit : public CWnd + { + public: + // Construction + CEdit() {} + virtual ~CEdit() {} + + // Attributes + BOOL CanUndo() const; + int CharFromPos(CPoint pt) const; + int GetFirstVisibleLine() const; + HLOCAL GetHandle() const; + UINT GetLimitText() const; + int GetLine(int nIndex, LPTSTR lpszBuffer) const; + int GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const; + int GetLineCount() const; + DWORD GetMargins() const; + BOOL GetModify() const; + TCHAR GetPasswordChar() const; + void GetRect(LPRECT lpRect) const; + void GetSel(int& nStartChar, int& nEndChar) const; + DWORD GetSel() const; + CPoint PosFromChar(UINT nChar) const; + void SetHandle(HLOCAL hBuffer) const; + void SetLimitText(UINT nMax) const; + void SetMargins(UINT nLeft, UINT nRight) const; + void SetModify(BOOL bModified = TRUE) const; + + // Operations + void EmptyUndoBuffer() const; + BOOL FmtLines(BOOL bAddEOL) const; + void LimitText(int nChars = 0) const; + int LineFromChar(int nIndex = -1) const; + int LineIndex(int nLine = -1) const; + int LineLength(int nLine = -1) const; + void LineScroll(int nLines, int nChars = 0) const; + void ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo) const; + void SetPasswordChar(TCHAR ch) const; + BOOL SetReadOnly(BOOL bReadOnly = TRUE) const; + void SetRect(LPCRECT lpRect) const; + void SetRectNP(LPCRECT lpRect) const; + void SetSel(DWORD dwSelection, BOOL bNoScroll) const; + void SetSel(int nStartChar, int nEndChar, BOOL bNoScroll) const; + BOOL SetTabStops(int nTabStops, LPINT rgTabStops) const; + BOOL SetTabStops() const; + BOOL SetTabStops(const int& cxEachStop) const; + + //Clipboard Operations + void Clear() const; + void Copy() const; + void Cut() const; + void Paste() const; + void Undo() const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc); + }; + + class CListBox : public CWnd + { + public: + CListBox() {} + virtual ~CListBox() {} + + // General Operations + int GetCount() const; + int GetHorizontalExtent() const; + DWORD GetItemData(int nIndex) const; + void* GetItemDataPtr(int nIndex) const; + int GetItemHeight(int nIndex) const; + int GetItemRect(int nIndex, LPRECT lpRect) const; + LCID GetLocale() const; + int GetSel(int nIndex) const; + int GetText(int nIndex, LPTSTR lpszBuffer) const; + int GetTextLen(int nIndex) const; + int GetTopIndex() const; + UINT ItemFromPoint(CPoint pt, BOOL& bOutside ) const; + void SetColumnWidth(int cxWidth) const; + void SetHorizontalExtent(int cxExtent) const; + int SetItemData(int nIndex, DWORD dwItemData) const; + int SetItemDataPtr(int nIndex, void* pData) const; + int SetItemHeight(int nIndex, UINT cyItemHeight) const; + LCID SetLocale(LCID nNewLocale) const; + BOOL SetTabStops(int nTabStops, LPINT rgTabStops) const; + void SetTabStops() const; + BOOL SetTabStops(const int& cxEachStop) const; + int SetTopIndex(int nIndex) const; + + // Single-Selection Operations + int GetCurSel() const; + int SetCurSel(int nSelect) const; + + // Multiple-Selection Operations + int GetAnchorIndex() const; + int GetCaretIndex() const; + int GetSelCount() const; + int GetSelItems(int nMaxItems, LPINT rgIndex) const; + int SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem) const; + void SetAnchorIndex(int nIndex) const; + int SetCaretIndex(int nIndex, BOOL bScroll) const; + int SetSel(int nIndex, BOOL bSelect) const; + + // String Operations + int AddString(LPCTSTR lpszItem) const; + int DeleteString(UINT nIndex) const; + int Dir(UINT attr, LPCTSTR lpszWildCard) const; + int FindString(int nStartAfter, LPCTSTR lpszItem) const; + int FindStringExact(int nIndexStart, LPCTSTR lpszFind) const; + int InsertString(int nIndex, LPCTSTR lpszItem) const; + void ResetContent() const; + int SelectString(int nStartAfter, LPCTSTR lpszItem) const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc); + }; + + class CStatic : public CWnd + { + public: + CStatic() {} + virtual ~CStatic() {} + + // Operations + HBITMAP GetBitmap() const; + HCURSOR GetCursor() const; + HENHMETAFILE GetEnhMetaFile() const; + HICON GetIcon() const; + HBITMAP SetBitmap(HBITMAP hBitmap) const; + HCURSOR SetCursor(HCURSOR hCursor) const; + HENHMETAFILE SetEnhMetaFile(HENHMETAFILE hMetaFile) const; + HICON SetIcon(HICON hIcon) const; + + protected: + // Overridables + virtual void PreRegisterClass(WNDCLASS &wc); + + }; + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + //////////////////////////////////////// + // Definitions for the CButton class + // + inline HBITMAP CButton::GetBitmap() const + // returns the handle to the bitmap associated with the button + { + assert(::IsWindow(m_hWnd)); + return (HBITMAP)SendMessage(BM_GETIMAGE, IMAGE_BITMAP, 0); + } + + inline UINT CButton::GetButtonStyle() const + // returns the style of the button + { + assert(::IsWindow(m_hWnd)); + return (UINT)GetWindowLongPtr(GWL_STYLE) & 0xff; + } + + inline int CButton::GetCheck() const + // returns the check state of the button + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(BM_GETCHECK, 0, 0); + } + + inline HCURSOR CButton::GetCursor() const + // returns the handle to the cursor associated withe the button + { + assert(::IsWindow(m_hWnd)); + return (HCURSOR)::SendMessage(m_hWnd, BM_GETIMAGE, IMAGE_CURSOR, 0L); + } + + inline HICON CButton::GetIcon() const + // returns the handle to the icon associated withe the button + { + assert(::IsWindow(m_hWnd)); + return (HICON)SendMessage(BM_GETIMAGE, IMAGE_ICON, 0); + } + + inline UINT CButton::GetState() const + // returns the state of the button + { + assert(::IsWindow(m_hWnd)); + return (UINT)SendMessage(BM_GETSTATE, 0, 0); + } + + inline HBITMAP CButton::SetBitmap(HBITMAP hBitmap) const + // sets the bitmap associated with the button + { + assert(::IsWindow(m_hWnd)); + return (HBITMAP)SendMessage(BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap); + } + + inline void CButton::SetButtonStyle(DWORD dwStyle, BOOL bRedraw) const + // sets the button style + { + assert(::IsWindow(m_hWnd)); + SendMessage(BM_SETSTYLE, dwStyle, bRedraw); + } + + inline void CButton::SetCheck(int nCheckState) const + // sets the button check state + { + assert(::IsWindow(m_hWnd)); + SendMessage(BM_SETCHECK, nCheckState, 0); + } + + inline HCURSOR CButton::SetCursor(HCURSOR hCursor) const + // sets the cursor associated with the button + { + assert(::IsWindow(m_hWnd)); + return (HCURSOR)SendMessage(STM_SETIMAGE, IMAGE_CURSOR, (LPARAM)hCursor); + } + + inline HICON CButton::SetIcon(HICON hIcon) const + // sets the icon associated with the button + { + assert(::IsWindow(m_hWnd)); + return (HICON)SendMessage( BM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); + } + + inline void CButton::SetState(BOOL bHighlight) const + // sets the button state + { + assert(::IsWindow(m_hWnd)); + SendMessage(BM_SETSTATE, bHighlight, 0); + } + + inline void CButton::PreCreate(CREATESTRUCT& cs) + { + cs.lpszClass = _T("Button"); + } + + + //////////////////////////////////////// + // Definitions for the CEdit class + // + inline BOOL CEdit::CanUndo() const + // Returns TRUE if the edit control operation can be undone. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(EM_CANUNDO, 0, 0); + } + + inline int CEdit::CharFromPos(CPoint pt) const + // Returns the character index and line index of the character nearest the specified point. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(EM_CHARFROMPOS, 0, MAKELPARAM(pt.x, pt.y)); + } + + inline int CEdit::GetFirstVisibleLine() const + // Returns the zero-based index of the first visible character in a single-line edit control + // or the zero-based index of the uppermost visible line in a multiline edit control. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(EM_GETFIRSTVISIBLELINE, 0, 0); + } + + inline HLOCAL CEdit::GetHandle() const + // Returns a handle identifying the buffer containing the multiline edit control's text. + // It is not processed by single-line edit controls. + { + assert(::IsWindow(m_hWnd)); + return (HLOCAL)SendMessage(EM_GETHANDLE, 0, 0); + } + + inline UINT CEdit::GetLimitText() const + // Returns the current text limit, in characters. + { + assert(::IsWindow(m_hWnd)); + return (UINT)SendMessage(EM_GETLIMITTEXT, 0, 0); + } + + inline int CEdit::GetLine(int nIndex, LPTSTR lpszBuffer) const + // Copies characters to a buffer and returns the number of characters copied. + { + assert(::IsWindow(m_hWnd)); + return (int)::SendMessage(m_hWnd, EM_GETLINE, nIndex, (LPARAM)lpszBuffer); + } + + inline int CEdit::GetLine(int nIndex, LPTSTR lpszBuffer, int nMaxLength) const + // Copies characters to a buffer and returns the number of characters copied. + { + assert(::IsWindow(m_hWnd)); + *(LPWORD)lpszBuffer = (WORD)nMaxLength; + return (int)SendMessage(EM_GETLINE, nIndex, (LPARAM)lpszBuffer); + } + + inline int CEdit::GetLineCount() const + // Returns the number of lines in the edit control. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(EM_GETLINECOUNT, 0, 0); + } + + inline DWORD CEdit::GetMargins() const + // Returns the widths of the left and right margins. + { + assert(::IsWindow(m_hWnd)); + return (DWORD)SendMessage(EM_GETMARGINS, 0, 0); + } + + inline BOOL CEdit::GetModify() const + // Returns a flag indicating whether the content of an edit control has been modified. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(EM_GETMODIFY, 0, 0); + } + + inline TCHAR CEdit::GetPasswordChar() const + // Returns the character that edit controls use in conjunction with the ES_PASSWORD style. + { + assert(::IsWindow(m_hWnd)); + return (TCHAR)SendMessage(EM_GETPASSWORDCHAR, 0, 0); + } + + inline void CEdit::GetRect(LPRECT lpRect) const + // Returns the coordinates of the formatting rectangle in an edit control. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_GETRECT, 0, (LPARAM)lpRect); + } + + inline void CEdit::GetSel(int& nStartChar, int& nEndChar) const + // Returns the starting and ending character positions of the current selection in the edit control. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_GETSEL, (WPARAM)&nStartChar,(LPARAM)&nEndChar); + } + + inline DWORD CEdit::GetSel() const + // Returns the starting and ending character positions of the current selection in the edit control. + { + assert(::IsWindow(m_hWnd)); + return (DWORD)SendMessage(EM_GETSEL, 0, 0); + } + + inline CPoint CEdit::PosFromChar(UINT nChar) const + // Returns the client coordinates of the specified character. + { + assert(::IsWindow(m_hWnd)); + return CPoint( (DWORD)SendMessage(EM_POSFROMCHAR, nChar, 0)); + } + + inline void CEdit::SetHandle(HLOCAL hBuffer) const + // Sets a handle to the memory used as a text buffer, empties the undo buffer, + // resets the scroll positions to zero, and redraws the window. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_SETHANDLE, (WPARAM)hBuffer, 0); + } + + inline void CEdit::SetLimitText(UINT nMax) const + // Sets the maximum number of characters the user may enter in the edit control. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_SETLIMITTEXT, (WPARAM)nMax, 0); + } + + inline void CEdit::SetMargins(UINT nLeft, UINT nRight) const + // Sets the widths of the left and right margins, and redraws the edit control to reflect the new margins. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELONG(nLeft, nRight)); + } + + inline void CEdit::SetModify(BOOL bModified) const + // Sets or clears the modification flag to indicate whether the edit control has been modified. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_SETMODIFY, bModified, 0); + } + + inline void CEdit::EmptyUndoBuffer() const + // Empties the undo buffer and sets the undo flag retrieved by the EM_CANUNDO message to FALSE. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_EMPTYUNDOBUFFER, 0, 0); + } + + inline BOOL CEdit::FmtLines(BOOL bAddEOL) const + // Adds or removes soft line-break characters (two carriage returns and a line feed) to the ends of wrapped lines + // in a multiline edit control. It is not processed by single-line edit controls. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(EM_FMTLINES, bAddEOL, 0); + } + + inline void CEdit::LimitText(int nChars) const + // Sets the text limit of an edit control. The text limit is the maximum amount of text, in TCHARs, + // that the user can type into the edit control. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_LIMITTEXT, nChars, 0); + } + + inline int CEdit::LineFromChar(int nIndex) const + // Returns the zero-based number of the line in a multiline edit control that contains a specified character index. + // This message is the reverse of the EM_LINEINDEX message. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(EM_LINEFROMCHAR, (WPARAM)nIndex, 0); + } + + inline int CEdit::LineIndex(int nLine) const + // Returns the character of a line in a multiline edit control. + // This message is the reverse of the EM_LINEFROMCHAR message + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(EM_LINEINDEX, (WPARAM)nLine, 0); + } + + inline int CEdit::LineLength(int nLine) const + // Returns the length, in characters, of a single-line edit control. In a multiline edit control, + // returns the length, in characters, of a specified line. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(EM_LINELENGTH, (WPARAM)nLine, 0); + } + + inline void CEdit::LineScroll(int nLines, int nChars) const + // Scrolls the text vertically in a single-line edit control or horizontally in a multiline edit control. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_LINESCROLL, (WPARAM)nChars, (LPARAM)nLines); + } + + inline void CEdit::ReplaceSel(LPCTSTR lpszNewText, BOOL bCanUndo) const + // Replaces the current selection with the text in an application-supplied buffer, sends the parent window + // EN_UPDATE and EN_CHANGE messages, and updates the undo buffer. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_REPLACESEL, (WPARAM) bCanUndo, (LPARAM)lpszNewText); + } + + inline void CEdit::SetPasswordChar(TCHAR ch) const + // Defines the character that edit controls use in conjunction with the ES_PASSWORD style. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_SETPASSWORDCHAR, ch, 0); + } + + inline BOOL CEdit::SetReadOnly(BOOL bReadOnly) const + // Sets or removes the read-only style (ES_READONLY) in an edit control. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(EM_SETREADONLY, bReadOnly, 0); + } + + inline void CEdit::SetRect(LPCRECT lpRect) const + // Sets the formatting rectangle for the multiline edit control and redraws the window. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_SETRECT, 0, (LPARAM)lpRect); + } + + inline void CEdit::SetRectNP(LPCRECT lpRect) const + // Sets the formatting rectangle for the multiline edit control but does not redraw the window. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_SETRECTNP, 0, (LPARAM)lpRect); + } + + inline void CEdit::SetSel(DWORD dwSelection, BOOL bNoScroll) const + // Selects a range of characters in the edit control by setting the starting and ending positions to be selected. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_SETSEL, LOWORD(dwSelection), HIWORD(dwSelection)); + if (!bNoScroll) + SendMessage(EM_SCROLLCARET, 0, 0); + } + + inline void CEdit::SetSel(int nStartChar, int nEndChar, BOOL bNoScroll) const + // Selects a range of characters in the edit control by setting the starting and ending positions to be selected. + { + assert(::IsWindow(m_hWnd)); + SendMessage(m_hWnd, EM_SETSEL, nStartChar, nEndChar); + if (!bNoScroll) + SendMessage(EM_SCROLLCARET, 0, 0); + } + + inline BOOL CEdit::SetTabStops(int nTabStops, LPINT rgTabStops) const + // Sets tab-stop positions in the multiline edit control. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)::SendMessage(m_hWnd, EM_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops); + } + + inline BOOL CEdit::SetTabStops() const + // Sets tab-stop positions in the multiline edit control. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage( EM_SETTABSTOPS, 0, 0); + } + + inline BOOL CEdit::SetTabStops(const int& cxEachStop) const + // Sets tab-stop positions in the multiline edit control. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(EM_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop); + } + + inline void CEdit::Clear() const + // Clears the current selection, if any, in an edit control. + { + assert(::IsWindow(m_hWnd)); + SendMessage(WM_CLEAR, 0, 0); + } + + inline void CEdit::Copy() const + // Copies text to the clipboard unless the style is ES_PASSWORD, in which case the message returns zero. + { + assert(::IsWindow(m_hWnd)); + SendMessage(WM_COPY, 0, 0); + } + + inline void CEdit::Cut() const + // Cuts the selection to the clipboard, or deletes the character to the left of the cursor if there is no selection. + { + assert(::IsWindow(m_hWnd)); + SendMessage(WM_CUT, 0, 0); + } + + inline void CEdit::Paste() const + // Pastes text from the clipboard into the edit control window at the caret position. + { + assert(::IsWindow(m_hWnd)); + SendMessage(WM_PASTE, 0, 0); + } + + inline void CEdit::Undo() const + // Removes any text that was just inserted or inserts any deleted characters and sets the selection to the inserted text. + { + assert(::IsWindow(m_hWnd)); + SendMessage(EM_UNDO, 0, 0); + } + + inline void CEdit::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = _T("Edit"); + } + + + //////////////////////////////////////// + // Definitions for the CListbox class + // + inline int CListBox::GetCount() const + // Returns the number of items in the list box. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETCOUNT, 0, 0); + } + + inline int CListBox::GetHorizontalExtent() const + // Returns the scrollable width, in pixels, of a list box. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETHORIZONTALEXTENT, 0, 0); + } + + inline DWORD CListBox::GetItemData(int nIndex) const + // Returns the value associated with the specified item. + { + assert(::IsWindow(m_hWnd)); + return (DWORD)SendMessage(LB_GETITEMDATA, nIndex, 0); + } + + inline void* CListBox::GetItemDataPtr(int nIndex) const + // Returns the value associated with the specified item. + { + assert(::IsWindow(m_hWnd)); + return (LPVOID)SendMessage(LB_GETITEMDATA, nIndex, 0); + } + + inline int CListBox::GetItemHeight(int nIndex) const + // Returns the height, in pixels, of an item in a list box. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETITEMHEIGHT, nIndex, 0L); + } + + inline int CListBox::GetItemRect(int nIndex, LPRECT lpRect) const + // Retrieves the client coordinates of the specified list box item. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETITEMRECT, nIndex, (LPARAM)lpRect); + } + + inline LCID CListBox::GetLocale() const + // Retrieves the locale of the list box. The high-order word contains the country/region code + // and the low-order word contains the language identifier. + { + assert(::IsWindow(m_hWnd)); + return (LCID)::SendMessage(m_hWnd, LB_GETLOCALE, 0, 0); + } + + inline int CListBox::GetSel(int nIndex) const + // Returns the selection state of a list box item. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETSEL, nIndex, 0); + } + + inline int CListBox::GetText(int nIndex, LPTSTR lpszBuffer) const + // Retrieves the string associated with a specified item and the length of the string. + { + assert(::IsWindow(m_hWnd)); + return (int)::SendMessage(m_hWnd, LB_GETTEXT, nIndex, (LPARAM)lpszBuffer); + } + + inline int CListBox::GetTextLen(int nIndex) const + // Returns the length, in characters, of the string associated with a specified item. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage( LB_GETTEXTLEN, nIndex, 0); + } + + inline int CListBox::GetTopIndex() const + // Returns the index of the first visible item in a list box. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETTOPINDEX, 0, 0); + } + + inline UINT CListBox::ItemFromPoint(CPoint pt, BOOL& bOutside) const + // Retrieves the zero-based index of the item nearest the specified point in a list box. + { + assert(::IsWindow(m_hWnd)); + DWORD dw = (DWORD)::SendMessage(m_hWnd, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x, pt.y)); + bOutside = !!HIWORD(dw); + return LOWORD(dw); + } + + inline void CListBox::SetColumnWidth(int cxWidth) const + // Sets the width, in pixels, of all columns in a list box. + { + assert(::IsWindow(m_hWnd)); + SendMessage(LB_SETCOLUMNWIDTH, cxWidth, 0); + } + + inline void CListBox::SetHorizontalExtent(int cxExtent) const + // Sets the scrollable width, in pixels, of a list box. + { + assert(::IsWindow(m_hWnd)); + SendMessage(LB_SETHORIZONTALEXTENT, cxExtent, 0); + } + + inline int CListBox::SetItemData(int nIndex, DWORD dwItemData) const + // Associates a value with a list box item. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_SETITEMDATA, nIndex, (LPARAM)dwItemData); + } + + inline int CListBox::SetItemDataPtr(int nIndex, void* pData) const + // Associates a value with a list box item. + { + assert(::IsWindow(m_hWnd)); + return SetItemData(nIndex, (DWORD)(DWORD_PTR)pData); + } + + inline int CListBox::SetItemHeight(int nIndex, UINT cyItemHeight) const + // Sets the height, in pixels, of an item or items in a list box. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_SETITEMHEIGHT, nIndex, MAKELONG(cyItemHeight, 0)); + } + + inline LCID CListBox::SetLocale(LCID nNewLocale) const + // Sets the locale of a list box and returns the previous locale identifier. + { + assert(::IsWindow(m_hWnd)); + return (LCID)::SendMessage(m_hWnd, LB_SETLOCALE, (WPARAM)nNewLocale, 0); + } + + inline BOOL CListBox::SetTabStops(int nTabStops, LPINT rgTabStops) const + // Sets the tab stops to those specified in a specified array. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(LB_SETTABSTOPS, nTabStops, (LPARAM)rgTabStops); + } + + inline void CListBox::SetTabStops() const + // Sets the tab stops to those specified in a specified array. + { + assert(::IsWindow(m_hWnd)); + SendMessage(LB_SETTABSTOPS, 0, 0); + } + + inline BOOL CListBox::SetTabStops(const int& cxEachStop) const + // Sets the tab stops to those specified in a specified array. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(LB_SETTABSTOPS, 1, (LPARAM)(LPINT)&cxEachStop); + } + + inline int CListBox::SetTopIndex(int nIndex) const + // Scrolls the list box so the specified item is at the top of the visible range. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_SETTOPINDEX, nIndex, 0); + } + + inline int CListBox::GetCurSel() const + // Returns the index of the currently selected item. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETCURSEL, 0, 0); + } + + inline int CListBox::SetCurSel(int nSelect) const + // Selects a specified list box item. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_SETCURSEL, nSelect, 0); + } + + inline int CListBox::GetAnchorIndex() const + // Returns the index of the item that the mouse last selected. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETANCHORINDEX, 0, 0); + } + + inline int CListBox::GetCaretIndex() const + // Returns the index of the item that has the focus rectangle. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETCARETINDEX, 0, 0L); + } + + inline int CListBox::GetSelCount() const + // Returns the number of selected items in a multiple-selection list box. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETSELCOUNT, 0, 0); + } + + inline int CListBox::GetSelItems(int nMaxItems, LPINT rgIndex) const + // Creates an array of the indexes of all selected items in a multiple-selection list box + // and returns the total number of selected items. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_GETSELITEMS, nMaxItems, (LPARAM)rgIndex); + } + + inline int CListBox::SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem) const + // Selects a specified range of items in a list box. + { + assert(::IsWindow(m_hWnd)); + if (bSelect) + return (int)SendMessage(LB_SELITEMRANGEEX, nFirstItem, nLastItem); + else + return (int)SendMessage(LB_SELITEMRANGEEX, nLastItem, nFirstItem); + } + + inline void CListBox::SetAnchorIndex(int nIndex) const + // Sets the item that the mouse last selected to a specified item. + { + assert(::IsWindow(m_hWnd)); + SendMessage(LB_SETANCHORINDEX, nIndex, 0); + } + + inline int CListBox::SetCaretIndex(int nIndex, BOOL bScroll) const + // Sets the focus rectangle to a specified list box item. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_SETCARETINDEX, nIndex, MAKELONG(bScroll, 0)); + } + + inline int CListBox::SetSel(int nIndex, BOOL bSelect) const + // Selects an item in a multiple-selection list box. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_SETSEL, bSelect, nIndex); + } + + inline int CListBox::AddString(LPCTSTR lpszItem) const + // Adds a string to a list box and returns its index. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_ADDSTRING, 0, (LPARAM)lpszItem); + } + + inline int CListBox::DeleteString(UINT nIndex) const + // Removes a string from a list box and returns the number of strings remaining in the list. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_DELETESTRING, nIndex, 0); + } + + inline int CListBox::Dir(UINT attr, LPCTSTR lpszWildCard) const + // Adds a list of filenames to a list box and returns the index of the last filename added. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_DIR, attr, (LPARAM)lpszWildCard); + } + + inline int CListBox::FindString(int nStartAfter, LPCTSTR lpszItem) const + // Returns the index of the first string in the list box that begins with a specified string. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_FINDSTRING, nStartAfter, (LPARAM)lpszItem); + } + + inline int CListBox::FindStringExact(int nIndexStart, LPCTSTR lpszFind) const + // Returns the index of the string in the list box that is equal to a specified string. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_FINDSTRINGEXACT, nIndexStart, (LPARAM)lpszFind); + } + + inline int CListBox::InsertString(int nIndex, LPCTSTR lpszItem) const + // Inserts a string at a specified index in a list box. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(LB_INSERTSTRING, nIndex, (LPARAM)lpszItem); + } + + inline void CListBox::ResetContent() const + // Removes all items from a list box. + { + assert(::IsWindow(m_hWnd)); + SendMessage(LB_RESETCONTENT, 0, 0); + } + + inline int CListBox::SelectString(int nStartAfter, LPCTSTR lpszItem) const + // Selects the first string it finds that matches a specified prefix. + { + assert(::IsWindow(m_hWnd)); + return (int)::SendMessage(m_hWnd, LB_SELECTSTRING, nStartAfter, (LPARAM)lpszItem); + } + + inline void CListBox::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = _T("ListBox"); + } + + + //////////////////////////////////////// + // Definitions for the CStatic class + // + inline HBITMAP CStatic::GetBitmap() const + // Returns the handle to the bitmap for the static control + { + assert(::IsWindow(m_hWnd)); + return (HBITMAP)SendMessage(STM_GETIMAGE, IMAGE_BITMAP, 0); + } + + inline HCURSOR CStatic::GetCursor() const + // Returns the handle to the icon for the static control + { + assert(::IsWindow(m_hWnd)); + return (HCURSOR)SendMessage(STM_GETIMAGE, IMAGE_CURSOR, 0); + } + + inline HENHMETAFILE CStatic::GetEnhMetaFile() const + // Returns the handle to the enhanced metafile for the static control + { + assert(::IsWindow(m_hWnd)); + return (HENHMETAFILE)SendMessage(STM_GETIMAGE, IMAGE_ENHMETAFILE, 0); + } + + inline HICON CStatic::GetIcon() const + // Returns the handle to the icon for the static control + { + assert(::IsWindow(m_hWnd)); + return (HICON)SendMessage(STM_GETIMAGE, IMAGE_ICON, 0); + } + + inline HBITMAP CStatic::SetBitmap(HBITMAP hBitmap) const + // Sets the handle to the bitmap for the static control + { + assert(::IsWindow(m_hWnd)); + return (HBITMAP)SendMessage(STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap); + } + + inline HCURSOR CStatic::SetCursor(HCURSOR hCursor) const + // Sets the handle to the cursor for the static control + { + assert(::IsWindow(m_hWnd)); + return (HCURSOR)SendMessage(STM_SETIMAGE, IMAGE_CURSOR, (LPARAM)hCursor); + } + + inline HENHMETAFILE CStatic::SetEnhMetaFile(HENHMETAFILE hMetaFile) const + // Sets the handle to the enhanced metafile for the static control + { + assert(::IsWindow(m_hWnd)); + return (HENHMETAFILE)SendMessage(STM_SETIMAGE, IMAGE_ENHMETAFILE, (LPARAM)hMetaFile); + } + + inline HICON CStatic::SetIcon(HICON hIcon) const + // Sets the handle to the icon for the static control + { + assert(::IsWindow(m_hWnd)); + return (HICON)SendMessage(STM_SETIMAGE, IMAGE_ICON, (LPARAM)hIcon); + } + + inline void CStatic::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = _T("Static"); + } + +} + +#endif // _WIN32XX_STDCONTROLS_H_ + diff --git a/mmc_updater/depends/win32cpp/tab.h b/mmc_updater/depends/win32cpp/tab.h new file mode 100644 index 00000000..15699144 --- /dev/null +++ b/mmc_updater/depends/win32cpp/tab.h @@ -0,0 +1,1658 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// tab.h +// Declaration of the CTab and CMDITab classes + +#ifndef _WIN32XX_TAB_H_ +#define _WIN32XX_TAB_H_ + +#include "wincore.h" +#include "dialog.h" +#include "gdi.h" +#include "default_resource.h" + +namespace Win32xx +{ + + struct TabPageInfo + { + TCHAR szTabText[MAX_MENU_STRING]; + int iImage; // index of this tab's image + int idTab; // identifier for this tab (optional) + CWnd* pView; // pointer to the view window + }; + + class CTab : public CWnd + { + protected: + // Declaration of the CSelectDialog class, a nested class of CTab + // It creates the dialog to choose which tab to activate + class CSelectDialog : public CDialog + { + public: + CSelectDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent = NULL); + virtual ~CSelectDialog() {} + virtual void AddItem(LPCTSTR szString); + virtual BOOL IsTab() const { return FALSE; } + + protected: + virtual BOOL OnInitDialog(); + virtual void OnOK(); + virtual void OnCancel() { EndDialog(-2); } + + private: + CSelectDialog(const CSelectDialog&); // Disable copy construction + CSelectDialog& operator = (const CSelectDialog&); // Disable assignment operator + + std::vector<tString> m_vItems; + int IDC_LIST; + + }; + public: + CTab(); + virtual ~CTab(); + virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText, HICON hIcon, UINT idTab); + virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText, int nID_Icon, UINT idTab = 0); + virtual int AddTabPage(WndPtr pView, LPCTSTR szTabText); + virtual CRect GetCloseRect() const; + virtual CRect GetListRect() const; + virtual HMENU GetListMenu(); + virtual BOOL GetTabsAtTop() const; + virtual int GetTabIndex(CWnd* pWnd) const; + virtual TabPageInfo GetTabPageInfo(UINT nTab) const; + virtual int GetTextHeight() const; + virtual void RecalcLayout(); + virtual void RemoveTabPage(int nPage); + virtual void SelectPage(int nPage); + virtual void SetFixedWidth(BOOL bEnabled); + virtual void SetOwnerDraw(BOOL bEnabled); + virtual void SetShowButtons(BOOL bShow); + virtual void SetTabIcon(int i, HICON hIcon); + virtual void SetTabsAtTop(BOOL bTop); + virtual void SetTabText(UINT nTab, LPCTSTR szText); + virtual void SwapTabs(UINT nTab1, UINT nTab2); + + // Attributes + std::vector <TabPageInfo>& GetAllTabs() const { return (std::vector <TabPageInfo>&) m_vTabPageInfo; } + HIMAGELIST GetImageList() const { return m_himlTab; } + BOOL GetShowButtons() const { return m_bShowButtons; } + int GetTabHeight() const { return m_nTabHeight; } + CWnd* GetActiveView() const { return m_pActiveView; } + void SetTabHeight(int nTabHeight) { m_nTabHeight = nTabHeight; NotifyChanged();} + + // Wrappers for Win32 Macros + void AdjustRect(BOOL fLarger, RECT *prc) const; + int GetCurFocus() const; + int GetCurSel() const; + BOOL GetItem(int iItem, LPTCITEM pitem) const; + int GetItemCount() const; + int HitTest(TCHITTESTINFO& info) const; + void SetCurFocus(int iItem) const; + int SetCurSel(int iItem) const; + DWORD SetItemSize(int cx, int cy) const; + int SetMinTabWidth(int cx) const; + void SetPadding(int cx, int cy) const; + + protected: + virtual void DrawCloseButton(CDC& DrawDC); + virtual void DrawListButton(CDC& DrawDC); + virtual void DrawTabs(CDC& dcMem); + virtual void DrawTabBorders(CDC& dcMem, CRect& rcTab); + virtual void OnCreate(); + virtual void OnLButtonDown(WPARAM wParam, LPARAM lParam); + virtual void OnLButtonUp(WPARAM wParam, LPARAM lParam); + virtual void OnMouseLeave(WPARAM wParam, LPARAM lParam); + virtual void OnMouseMove(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNCHitTest(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam); + virtual void NotifyChanged(); + virtual void Paint(); + virtual void PreCreate(CREATESTRUCT& cs); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual void SetTabSize(); + virtual void ShowListDialog(); + virtual void ShowListMenu(); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CTab(const CTab&); // Disable copy construction + CTab& operator = (const CTab&); // Disable assignment operator + + SIZE GetMaxTabSize() const; + void ShowActiveView(CWnd* pView); + + std::vector<TabPageInfo> m_vTabPageInfo; + std::vector<WndPtr> m_vTabViews; + CFont m_Font; + HIMAGELIST m_himlTab; + HMENU m_hListMenu; + CWnd* m_pActiveView; + BOOL m_bShowButtons; // Show or hide the close and list button + BOOL m_IsTracking; + BOOL m_IsClosePressed; + BOOL m_IsListPressed; + BOOL m_IsListMenuActive; + int m_nTabHeight; + }; + + //////////////////////////////////////// + // Declaration of the CTabbedMDI class + class CTabbedMDI : public CWnd + { + public: + CTabbedMDI(); + virtual ~CTabbedMDI(); + virtual CWnd* AddMDIChild(CWnd* pView, LPCTSTR szTabText, int idMDIChild = 0); + virtual void CloseActiveMDI(); + virtual void CloseAllMDIChildren(); + virtual void CloseMDIChild(int nTab); + virtual CWnd* GetActiveMDIChild() const; + virtual int GetActiveMDITab() const; + virtual CWnd* GetMDIChild(int nTab) const; + virtual int GetMDIChildCount() const; + virtual int GetMDIChildID(int nTab) const; + virtual LPCTSTR GetMDIChildTitle(int nTab) const; + virtual HMENU GetListMenu() const { return GetTab().GetListMenu(); } + virtual CTab& GetTab() const {return (CTab&)m_Tab;} + virtual BOOL LoadRegistrySettings(tString tsRegistryKeyName); + virtual void RecalcLayout(); + virtual BOOL SaveRegistrySettings(tString tsRegistryKeyName); + virtual void SetActiveMDIChild(CWnd* pWnd); + virtual void SetActiveMDITab(int nTab); + + protected: + virtual HWND Create(CWnd* pParent); + virtual CWnd* NewMDIChildFromID(int idMDIChild); + virtual void OnCreate(); + virtual void OnDestroy(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual void OnWindowPosChanged(WPARAM wParam, LPARAM lParam); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CTabbedMDI(const CTabbedMDI&); // Disable copy construction + CTabbedMDI& operator = (const CTabbedMDI&); // Disable assignment operator + + CTab m_Tab; + }; + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + ///////////////////////////////////////////////////////////// + // Definitions for the CSelectDialog class nested within CTab + // + inline CTab::CSelectDialog::CSelectDialog(LPCDLGTEMPLATE lpTemplate, CWnd* pParent) : + CDialog(lpTemplate, pParent), IDC_LIST(121) + { + } + + inline BOOL CTab::CSelectDialog::OnInitDialog() + { + for (UINT u = 0; u < m_vItems.size(); ++u) + { + SendDlgItemMessage(IDC_LIST, LB_ADDSTRING, 0, (LPARAM) m_vItems[u].c_str()); + } + + return true; + } + + inline void CTab::CSelectDialog::AddItem(LPCTSTR szString) + { + m_vItems.push_back(szString); + } + + inline void CTab::CSelectDialog::OnOK() + { + int iSelect = (int)SendDlgItemMessage(IDC_LIST, LB_GETCURSEL, 0, 0); + if (iSelect != LB_ERR) + EndDialog(iSelect); + else + EndDialog(-2); + } + + + ////////////////////////////////////////////////////////// + // Definitions for the CTab class + // + inline CTab::CTab() : m_hListMenu(NULL), m_pActiveView(NULL), m_bShowButtons(FALSE), m_IsTracking(FALSE), m_IsClosePressed(FALSE), + m_IsListPressed(FALSE), m_IsListMenuActive(FALSE), m_nTabHeight(0) + { + // Create and assign the image list + m_himlTab = ImageList_Create(16, 16, ILC_MASK|ILC_COLOR32, 0, 0); + + // Set the tab control's font + NONCLIENTMETRICS info = {0}; + info.cbSize = GetSizeofNonClientMetrics(); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(info), &info, 0); + m_Font.CreateFontIndirect(&info.lfStatusFont); + } + + inline CTab::~CTab() + { + ImageList_Destroy(m_himlTab); + + if (IsMenu(m_hListMenu)) ::DestroyMenu(m_hListMenu); + } + + inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText, HICON hIcon, UINT idTab) + { + assert(pView.get()); + assert(lstrlen(szTabText) < MAX_MENU_STRING); + + m_vTabViews.push_back(pView); + + TabPageInfo tpi = {0}; + tpi.pView = pView.get(); + tpi.idTab = idTab; + lstrcpyn(tpi.szTabText, szTabText, MAX_MENU_STRING); + if (hIcon) + tpi.iImage = ImageList_AddIcon(GetImageList(), hIcon); + else + tpi.iImage = -1; + + int iNewPage = (int)m_vTabPageInfo.size(); + m_vTabPageInfo.push_back(tpi); + + if (m_hWnd) + { + TCITEM tie = {0}; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = tpi.iImage; + tie.pszText = tpi.szTabText; + TabCtrl_InsertItem(m_hWnd, iNewPage, &tie); + + SetTabSize(); + SelectPage(iNewPage); + NotifyChanged(); + } + + return iNewPage; + } + + inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText, int idIcon, UINT idTab /* = 0*/) + { + HICON hIcon = (HICON)LoadImage(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(idIcon), IMAGE_ICON, 0, 0, LR_SHARED); + return AddTabPage(pView, szTabText, hIcon, idTab); + } + + inline int CTab::AddTabPage(WndPtr pView, LPCTSTR szTabText) + { + return AddTabPage(pView, szTabText, (HICON)0, 0); + } + + inline void CTab::DrawCloseButton(CDC& DrawDC) + { + // The close button isn't displayed on Win95 + if (GetWinVersion() == 1400) return; + + if (!m_bShowButtons) return; + if (!GetActiveView()) return; + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_FIXEDWIDTH)) return; + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) return; + + // Determine the close button's drawing position relative to the window + CRect rcClose = GetCloseRect(); + + CPoint pt = GetCursorPos(); + ScreenToClient(pt); + UINT uState = rcClose.PtInRect(pt)? m_IsClosePressed? 2: 1: 0; + + // Draw the outer highlight for the close button + if (!IsRectEmpty(&rcClose)) + { + switch (uState) + { + case 0: + { + DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220)); + + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + break; + } + + case 1: + { + // Draw outline, white at top, black on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + } + + break; + case 2: + { + // Draw outline, black on top, white on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.MoveTo(rcClose.left, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.bottom); + DrawDC.LineTo(rcClose.right, rcClose.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.LineTo(rcClose.left, rcClose.top); + DrawDC.LineTo(rcClose.left, rcClose.bottom); + } + break; + } + + // Manually draw close button + DrawDC.CreatePen(PS_SOLID, 1, RGB(64, 64, 64)); + + DrawDC.MoveTo(rcClose.left + 3, rcClose.top +3); + DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.left + 4, rcClose.top +3); + DrawDC.LineTo(rcClose.right - 2, rcClose.bottom -3); + + DrawDC.MoveTo(rcClose.left + 3, rcClose.top +4); + DrawDC.LineTo(rcClose.right - 3, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -3, rcClose.top +3); + DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -3, rcClose.top +4); + DrawDC.LineTo(rcClose.left + 3, rcClose.bottom -2); + + DrawDC.MoveTo(rcClose.right -4, rcClose.top +3); + DrawDC.LineTo(rcClose.left + 2, rcClose.bottom -3); + } + } + + inline void CTab::DrawListButton(CDC& DrawDC) + { + // The list button isn't displayed on Win95 + if (GetWinVersion() == 1400) return; + + if (!m_bShowButtons) return; + if (!GetActiveView()) return; + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_FIXEDWIDTH)) return; + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) return; + + // Determine the list button's drawing position relative to the window + CRect rcList = GetListRect(); + + CPoint pt = GetCursorPos(); + ScreenToClient(pt); + UINT uState = rcList.PtInRect(pt)? 1: 0; + if (m_IsListMenuActive) uState = 2; + + // Draw the outer highlight for the list button + if (!IsRectEmpty(&rcList)) + { + switch (uState) + { + case 0: + { + DrawDC.CreatePen(PS_SOLID, 1, RGB(232, 228, 220)); + + DrawDC.MoveTo(rcList.left, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.top); + DrawDC.LineTo(rcList.left, rcList.top); + DrawDC.LineTo(rcList.left, rcList.bottom); + break; + } + + case 1: + { + // Draw outline, white at top, black on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.MoveTo(rcList.left, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.LineTo(rcList.left, rcList.top); + DrawDC.LineTo(rcList.left, rcList.bottom); + } + + break; + case 2: + { + // Draw outline, black on top, white on bottom + DrawDC.CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); + DrawDC.MoveTo(rcList.left, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.bottom); + DrawDC.LineTo(rcList.right, rcList.top); + DrawDC.CreatePen(PS_SOLID, 1, RGB(0, 0, 0)); + DrawDC.LineTo(rcList.left, rcList.top); + DrawDC.LineTo(rcList.left, rcList.bottom); + } + break; + } + + // Manually draw list button + DrawDC.CreatePen(PS_SOLID, 1, RGB(64, 64, 64)); + + int MaxLength = (int)(0.65 * rcList.Width()); + int topGap = 1 + rcList.Height()/3; + for (int i = 0; i <= MaxLength/2; i++) + { + int Length = MaxLength - 2*i; + DrawDC.MoveTo(rcList.left +1 + (rcList.Width() - Length)/2, rcList.top +topGap +i); + DrawDC.LineTo(rcList.left +1 + (rcList.Width() - Length)/2 + Length, rcList.top +topGap +i); + } + } + } + + inline void CTab::DrawTabs(CDC& dcMem) + { + // Draw the tab buttons: + for (int i = 0; i < TabCtrl_GetItemCount(m_hWnd); ++i) + { + CRect rcItem; + TabCtrl_GetItemRect(m_hWnd, i, &rcItem); + if (!rcItem.IsRectEmpty()) + { + if (i == TabCtrl_GetCurSel(m_hWnd)) + { + dcMem.CreateSolidBrush(RGB(248,248,248)); + dcMem.SetBkColor(RGB(248,248,248)); + } + else + { + dcMem.CreateSolidBrush(RGB(200,200,200)); + dcMem.SetBkColor(RGB(200,200,200)); + } + + dcMem.CreatePen(PS_SOLID, 1, RGB(160, 160, 160)); + dcMem.RoundRect(rcItem.left+1, rcItem.top, rcItem.right+2, rcItem.bottom, 6, 6); + + if (rcItem.Width() >= 24) + { + TCHAR szText[30]; + TCITEM tcItem = {0}; + tcItem.mask = TCIF_TEXT | TCIF_IMAGE; + tcItem.cchTextMax = 30; + tcItem.pszText = szText; + TabCtrl_GetItem(m_hWnd, i, &tcItem); + int xImage; + int yImage; + int yOffset = 0; + if (ImageList_GetIconSize(m_himlTab, &xImage, &yImage)) + yOffset = (rcItem.Height() - yImage)/2; + + // Draw the icon + ImageList_Draw(m_himlTab, tcItem.iImage, dcMem, rcItem.left+5, rcItem.top+yOffset, ILD_NORMAL); + + // Draw the text + dcMem.SelectObject(&m_Font); + + // Calculate the size of the text + CRect rcText = rcItem; + + int iImageSize = 20; + int iPadding = 4; + if (tcItem.iImage >= 0) + rcText.left += iImageSize; + + rcText.left += iPadding; + dcMem.DrawText(szText, -1, rcText, DT_LEFT|DT_VCENTER|DT_SINGLELINE|DT_END_ELLIPSIS); + } + } + } + } + + inline void CTab::DrawTabBorders(CDC& dcMem, CRect& rcTab) + { + BOOL IsBottomTab = (BOOL)GetWindowLongPtr(GWL_STYLE) & TCS_BOTTOM; + + // Draw a lighter rectangle touching the tab buttons + CRect rcItem; + TabCtrl_GetItemRect(m_hWnd, 0, &rcItem); + int left = rcItem.left +1; + int right = rcTab.right; + int top = rcTab.bottom; + int bottom = top + 3; + + if (!IsBottomTab) + { + bottom = MAX(rcTab.top, m_nTabHeight +4); + top = bottom -3; + } + + dcMem.CreateSolidBrush(RGB(248,248,248)); + dcMem.CreatePen(PS_SOLID, 1, RGB(248,248,248)); + if (!rcItem.IsRectEmpty()) + { + dcMem.Rectangle(left, top, right, bottom); + + // Draw a darker line below the rectangle + dcMem.CreatePen(PS_SOLID, 1, RGB(160, 160, 160)); + if (IsBottomTab) + { + dcMem.MoveTo(left-1, bottom); + dcMem.LineTo(right, bottom); + } + else + { + dcMem.MoveTo(left-1, top-1); + dcMem.LineTo(right, top-1); + } + + // Draw a lighter line over the darker line for the selected tab + dcMem.CreatePen(PS_SOLID, 1, RGB(248,248,248)); + TabCtrl_GetItemRect(m_hWnd, TabCtrl_GetCurSel(m_hWnd), &rcItem); + OffsetRect(&rcItem, 1, 1); + + if (IsBottomTab) + { + dcMem.MoveTo(rcItem.left, bottom); + dcMem.LineTo(rcItem.right, bottom); + } + else + { + dcMem.MoveTo(rcItem.left, top-1); + dcMem.LineTo(rcItem.right, top-1); + } + } + } + + inline CRect CTab::GetCloseRect() const + { + CRect rcClose; + if (GetShowButtons()) + { + rcClose= GetClientRect(); + int Gap = 2; + int cx = GetSystemMetrics(SM_CXSMICON) -1; + int cy = GetSystemMetrics(SM_CYSMICON) -1; + rcClose.right -= Gap; + rcClose.left = rcClose.right - cx; + + if (GetTabsAtTop()) + rcClose.top = Gap; + else + rcClose.top = MAX(Gap, rcClose.bottom - m_nTabHeight); + + rcClose.bottom = rcClose.top + cy; + } + return rcClose; + } + + inline HMENU CTab::GetListMenu() + { + if (IsMenu(m_hListMenu)) + ::DestroyMenu(m_hListMenu); + + m_hListMenu = CreatePopupMenu(); + + // Add the menu items + for(UINT u = 0; u < MIN(GetAllTabs().size(), 9); ++u) + { + TCHAR szMenuString[MAX_MENU_STRING+1]; + TCHAR szTabText[MAX_MENU_STRING]; + lstrcpyn(szTabText, GetAllTabs()[u].szTabText, MAX_MENU_STRING -4); + wsprintf(szMenuString, _T("&%d %s"), u+1, szTabText); + AppendMenu(m_hListMenu, MF_STRING, IDW_FIRSTCHILD +u, szMenuString); + } + if (GetAllTabs().size() >= 10) + AppendMenu(m_hListMenu, MF_STRING, IDW_FIRSTCHILD +9, _T("More Windows")); + + // Add a checkmark to the menu + int iSelected = GetCurSel(); + if (iSelected < 9) + CheckMenuItem(m_hListMenu, iSelected, MF_BYPOSITION|MF_CHECKED); + + return m_hListMenu; + } + + inline CRect CTab::GetListRect() const + { + CRect rcList; + if (GetShowButtons()) + { + CRect rcClose = GetCloseRect(); + rcList = rcClose; + rcList.OffsetRect( -(rcClose.Width() + 2), 0); + rcList.InflateRect(-1, 0); + } + return rcList; + } + + inline SIZE CTab::GetMaxTabSize() const + { + CSize Size; + + for (int i = 0; i < TabCtrl_GetItemCount(m_hWnd); i++) + { + CClientDC dcClient(this); + dcClient.SelectObject(&m_Font); + std::vector<TCHAR> vTitle(MAX_MENU_STRING, _T('\0')); + TCHAR* pszTitle = &vTitle.front(); + TCITEM tcItem = {0}; + tcItem.mask = TCIF_TEXT |TCIF_IMAGE; + tcItem.cchTextMax = MAX_MENU_STRING; + tcItem.pszText = pszTitle; + TabCtrl_GetItem(m_hWnd, i, &tcItem); + CSize TempSize = dcClient.GetTextExtentPoint32(pszTitle, lstrlen(pszTitle)); + + int iImageSize = 0; + int iPadding = 6; + if (tcItem.iImage >= 0) + iImageSize = 20; + TempSize.cx += iImageSize + iPadding; + + if (TempSize.cx > Size.cx) + Size = TempSize; + } + + return Size; + } + + inline BOOL CTab::GetTabsAtTop() const + // Returns TRUE if the contol's tabs are placed at the top + { + DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + return (!(dwStyle & TCS_BOTTOM)); + } + + inline int CTab::GetTextHeight() const + { + CClientDC dcClient(this); + dcClient.SelectObject(&m_Font); + CSize szText = dcClient.GetTextExtentPoint32(_T("Text"), lstrlen(_T("Text"))); + return szText.cy; + } + + inline int CTab::GetTabIndex(CWnd* pWnd) const + { + assert(pWnd); + + for (int i = 0; i < (int)m_vTabPageInfo.size(); ++i) + { + if (m_vTabPageInfo[i].pView == pWnd) + return i; + } + + return -1; + } + + inline TabPageInfo CTab::GetTabPageInfo(UINT nTab) const + { + assert (nTab < m_vTabPageInfo.size()); + + return m_vTabPageInfo[nTab]; + } + + inline void CTab::NotifyChanged() + { + NMHDR nmhdr = {0}; + nmhdr.hwndFrom = m_hWnd; + nmhdr.code = UWM_TAB_CHANGED; + GetParent()->SendMessage(WM_NOTIFY, 0L, (LPARAM)&nmhdr); + } + + inline void CTab::OnCreate() + { + SetFont(&m_Font, TRUE); + + // Assign ImageList unless we are owner drawn + if (!(GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) + TabCtrl_SetImageList(m_hWnd, m_himlTab); + + for (int i = 0; i < (int)m_vTabPageInfo.size(); ++i) + { + // Add tabs for each view. + TCITEM tie = {0}; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = m_vTabPageInfo[i].iImage; + tie.pszText = m_vTabPageInfo[i].szTabText; + TabCtrl_InsertItem(m_hWnd, i, &tie); + } + + int HeightGap = 5; + SetTabHeight(MAX(20, (GetTextHeight() + HeightGap))); + SelectPage(0); + } + + inline void CTab::OnLButtonDown(WPARAM /*wParam*/, LPARAM lParam) + { + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + + if (GetCloseRect().PtInRect(pt)) + { + m_IsClosePressed = TRUE; + SetCapture(); + CClientDC dc(this); + DrawCloseButton(dc); + } + else + m_IsClosePressed = FALSE; + + if (GetListRect().PtInRect(pt)) + { + ShowListMenu(); + } + } + + inline void CTab::OnLButtonUp(WPARAM /*wParam*/, LPARAM lParam) + { + ReleaseCapture(); + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + if (m_IsClosePressed && GetCloseRect().PtInRect(pt)) + { + RemoveTabPage(GetCurSel()); + if (GetActiveView()) + GetActiveView()->RedrawWindow(); + } + + m_IsClosePressed = FALSE; + } + + inline void CTab::OnMouseLeave(WPARAM /*wParam*/, LPARAM /*lParam*/) + { + CClientDC dc(this); + DrawCloseButton(dc); + DrawListButton(dc); + + m_IsTracking = FALSE; + } + + inline void CTab::OnMouseMove(WPARAM /*wParam*/, LPARAM /*lParam*/) + { + if (!m_IsListMenuActive && m_IsListPressed) + { + m_IsListPressed = FALSE; + } + + if (!m_IsTracking) + { + TRACKMOUSEEVENT TrackMouseEventStruct = {0}; + TrackMouseEventStruct.cbSize = sizeof(TrackMouseEventStruct); + TrackMouseEventStruct.dwFlags = TME_LEAVE; + TrackMouseEventStruct.hwndTrack = m_hWnd; + _TrackMouseEvent(&TrackMouseEventStruct); + m_IsTracking = TRUE; + } + + CClientDC dc(this); + DrawCloseButton(dc); + DrawListButton(dc); + } + + inline LRESULT CTab::OnNCHitTest(WPARAM wParam, LPARAM lParam) + { + // Ensure we have an arrow cursor when the tab has no view window + if (0 == GetAllTabs().size()) + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + // Cause WM_LBUTTONUP and WM_LBUTTONDOWN messages to be sent for buttons + CPoint pt(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + ScreenToClient(pt); + if (GetCloseRect().PtInRect(pt)) return HTCLIENT; + if (GetListRect().PtInRect(pt)) return HTCLIENT; + + return CWnd::WndProcDefault(WM_NCHITTEST, wParam, lParam); + } + + inline LRESULT CTab::OnNotifyReflect(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + case TCN_SELCHANGE: + { + // Display the newly selected tab page + int nPage = GetCurSel(); + ShowActiveView(m_vTabPageInfo[nPage].pView); + } + break; + } + + return 0L; + } + + inline void CTab::Paint() + { + // Microsoft's drawing for a tab control is rubbish, so we do our own. + // We use double buffering and regions to eliminate flicker + + // Create the memory DC and bitmap + CClientDC dcView(this); + CMemDC dcMem(&dcView); + CRect rcClient = GetClientRect(); + dcMem.CreateCompatibleBitmap(&dcView, rcClient.Width(), rcClient.Height()); + + if (0 == GetItemCount()) + { + // No tabs, so simply display a grey background and exit + COLORREF rgbDialog = GetSysColor(COLOR_BTNFACE); + dcView.SolidFill(rgbDialog, rcClient); + return; + } + + // Create a clipping region. Its the overall tab window's region, + // less the region belonging to the individual tab view's client area + CRgn rgnSrc1 = ::CreateRectRgn(rcClient.left, rcClient.top, rcClient.right, rcClient.bottom); + CRect rcTab = GetClientRect(); + TabCtrl_AdjustRect(m_hWnd, FALSE, &rcTab); + if (rcTab.Height() < 0) + rcTab.top = rcTab.bottom; + if (rcTab.Width() < 0) + rcTab.left = rcTab.right; + + CRgn rgnSrc2 = ::CreateRectRgn(rcTab.left, rcTab.top, rcTab.right, rcTab.bottom); + CRgn rgnClip = ::CreateRectRgn(0, 0, 0, 0); + ::CombineRgn(rgnClip, rgnSrc1, rgnSrc2, RGN_DIFF); + + // Use the region in the memory DC to paint the grey background + dcMem.SelectClipRgn(&rgnClip); + HWND hWndParent = ::GetParent(m_hWnd); + CDC dcParent = ::GetDC(hWndParent); + HBRUSH hBrush = (HBRUSH) SendMessage(hWndParent, WM_CTLCOLORDLG, (WPARAM)dcParent.GetHDC(), (LPARAM)hWndParent); + dcMem.SelectObject(FromHandle(hBrush)); + dcMem.PaintRgn(&rgnClip); + + // Draw the tab buttons on the memory DC: + DrawTabs(dcMem); + + // Draw buttons and tab borders + DrawCloseButton(dcMem); + DrawListButton(dcMem); + DrawTabBorders(dcMem, rcTab); + + // Now copy our from our memory DC to the window DC + dcView.SelectClipRgn(&rgnClip); + dcView.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &dcMem, 0, 0, SRCCOPY); + } + + inline void CTab::PreCreate(CREATESTRUCT &cs) + { + // For Tabs on the bottom, add the TCS_BOTTOM style + cs.style = WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_VISIBLE; + } + + inline void CTab::PreRegisterClass(WNDCLASS &wc) + { + wc.lpszClassName = WC_TABCONTROL; + } + + inline void CTab::RecalcLayout() + { + if (IsWindow()) + { + if (GetActiveView()) + { + // Set the tab sizes + SetTabSize(); + + // Position the View over the tab control's display area + CRect rc = GetClientRect(); + TabCtrl_AdjustRect(m_hWnd, FALSE, &rc); + GetActiveView()->SetWindowPos(NULL, rc, SWP_SHOWWINDOW); + } + else + RedrawWindow(); + } + } + + inline void CTab::RemoveTabPage(int nPage) + { + if ((nPage < 0) || (nPage > (int)m_vTabPageInfo.size() -1)) + return; + + // Remove the tab + TabCtrl_DeleteItem(m_hWnd, nPage); + + // Remove the TapPageInfo entry + std::vector<TabPageInfo>::iterator itTPI = m_vTabPageInfo.begin() + nPage; + CWnd* pView = (*itTPI).pView; + int iImage = (*itTPI).iImage; + if (iImage >= 0) + TabCtrl_RemoveImage(m_hWnd, iImage); + + if (pView == m_pActiveView) + m_pActiveView = 0; + + (*itTPI).pView->Destroy(); + m_vTabPageInfo.erase(itTPI); + + std::vector<WndPtr>::iterator itView; + for (itView = m_vTabViews.begin(); itView < m_vTabViews.end(); ++itView) + { + if ((*itView).get() == pView) + { + m_vTabViews.erase(itView); + break; + } + } + + if (IsWindow()) + { + if (m_vTabPageInfo.size() > 0) + { + SetTabSize(); + SelectPage(0); + } + else + ShowActiveView(NULL); + + NotifyChanged(); + } + } + + inline void CTab::SelectPage(int nPage) + { + if ((nPage >= 0) && (nPage < GetItemCount())) + { + if (nPage != GetCurSel()) + SetCurSel(nPage); + + ShowActiveView(m_vTabPageInfo[nPage].pView); + } + } + + inline void CTab::SetFixedWidth(BOOL bEnabled) + { + DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + if (bEnabled) + SetWindowLongPtr(GWL_STYLE, dwStyle | TCS_FIXEDWIDTH); + else + SetWindowLongPtr(GWL_STYLE, dwStyle & ~TCS_FIXEDWIDTH); + + RecalcLayout(); + } + + inline void CTab::SetOwnerDraw(BOOL bEnabled) + // Enable or disable owner draw + { + DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + if (bEnabled) + { + SetWindowLongPtr(GWL_STYLE, dwStyle | TCS_OWNERDRAWFIXED); + TabCtrl_SetImageList(m_hWnd, NULL); + } + else + { + SetWindowLongPtr(GWL_STYLE, dwStyle & ~TCS_OWNERDRAWFIXED); + TabCtrl_SetImageList(m_hWnd, m_himlTab); + } + + RecalcLayout(); + } + + inline void CTab::SetShowButtons(BOOL bShow) + { + m_bShowButtons = bShow; + RecalcLayout(); + } + + inline void CTab::SetTabIcon(int i, HICON hIcon) + // Changes or sets the tab's icon + { + assert (GetItemCount() > i); + TCITEM tci = {0}; + tci.mask = TCIF_IMAGE; + GetItem(i, &tci); + if (tci.iImage >= 0) + { + ImageList_ReplaceIcon(GetImageList(), i, hIcon); + } + else + { + int iImage = ImageList_AddIcon(GetImageList(), hIcon); + tci.iImage = iImage; + TabCtrl_SetItem(m_hWnd, i, &tci); + m_vTabPageInfo[i].iImage = iImage; + } + } + + inline void CTab::SetTabsAtTop(BOOL bTop) + // Positions the tabs at the top or botttom of the control + { + DWORD dwStyle = (DWORD)GetWindowLongPtr(GWL_STYLE); + + if (bTop) + dwStyle &= ~TCS_BOTTOM; + else + dwStyle |= TCS_BOTTOM; + + SetWindowLongPtr(GWL_STYLE, dwStyle); + RecalcLayout(); + } + + inline void CTab::SetTabSize() + { + if (GetItemCount() > 0) + { + CRect rc = GetClientRect(); + TabCtrl_AdjustRect(m_hWnd, FALSE, &rc); + + int xGap = 2; + if (m_bShowButtons) xGap += GetCloseRect().Width() + GetListRect().Width() +2; + + int nItemWidth = MIN( GetMaxTabSize().cx, (rc.Width() - xGap)/GetItemCount() ); + nItemWidth = MAX(nItemWidth, 0); + SendMessage(TCM_SETITEMSIZE, 0L, MAKELPARAM(nItemWidth, m_nTabHeight)); + NotifyChanged(); + } + } + + inline void CTab::SetTabText(UINT nTab, LPCTSTR szText) + { + // Allows the text to be changed on an existing tab + if (nTab < GetAllTabs().size()) + { + TCITEM Item = {0}; + std::vector<TCHAR> vTChar(MAX_MENU_STRING+1, _T('\0')); + TCHAR* pTChar = &vTChar.front(); + lstrcpyn(pTChar, szText, MAX_MENU_STRING); + Item.mask = TCIF_TEXT; + Item.pszText = pTChar; + + if (TabCtrl_SetItem(m_hWnd, nTab, &Item)) + lstrcpyn(m_vTabPageInfo[nTab].szTabText, pTChar, MAX_MENU_STRING); + } + } + + inline void CTab::ShowActiveView(CWnd* pView) + // Sets or changes the View window displayed within the tab page + { + // Hide the old view + if (GetActiveView() && (GetActiveView()->IsWindow())) + GetActiveView()->ShowWindow(SW_HIDE); + + // Assign the view window + m_pActiveView = pView; + + if (m_pActiveView && m_hWnd) + { + if (!m_pActiveView->IsWindow()) + { + // The tab control is already created, so create the new view too + GetActiveView()->Create(this); + } + + // Position the View over the tab control's display area + CRect rc = GetClientRect(); + TabCtrl_AdjustRect(m_hWnd, FALSE, &rc); + GetActiveView()->SetWindowPos(HWND_TOP, rc, SWP_SHOWWINDOW); + GetActiveView()->SetFocus(); + } + } + + inline void CTab::ShowListMenu() + // Displays the list of windows in a popup menu + { + if (!m_IsListPressed) + { + m_IsListPressed = TRUE; + HMENU hMenu = GetListMenu(); + + CPoint pt(GetListRect().left, GetListRect().top + GetTabHeight()); + ClientToScreen(pt); + + // Choosing the frame's hwnd for the menu's messages will automatically theme the popup menu + HWND MenuHwnd = GetAncestor()->GetHwnd(); + int nPage = 0; + m_IsListMenuActive = TRUE; + nPage = TrackPopupMenuEx(hMenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, pt.x, pt.y, MenuHwnd, NULL) - IDW_FIRSTCHILD; + if ((nPage >= 0) && (nPage < 9)) SelectPage(nPage); + if (nPage == 9) ShowListDialog(); + m_IsListMenuActive = FALSE; + } + + CClientDC dc(this); + DrawListButton(dc); + } + + inline void CTab::ShowListDialog() + { + // Definition of a dialog template which displays a List Box + unsigned char dlg_Template[] = + { + 0x01,0x00,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0xc8,0x00,0xc8,0x90,0x03, + 0x00,0x00,0x00,0x00,0x00,0xdc,0x00,0x8e,0x00,0x00,0x00,0x00,0x00,0x53,0x00,0x65, + 0x00,0x6c,0x00,0x65,0x00,0x63,0x00,0x74,0x00,0x20,0x00,0x57,0x00,0x69,0x00,0x6e, + 0x00,0x64,0x00,0x6f,0x00,0x77,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0x00,0x01,0x4d, + 0x00,0x53,0x00,0x20,0x00,0x53,0x00,0x68,0x00,0x65,0x00,0x6c,0x00,0x6c,0x00,0x20, + 0x00,0x44,0x00,0x6c,0x00,0x67,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x01,0x00,0x01,0x50,0x40,0x00,0x7a,0x00,0x25,0x00,0x0f,0x00,0x01, + 0x00,0x00,0x00,0xff,0xff,0x80,0x00,0x4f,0x00,0x4b,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x50,0x7a,0x00,0x7a,0x00,0x25, + 0x00,0x0f,0x00,0x02,0x00,0x00,0x00,0xff,0xff,0x80,0x00,0x43,0x00,0x61,0x00,0x6e, + 0x00,0x63,0x00,0x65,0x00,0x6c,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x02,0x00,0x01,0x01,0x21,0x50,0x06,0x00,0x06,0x00,0xcf,0x00,0x6d,0x00,0x79, + 0x00,0x00,0x00,0xff,0xff,0x83,0x00,0x00,0x00,0x00,0x00 + }; + + // Display the modal dialog. The dialog is defined in the dialog template rather + // than in the resource script (rc) file. + CSelectDialog MyDialog((LPCDLGTEMPLATE) dlg_Template); + for(UINT u = 0; u < GetAllTabs().size(); ++u) + { + MyDialog.AddItem(GetAllTabs()[u].szTabText); + } + + int iSelected = (int)MyDialog.DoModal(); + if (iSelected >= 0) SelectPage(iSelected); + } + + inline void CTab::SwapTabs(UINT nTab1, UINT nTab2) + { + if ((nTab1 < GetAllTabs().size()) && (nTab2 < GetAllTabs().size()) && (nTab1 != nTab2)) + { + int nPage = GetCurSel(); + TabPageInfo T1 = GetTabPageInfo(nTab1); + TabPageInfo T2 = GetTabPageInfo(nTab2); + + TCITEM Item1 = {0}; + Item1.mask = TCIF_IMAGE | TCIF_PARAM | TCIF_RTLREADING | TCIF_STATE | TCIF_TEXT; + GetItem(nTab1, &Item1); + TCITEM Item2 = {0}; + Item2.mask = TCIF_IMAGE | TCIF_PARAM | TCIF_RTLREADING | TCIF_STATE | TCIF_TEXT; + GetItem(nTab2, &Item2); + TabCtrl_SetItem(m_hWnd, nTab1, &Item2); + TabCtrl_SetItem(m_hWnd, nTab2, &Item1); + + m_vTabPageInfo[nTab1] = T2; + m_vTabPageInfo[nTab2] = T1; + SelectPage(nPage); + } + } + + inline LRESULT CTab::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch(uMsg) + { + case WM_PAINT: + if (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED) + { + // Remove all pending paint requests + PAINTSTRUCT ps; + BeginPaint(ps); + EndPaint(ps); + + // Now call our local Paint + Paint(); + return 0; + } + break; + + case WM_ERASEBKGND: + if (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED) + return 0; + break; + case WM_KILLFOCUS: + m_IsClosePressed = FALSE; + break; + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + OnLButtonDown(wParam, lParam); + break; + case WM_LBUTTONUP: + OnLButtonUp(wParam, lParam); + break; + case WM_MOUSEMOVE: + OnMouseMove(wParam, lParam); + break; + case WM_MOUSELEAVE: + OnMouseLeave(wParam, lParam); + break; + case WM_NCHITTEST: + return OnNCHitTest(wParam, lParam); + + case WM_WINDOWPOSCHANGING: + // A little hack to reduce tab flicker + if (IsWindowVisible() && (GetWindowLongPtr(GWL_STYLE) & TCS_OWNERDRAWFIXED)) + { + LPWINDOWPOS pWinPos = (LPWINDOWPOS)lParam; + pWinPos->flags |= SWP_NOREDRAW; + + Paint(); + } + + break; + + case WM_WINDOWPOSCHANGED: + RecalcLayout(); + break; + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + // Wrappers for Win32 Macros + inline void CTab::AdjustRect(BOOL fLarger, RECT *prc) const + { + assert(::IsWindow(m_hWnd)); + TabCtrl_AdjustRect(m_hWnd, fLarger, prc); + } + + inline int CTab::GetCurFocus() const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_GetCurFocus(m_hWnd); + } + + inline int CTab::GetCurSel() const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_GetCurSel(m_hWnd); + } + + inline BOOL CTab::GetItem(int iItem, LPTCITEM pitem) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_GetItem(m_hWnd, iItem, pitem); + } + + inline int CTab::GetItemCount() const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_GetItemCount(m_hWnd); + } + + inline int CTab::HitTest(TCHITTESTINFO& info) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_HitTest(m_hWnd, &info); + } + + inline void CTab::SetCurFocus(int iItem) const + { + assert(::IsWindow(m_hWnd)); + TabCtrl_SetCurFocus(m_hWnd, iItem); + } + + inline int CTab::SetCurSel(int iItem) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_SetCurSel(m_hWnd, iItem); + } + + inline DWORD CTab::SetItemSize(int cx, int cy) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_SetItemSize(m_hWnd, cx, cy); + } + + inline int CTab::SetMinTabWidth(int cx) const + { + assert(::IsWindow(m_hWnd)); + return TabCtrl_SetMinTabWidth(m_hWnd, cx); + } + + inline void CTab::SetPadding(int cx, int cy) const + { + assert(::IsWindow(m_hWnd)); + TabCtrl_SetPadding(m_hWnd, cx, cy); + } + + //////////////////////////////////////// + // Definitions for the CTabbedMDI class + inline CTabbedMDI::CTabbedMDI() + { + GetTab().SetShowButtons(TRUE); + } + + inline CTabbedMDI::~CTabbedMDI() + { + } + + inline CWnd* CTabbedMDI::AddMDIChild(CWnd* pView, LPCTSTR szTabText, int idMDIChild /*= 0*/) + { + assert(pView); + assert(lstrlen(szTabText) < MAX_MENU_STRING); + + GetTab().AddTabPage(WndPtr(pView), szTabText, 0, idMDIChild); + + // Fake a WM_MOUSEACTIVATE to propogate focus change to dockers + if (IsWindow()) + GetParent()->SendMessage(WM_MOUSEACTIVATE, (WPARAM)GetAncestor(), MAKELPARAM(HTCLIENT,WM_LBUTTONDOWN)); + + return pView; + } + + inline void CTabbedMDI::CloseActiveMDI() + { + int nTab = GetTab().GetCurSel(); + if (nTab >= 0) + GetTab().RemoveTabPage(nTab); + + RecalcLayout(); + } + + inline void CTabbedMDI::CloseAllMDIChildren() + { + while (GetMDIChildCount() > 0) + { + GetTab().RemoveTabPage(0); + } + } + + inline void CTabbedMDI::CloseMDIChild(int nTab) + { + GetTab().RemoveTabPage(nTab); + + if (GetActiveMDIChild()) + GetActiveMDIChild()->RedrawWindow(); + } + + inline HWND CTabbedMDI::Create(CWnd* pParent /* = NULL*/) + { + CLIENTCREATESTRUCT clientcreate ; + clientcreate.hWindowMenu = m_hWnd; + clientcreate.idFirstChild = IDW_FIRSTCHILD ; + DWORD dwStyle = WS_CHILD | WS_VISIBLE | MDIS_ALLCHILDSTYLES; + + // Create the MDICLIENT view window + if (!CreateEx(0, _T("MDICLIENT"), _T(""), + dwStyle, 0, 0, 0, 0, pParent, NULL, (PSTR) &clientcreate)) + throw CWinException(_T("CMDIClient::Create ... CreateEx failed")); + + return m_hWnd; + } + + inline CWnd* CTabbedMDI::GetActiveMDIChild() const + { + CWnd* pView = NULL; + int nTab = GetTab().GetCurSel(); + if (nTab >= 0) + { + TabPageInfo tbi = GetTab().GetTabPageInfo(nTab); + pView = tbi.pView; + } + + return pView; + } + + inline int CTabbedMDI::GetActiveMDITab() const + { + return GetTab().GetCurSel(); + } + + inline CWnd* CTabbedMDI::GetMDIChild(int nTab) const + { + assert(nTab >= 0); + assert(nTab < GetMDIChildCount()); + return GetTab().GetTabPageInfo(nTab).pView; + } + + inline int CTabbedMDI::GetMDIChildCount() const + { + return (int) GetTab().GetAllTabs().size(); + } + + inline int CTabbedMDI::GetMDIChildID(int nTab) const + { + assert(nTab >= 0); + assert(nTab < GetMDIChildCount()); + return GetTab().GetTabPageInfo(nTab).idTab; + } + + inline LPCTSTR CTabbedMDI::GetMDIChildTitle(int nTab) const + { + assert(nTab >= 0); + assert(nTab < GetMDIChildCount()); + return GetTab().GetTabPageInfo(nTab).szTabText; + } + + inline BOOL CTabbedMDI::LoadRegistrySettings(tString tsRegistryKeyName) + { + BOOL bResult = FALSE; + + if (0 != tsRegistryKeyName.size()) + { + tString tsKey = _T("Software\\") + tsRegistryKeyName + _T("\\MDI Children"); + HKEY hKey = 0; + RegOpenKeyEx(HKEY_CURRENT_USER, tsKey.c_str(), 0, KEY_READ, &hKey); + if (hKey) + { + DWORD dwType = REG_BINARY; + DWORD BufferSize = sizeof(TabPageInfo); + TabPageInfo tbi = {0}; + int i = 0; + TCHAR szNumber[16]; + tString tsSubKey = _T("MDI Child "); + tsSubKey += _itot(i, szNumber, 10); + + // Fill the DockList vector from the registry + while (0 == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&tbi, &BufferSize)) + { + CWnd* pWnd = NewMDIChildFromID(tbi.idTab); + if (pWnd) + { + AddMDIChild(pWnd, tbi.szTabText, tbi.idTab); + i++; + tsSubKey = _T("MDI Child "); + tsSubKey += _itot(i, szNumber, 10); + bResult = TRUE; + } + else + { + TRACE(_T("Failed to get TabbedMDI info from registry")); + bResult = FALSE; + break; + } + } + + // Load Active MDI Tab from the registry + tsSubKey = _T("Active MDI Tab"); + int nTab; + dwType = REG_DWORD; + BufferSize = sizeof(int); + if(ERROR_SUCCESS == RegQueryValueEx(hKey, tsSubKey.c_str(), NULL, &dwType, (LPBYTE)&nTab, &BufferSize)) + SetActiveMDITab(nTab); + else + SetActiveMDITab(0); + + RegCloseKey(hKey); + } + } + + if (!bResult) + CloseAllMDIChildren(); + + return bResult; + } + + inline CWnd* CTabbedMDI::NewMDIChildFromID(int /*idMDIChild*/) + { + // Override this function to create new MDI children from IDs as shown below + CWnd* pView = NULL; + /* switch(idTab) + { + case ID_SIMPLE: + pView = new CViewSimple; + break; + case ID_RECT: + pView = new CViewRect; + break; + default: + TRACE(_T("Unknown MDI child ID\n")); + break; + } */ + + return pView; + } + + inline void CTabbedMDI::OnCreate() + { + GetTab().Create(this); + GetTab().SetFixedWidth(TRUE); + GetTab().SetOwnerDraw(TRUE); + } + + inline void CTabbedMDI::OnDestroy(WPARAM /*wParam*/, LPARAM /*lParam*/ ) + { + CloseAllMDIChildren(); + } + + inline LRESULT CTabbedMDI::OnNotify(WPARAM /*wParam*/, LPARAM lParam) + { + LPNMHDR pnmhdr = (LPNMHDR)lParam; + if (pnmhdr->code == UWM_TAB_CHANGED) + RecalcLayout(); + + return 0L; + } + + inline void CTabbedMDI::OnWindowPosChanged(WPARAM /*wParam*/, LPARAM /*lParam*/) + { + RecalcLayout(); + } + + inline void CTabbedMDI::RecalcLayout() + { + if (GetTab().IsWindow()) + { + if (GetTab().GetItemCount() >0) + { + CRect rcClient = GetClientRect(); + GetTab().SetWindowPos(NULL, rcClient, SWP_SHOWWINDOW); + GetTab().UpdateWindow(); + } + else + { + CRect rcClient = GetClientRect(); + GetTab().SetWindowPos(NULL, rcClient, SWP_HIDEWINDOW); + Invalidate(); + } + } + } + + inline BOOL CTabbedMDI::SaveRegistrySettings(tString tsRegistryKeyName) + { + if (0 != tsRegistryKeyName.size()) + { + tString tsKeyName = _T("Software\\") + tsRegistryKeyName; + HKEY hKey = NULL; + HKEY hKeyMDIChild = NULL; + + try + { + if (ERROR_SUCCESS != RegCreateKeyEx(HKEY_CURRENT_USER, tsKeyName.c_str(), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, NULL)) + throw (CWinException(_T("RegCreateKeyEx Failed"))); + + RegDeleteKey(hKey, _T("MDI Children")); + if (ERROR_SUCCESS != RegCreateKeyEx(hKey, _T("MDI Children"), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKeyMDIChild, NULL)) + throw (CWinException(_T("RegCreateKeyEx Failed"))); + + for (int i = 0; i < GetMDIChildCount(); ++i) + { + TCHAR szNumber[16]; + tString tsSubKey = _T("MDI Child "); + tsSubKey += _itot(i, szNumber, 10); + TabPageInfo pdi = GetTab().GetTabPageInfo(i); + if (ERROR_SUCCESS != RegSetValueEx(hKeyMDIChild, tsSubKey.c_str(), 0, REG_BINARY, (LPBYTE)&pdi, sizeof(TabPageInfo))) + throw (CWinException(_T("RegSetValueEx Failed"))); + } + + // Add Active Tab to the registry + tString tsSubKey = _T("Active MDI Tab"); + int nTab = GetActiveMDITab(); + if(ERROR_SUCCESS != RegSetValueEx(hKeyMDIChild, tsSubKey.c_str(), 0, REG_DWORD, (LPBYTE)&nTab, sizeof(int))) + throw (CWinException(_T("RegSetValueEx failed"))); + + RegCloseKey(hKeyMDIChild); + RegCloseKey(hKey); + } + catch (const CWinException& e) + { + // Roll back the registry changes by deleting the subkeys + if (hKey) + { + if (hKeyMDIChild) + { + RegDeleteKey(hKeyMDIChild, _T("MDI Children")); + RegCloseKey(hKeyMDIChild); + } + + RegDeleteKey(HKEY_CURRENT_USER ,tsKeyName.c_str()); + RegCloseKey(hKey); + } + + e.what(); + return FALSE; + } + } + + return TRUE; + } + + inline void CTabbedMDI::SetActiveMDIChild(CWnd* pWnd) + { + assert(pWnd); + int nPage = GetTab().GetTabIndex(pWnd); + if (nPage >= 0) + GetTab().SelectPage(nPage); + } + + inline void CTabbedMDI::SetActiveMDITab(int iTab) + { + assert(::IsWindow(m_hWnd)); + assert(GetTab().IsWindow()); + GetTab().SelectPage(iTab); + } + + inline LRESULT CTabbedMDI::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch(uMsg) + { + case WM_DESTROY: + OnDestroy(wParam, lParam); + break; + + case WM_WINDOWPOSCHANGED: + OnWindowPosChanged(wParam, lParam); + break; + } + + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + +} // namespace Win32xx + +#endif // _WIN32XX_TAB_H_ diff --git a/mmc_updater/depends/win32cpp/taskdialog.h b/mmc_updater/depends/win32cpp/taskdialog.h new file mode 100644 index 00000000..2285d1e1 --- /dev/null +++ b/mmc_updater/depends/win32cpp/taskdialog.h @@ -0,0 +1,811 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////// +// taskdialog.h +// Declaration of the CTaskDialog class + +// A task dialog is a dialog box that can be used to display information +// and receive simple input from the user. Like a message box, it is +// formatted by the operating system according to parameters you set. +// However, a task dialog has many more features than a message box. + +// NOTES: +// Task Dialogs are only supported on Windows Vista and above. +// Task Dialogs require XP themes enabled (use version 6 of Common Controls) +// Task Dialogs are always modal. + + +#ifndef _WIN32XX_TASKDIALOG_H_ +#define _WIN32XX_TASKDIALOG_H_ + +#include "wincore.h" + +namespace Win32xx +{ + + class CTaskDialog : public CWnd + { + public: + CTaskDialog(); + virtual ~CTaskDialog() {} + + void AddCommandControl(int nButtonID, LPCTSTR pszCaption); + void AddRadioButton(int nRadioButtonID, LPCTSTR pszCaption); + void AddRadioButtonGroup(int nIDRadioButtonsFirst, int nIDRadioButtonsLast); + void ClickButton(int nButtonID) const; + void ClickRadioButton(int nRadioButtonID) const; + LRESULT DoModal(CWnd* pParent = NULL); + void ElevateButton(int nButtonID, BOOL bElevated); + void EnableButton(int nButtonID, BOOL bEnabled); + void EnableRadioButton(int nButtonID, BOOL bEnabled); + TASKDIALOGCONFIG GetConfig() const; + TASKDIALOG_FLAGS GetOptions() const; + int GetSelectedButtonID() const; + int GetSelectedRadioButtonID() const; + BOOL GetVerificationCheckboxState() const; + static BOOL IsSupported(); + void NavigateTo(CTaskDialog& TaskDialog) const; + void RemoveAllButtons(); + void RemoveAllRadioButtons(); + void Reset(); + void SetCommonButtons(TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons); + void SetContent(LPCTSTR pszContent); + void SetDefaultButton(int nButtonID); + void SetDefaultRadioButton(int nRadioButtonID); + void SetDialogWidth(UINT nWidth = 0); + void SetExpansionArea(LPCTSTR pszExpandedInfo, LPCTSTR pszExpandedLabel = _T(""), LPCTSTR pszCollapsedLabel = _T("")); + void SetFooterIcon(HICON hFooterIcon); + void SetFooterIcon(LPCTSTR lpszFooterIcon); + void SetFooterText(LPCTSTR pszFooter); + void SetMainIcon(HICON hMainIcon); + void SetMainIcon(LPCTSTR lpszMainIcon); + void SetMainInstruction(LPCTSTR pszMainInstruction); + void SetOptions(TASKDIALOG_FLAGS dwFlags); + void SetProgressBarMarquee(BOOL bEnabled = TRUE, int nMarqueeSpeed = 0); + void SetProgressBarPosition(int nProgressPos); + void SetProgressBarRange(int nMinRange, int nMaxRange); + void SetProgressBarState(int nNewState = PBST_NORMAL); + void SetVerificationCheckbox(BOOL bChecked); + void SetVerificationCheckboxText(LPCTSTR pszVerificationText); + void SetWindowTitle(LPCTSTR pszWindowTitle); + static HRESULT CALLBACK StaticTaskDialogProc(HWND hWnd, UINT uNotification, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData); + void StoreText(std::vector<WCHAR>& vWChar, LPCTSTR pFromTChar); + void UpdateElementText(TASKDIALOG_ELEMENTS eElement, LPCTSTR pszNewText); + + + protected: + // Override these functions as required + virtual BOOL OnTDButtonClicked(int nButtonID); + virtual void OnTDConstructed(); + virtual void OnTDCreated(); + virtual void OnTDDestroyed(); + virtual void OnTDExpandButtonClicked(BOOL bExpanded); + virtual void OnTDHelp(); + virtual void OnTDHyperlinkClicked(LPCTSTR pszHref); + virtual void OnTDNavigatePage(); + virtual BOOL OnTDRadioButtonClicked(int nRadioButtonID); + virtual BOOL OnTDTimer(DWORD dwTickCount); + virtual void OnTDVerificationCheckboxClicked(BOOL bChecked); + virtual LRESULT TaskDialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual LRESULT TaskDialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CTaskDialog(const CTaskDialog&); // Disable copy construction + CTaskDialog& operator = (const CTaskDialog&); // Disable assignment operator + + std::vector<TASKDIALOG_BUTTON> m_vButtons; + std::vector<TASKDIALOG_BUTTON> m_vRadioButtons; + + std::vector< std::vector<WCHAR> > m_vButtonsText; // A vector of WCHAR vectors + std::vector< std::vector<WCHAR> > m_vRadioButtonsText; // A vector of WCHAR vectors + + std::vector<WCHAR> m_vWindowTitle; + std::vector<WCHAR> m_vMainInstruction; + std::vector<WCHAR> m_vContent; + std::vector<WCHAR> m_vVerificationText; + std::vector<WCHAR> m_vExpandedInformation; + std::vector<WCHAR> m_vExpandedControlText; + std::vector<WCHAR> m_vCollapsedControlText; + std::vector<WCHAR> m_vFooter; + + TASKDIALOGCONFIG m_tc; + int m_SelectedButtonID; + int m_SelectedRadioButtonID; + BOOL m_VerificationCheckboxState; + }; + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + inline CTaskDialog::CTaskDialog() : m_SelectedButtonID(0), m_SelectedRadioButtonID(0), m_VerificationCheckboxState(FALSE) + { + ZeroMemory(&m_tc, sizeof(m_tc)); + m_tc.cbSize = sizeof(m_tc); + m_tc.pfCallback = CTaskDialog::StaticTaskDialogProc; + } + + inline void CTaskDialog::AddCommandControl(int nButtonID, LPCTSTR pszCaption) + // Adds a command control or push button to the Task Dialog. + { + assert (m_hWnd == NULL); + + std::vector<WCHAR> vButtonText; + StoreText(vButtonText, pszCaption); + m_vButtonsText.push_back(vButtonText); // m_vButtonsText is a vector of vector<WCHAR>'s + + TASKDIALOG_BUTTON tdb; + tdb.nButtonID = nButtonID; + tdb.pszButtonText = &m_vButtonsText.back().front(); + + m_vButtons.push_back(tdb); + } + + inline void CTaskDialog::AddRadioButton(int nRadioButtonID, LPCTSTR pszCaption) + // Adds a radio button to the Task Dialog. + { + assert (m_hWnd == NULL); + + std::vector<WCHAR> vRadioButtonText; + StoreText(vRadioButtonText, pszCaption); + m_vRadioButtonsText.push_back(vRadioButtonText); // m_vRadioButtonsText is a vector of vector<WCHAR>'s + + TASKDIALOG_BUTTON tdb; + tdb.nButtonID = nRadioButtonID; + tdb.pszButtonText = &m_vRadioButtonsText.back().front(); + + m_vRadioButtons.push_back(tdb); + } + + inline void CTaskDialog::AddRadioButtonGroup(int nIDRadioButtonsFirst, int nIDRadioButtonsLast) + // Adds a range of radio buttons to the Task Dialog. + // Assumes the resource ID of the button and it's string match + { + assert (m_hWnd == NULL); + assert(nIDRadioButtonsFirst > 0); + assert(nIDRadioButtonsLast > nIDRadioButtonsFirst); + + TASKDIALOG_BUTTON tdb; + for (int nID = nIDRadioButtonsFirst; nID <= nIDRadioButtonsLast; ++nID) + { + tdb.nButtonID = nID; + tdb.pszButtonText = MAKEINTRESOURCEW(nID); + m_vRadioButtons.push_back(tdb); + } + } + + inline void CTaskDialog::ClickButton(int nButtonID) const + // Simulates the action of a button click in the Task Dialog. + { + assert(m_hWnd); + SendMessage(TDM_CLICK_BUTTON, (WPARAM)nButtonID, 0); + } + + inline void CTaskDialog::ClickRadioButton(int nRadioButtonID) const + // Simulates the action of a radio button click in the TaskDialog. + { + assert(m_hWnd); + SendMessage(TDM_CLICK_RADIO_BUTTON, (WPARAM)nRadioButtonID, 0); + } + + inline LRESULT CTaskDialog::DoModal(CWnd* pParent /* = NULL */) + // Creates and displays the Task Dialog. + { + assert (m_hWnd == NULL); + + m_tc.cbSize = sizeof(m_tc); + m_tc.pButtons = m_vButtons.empty()? NULL : &m_vButtons.front(); + m_tc.cButtons = m_vButtons.size(); + m_tc.pRadioButtons = m_vRadioButtons.empty()? NULL : &m_vRadioButtons.front(); + m_tc.cRadioButtons = m_vRadioButtons.size(); + m_tc.hwndParent = pParent? pParent->GetHwnd() : NULL; + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + + // Store the CWnd pointer in thread local storage + pTLSData->pCWnd = this; + + // Declare a pointer to the TaskDialogIndirect function + HMODULE hComCtl = ::LoadLibrary(_T("COMCTL32.DLL")); + assert(hComCtl); + typedef HRESULT WINAPI TASKDIALOGINDIRECT(const TASKDIALOGCONFIG*, int*, int*, BOOL*); + TASKDIALOGINDIRECT* pTaskDialogIndirect = (TASKDIALOGINDIRECT*)::GetProcAddress(hComCtl, "TaskDialogIndirect"); + + // Call TaskDialogIndirect through our function pointer + LRESULT lr = (*pTaskDialogIndirect)(&m_tc, &m_SelectedButtonID, &m_SelectedRadioButtonID, &m_VerificationCheckboxState); + + FreeLibrary(hComCtl); + return lr; + } + + inline void CTaskDialog::ElevateButton(int nButtonID, BOOL bElevated) + // Adds a shield icon to indicate that the button's action requires elevated privilages. + { + assert(m_hWnd); + SendMessage(TDM_SET_BUTTON_ELEVATION_REQUIRED_STATE, (WPARAM)nButtonID, (LPARAM)bElevated); + } + + inline void CTaskDialog::EnableButton(int nButtonID, BOOL bEnabled) + // Enables or disables a push button in the TaskDialog. + { + assert(m_hWnd); + SendMessage(TDM_ENABLE_BUTTON, (WPARAM)nButtonID, (LPARAM)bEnabled); + } + inline void CTaskDialog::EnableRadioButton(int nRadioButtonID, BOOL bEnabled) + // Enables or disables a radio button in the TaskDialog. + { + assert(m_hWnd); + SendMessage(TDM_ENABLE_RADIO_BUTTON, (WPARAM)nRadioButtonID, (LPARAM)bEnabled); + } + + inline TASKDIALOGCONFIG CTaskDialog::GetConfig() const + // Returns the TASKDIALOGCONFIG structure for the Task Dialog. + { + return m_tc; + } + + inline TASKDIALOG_FLAGS CTaskDialog::GetOptions() const + // Returns the Task Dialog's options. These are a combination of: + // TDF_ENABLE_HYPERLINKS + // TDF_USE_HICON_MAIN + // TDF_USE_HICON_FOOTER + // TDF_ALLOW_DIALOG_CANCELLATION + // TDF_USE_COMMAND_LINKS + // TDF_USE_COMMAND_LINKS_NO_ICON + // TDF_EXPAND_FOOTER_AREA + // TDF_EXPANDED_BY_DEFAULT + // TDF_VERIFICATION_FLAG_CHECKED + // TDF_SHOW_PROGRESS_BAR + // TDF_SHOW_MARQUEE_PROGRESS_BAR + // TDF_CALLBACK_TIMER + // TDF_POSITION_RELATIVE_TO_WINDOW + // TDF_RTL_LAYOUT + // TDF_NO_DEFAULT_RADIO_BUTTON + // TDF_CAN_BE_MINIMIZED + { + return m_tc.dwFlags; + } + + inline int CTaskDialog::GetSelectedButtonID() const + // Returns the ID of the selected button. + { + assert (m_hWnd == NULL); + return m_SelectedButtonID; + } + + inline int CTaskDialog::GetSelectedRadioButtonID() const + // Returns the ID of the selected radio button. + { + assert (m_hWnd == NULL); + return m_SelectedRadioButtonID; + } + + inline BOOL CTaskDialog::GetVerificationCheckboxState() const + // Returns the state of the verification check box. + { + assert (m_hWnd == NULL); + return m_VerificationCheckboxState; + } + + inline BOOL CTaskDialog::IsSupported() + // Returns true if TaskDialogs are supported on this system. + { + HMODULE hModule = ::LoadLibrary(_T("COMCTL32.DLL")); + assert(hModule); + + BOOL bResult = (BOOL)::GetProcAddress(hModule, "TaskDialogIndirect"); + + ::FreeLibrary(hModule); + return bResult; + } + + inline void CTaskDialog::NavigateTo(CTaskDialog& TaskDialog) const + // Replaces the information displayed by the task dialog. + { + assert(m_hWnd); + TASKDIALOGCONFIG tc = TaskDialog.GetConfig(); + SendMessage(TDM_NAVIGATE_PAGE, 0, (LPARAM)&tc); + } + + inline BOOL CTaskDialog::OnTDButtonClicked(int nButtonID) + // Called when the user selects a button or command link. + { + UNREFERENCED_PARAMETER(nButtonID); + + // return TRUE to prevent the task dialog from closing + return FALSE; + } + + inline void CTaskDialog::OnTDConstructed() + // Called when the task dialog is constructed, before it is displayed. + {} + + inline void CTaskDialog::OnTDCreated() + // Called when the task dialog is displayed. + {} + + inline void CTaskDialog::OnTDDestroyed() + // Called when the task dialog is destroyed. + { + } + + inline void CTaskDialog::OnTDExpandButtonClicked(BOOL bExpanded) + // Called when the expand button is clicked. + { + UNREFERENCED_PARAMETER(bExpanded); + } + + inline void CTaskDialog::OnTDHelp() + // Called when the user presses F1 on the keyboard. + {} + + inline void CTaskDialog::OnTDHyperlinkClicked(LPCTSTR pszHref) + // Called when the user clicks on a hyperlink. + { + UNREFERENCED_PARAMETER(pszHref); + } + + inline void CTaskDialog::OnTDNavigatePage() + // Called when a navigation has occurred. + {} + + inline BOOL CTaskDialog::OnTDRadioButtonClicked(int nRadioButtonID) + // Called when the user selects a radio button. + { + UNREFERENCED_PARAMETER(nRadioButtonID); + return TRUE; + } + + inline BOOL CTaskDialog::OnTDTimer(DWORD dwTickCount) + // Called every 200 milliseconds (aproximately) when the TDF_CALLBACK_TIMER flag is set. + { + UNREFERENCED_PARAMETER(dwTickCount); + + // return TRUE to reset the tick count + return FALSE; + } + + inline void CTaskDialog::OnTDVerificationCheckboxClicked(BOOL bChecked) + // Called when the user clicks the Task Dialog verification check box. + { + UNREFERENCED_PARAMETER(bChecked); + } + + inline void CTaskDialog::RemoveAllButtons() + // Removes all push buttons from the task dialog. + { + assert (m_hWnd == NULL); + m_vButtons.clear(); + m_vButtonsText.clear(); + } + + inline void CTaskDialog::RemoveAllRadioButtons() + // Removes all radio buttons from the task dialog. + { + assert (m_hWnd == NULL); + m_vRadioButtons.clear(); + m_vRadioButtonsText.clear(); + } + + inline void CTaskDialog::Reset() + // Returns the dialog to its default state. + { + assert (m_hWnd == NULL); + + RemoveAllButtons(); + RemoveAllRadioButtons(); + ZeroMemory(&m_tc, sizeof(m_tc)); + m_tc.cbSize = sizeof(m_tc); + m_tc.pfCallback = CTaskDialog::StaticTaskDialogProc; + + m_SelectedButtonID = 0; + m_SelectedRadioButtonID = 0; + m_VerificationCheckboxState = FALSE; + + m_vWindowTitle.clear(); + m_vMainInstruction.clear(); + m_vContent.clear(); + m_vVerificationText.clear(); + m_vExpandedInformation.clear(); + m_vExpandedControlText.clear(); + m_vCollapsedControlText.clear(); + m_vFooter.clear(); + } + + inline void CTaskDialog::SetCommonButtons(TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons) + // The dwCommonButtons parameter can be a combination of: + // TDCBF_OK_BUTTON OK button + // TDCBF_YES_BUTTON Yes button + // TDCBF_NO_BUTTON No button + // TDCBF_CANCEL_BUTTON Cancel button + // TDCBF_RETRY_BUTTON Retry button + // TDCBF_CLOSE_BUTTON Close button + { + assert (m_hWnd == NULL); + m_tc.dwCommonButtons = dwCommonButtons; + } + + inline void CTaskDialog::SetContent(LPCTSTR pszContent) + // Sets the task dialog's primary content. + { + StoreText(m_vContent, pszContent); + m_tc.pszContent = &m_vContent.front(); + + if (IsWindow()) + SendMessage(TDM_SET_ELEMENT_TEXT, (WPARAM)TDE_CONTENT, (LPARAM)(LPCWSTR)T2W(pszContent)); + } + + inline void CTaskDialog::SetDefaultButton(int nButtonID) + // Sets the task dialog's default button. + // Can be either a button ID or one of the common buttons + { + assert (m_hWnd == NULL); + m_tc.nDefaultButton = nButtonID; + } + + inline void CTaskDialog::SetDefaultRadioButton(int nRadioButtonID) + // Sets the default radio button. + { + assert (m_hWnd == NULL); + m_tc.nDefaultRadioButton = nRadioButtonID; + } + + inline void CTaskDialog::SetDialogWidth(UINT nWidth /*= 0*/) + // The width of the task dialog's client area. If 0, the + // task dialog manager will calculate the ideal width. + { + assert (m_hWnd == NULL); + m_tc.cxWidth = nWidth; + } + + inline void CTaskDialog::SetExpansionArea(LPCTSTR pszExpandedInfo, LPCTSTR pszExpandedLabel /* = _T("")*/, LPCTSTR pszCollapsedLabel /* = _T("")*/) + // Sets the text in the expandable area of the Task Dialog. + { + StoreText(m_vExpandedInformation, pszExpandedInfo); + m_tc.pszExpandedInformation = &m_vExpandedInformation.front(); + + StoreText(m_vExpandedControlText, pszExpandedLabel); + m_tc.pszExpandedControlText = &m_vExpandedControlText.front(); + + StoreText(m_vCollapsedControlText, pszCollapsedLabel); + m_tc.pszCollapsedControlText = &m_vCollapsedControlText.front(); + + if (IsWindow()) + SendMessage(TDM_SET_ELEMENT_TEXT, (WPARAM)TDE_EXPANDED_INFORMATION, (LPARAM)(LPCWSTR)T2W(pszExpandedInfo)); + } + + inline void CTaskDialog::SetFooterIcon(HICON hFooterIcon) + // Sets the icon that will be displayed in the Task Dialog's footer. + { + m_tc.hFooterIcon = hFooterIcon; + + if (IsWindow()) + SendMessage(TDM_UPDATE_ICON, (WPARAM)TDIE_ICON_FOOTER, (LPARAM)hFooterIcon); + } + + inline void CTaskDialog::SetFooterIcon(LPCTSTR lpszFooterIcon) + // Sets the icon that will be displayed in the Task Dialog's footer. + // Possible icons: + // TD_ERROR_ICON A stop-sign icon appears in the task dialog. + // TD_WARNING_ICON An exclamation-point icon appears in the task dialog. + // TD_INFORMATION_ICON An icon consisting of a lowercase letter i in a circle appears in the task dialog. + // TD_SHIELD_ICON A shield icon appears in the task dialog. + // or a value passed via MAKEINTRESOURCE + { + m_tc.pszFooterIcon = (LPCWSTR)lpszFooterIcon; + + if (IsWindow()) + SendMessage(TDM_UPDATE_ICON, (WPARAM)TDIE_ICON_FOOTER, (LPARAM)lpszFooterIcon); + } + + inline void CTaskDialog::SetFooterText(LPCTSTR pszFooter) + // Sets the text that will be displayed in the Task Dialog's footer. + { + StoreText(m_vFooter, pszFooter); + m_tc.pszFooter = &m_vFooter.front(); + + if (IsWindow()) + SendMessage(TDM_SET_ELEMENT_TEXT, (WPARAM)TDE_FOOTER, (LPARAM)(LPCWSTR)T2W(pszFooter)); + } + + inline void CTaskDialog::SetMainIcon(HICON hMainIcon) + // Sets Task Dialog's main icon. + { + m_tc.hMainIcon = hMainIcon; + + if (IsWindow()) + SendMessage(TDM_UPDATE_ICON, (WPARAM)TDIE_ICON_MAIN, (LPARAM)hMainIcon); + } + + inline void CTaskDialog::SetMainIcon(LPCTSTR lpszMainIcon) + // Sets Task Dialog's main icon. + // Possible icons: + // TD_ERROR_ICON A stop-sign icon appears in the task dialog. + // TD_WARNING_ICON An exclamation-point icon appears in the task dialog. + // TD_INFORMATION_ICON An icon consisting of a lowercase letter i in a circle appears in the task dialog. + // TD_SHIELD_ICON A shield icon appears in the task dialog. + // or a value passed via MAKEINTRESOURCE + // + // Note: Some values of main icon will also generate a MessageBeep when the TaskDialog is created. + { + m_tc.pszMainIcon = (LPCWSTR)lpszMainIcon; + + if (IsWindow()) + SendMessage(TDM_UPDATE_ICON, (WPARAM)TDIE_ICON_MAIN, (LPARAM)lpszMainIcon); + } + + inline void CTaskDialog::SetMainInstruction(LPCTSTR pszMainInstruction) + // Sets the Task Dialog's main instruction text. + { + StoreText(m_vMainInstruction, pszMainInstruction); + m_tc.pszMainInstruction = &m_vMainInstruction.front(); + + if (IsWindow()) + SendMessage(TDM_SET_ELEMENT_TEXT, (WPARAM)TDE_FOOTER, (LPARAM)(LPCWSTR)T2W(pszMainInstruction)); + } + + inline void CTaskDialog::SetOptions(TASKDIALOG_FLAGS dwFlags) + // Sets the Task Dialog's options. These are a combination of: + // TDF_ENABLE_HYPERLINKS + // TDF_USE_HICON_MAIN + // TDF_USE_HICON_FOOTER + // TDF_ALLOW_DIALOG_CANCELLATION + // TDF_USE_COMMAND_LINKS + // TDF_USE_COMMAND_LINKS_NO_ICON + // TDF_EXPAND_FOOTER_AREA + // TDF_EXPANDED_BY_DEFAULT + // TDF_VERIFICATION_FLAG_CHECKED + // TDF_SHOW_PROGRESS_BAR + // TDF_SHOW_MARQUEE_PROGRESS_BAR + // TDF_CALLBACK_TIMER + // TDF_POSITION_RELATIVE_TO_WINDOW + // TDF_RTL_LAYOUT + // TDF_NO_DEFAULT_RADIO_BUTTON + // TDF_CAN_BE_MINIMIZED + { + assert (m_hWnd == NULL); + m_tc.dwFlags = dwFlags; + } + + inline void CTaskDialog::SetProgressBarMarquee(BOOL bEnabled /* = TRUE*/, int nMarqueeSpeed /* = 0*/) + // Starts and stops the marquee display of the progress bar, and sets the speed of the marquee. + { + assert(m_hWnd); + SendMessage(TDM_SET_PROGRESS_BAR_MARQUEE, (WPARAM)bEnabled, (LPARAM)nMarqueeSpeed); + } + + inline void CTaskDialog::SetProgressBarPosition(int nProgressPos) + // Sets the current position for a progress bar. + { + assert(m_hWnd); + SendMessage(TDM_SET_PROGRESS_BAR_POS, (WPARAM)nProgressPos, 0); + } + + inline void CTaskDialog::SetProgressBarRange(int nMinRange, int nMaxRange) + // Sets the minimum and maximum values for the hosted progress bar. + { + assert(m_hWnd); + SendMessage(TDM_SET_PROGRESS_BAR_RANGE, 0, MAKELPARAM(nMinRange, nMaxRange)); + } + + inline void CTaskDialog::SetProgressBarState(int nNewState /* = PBST_NORMAL*/) + // Sets the current state of the progress bar. Possible states are: + // PBST_NORMAL + // PBST_PAUSE + // PBST_ERROR + { + assert(m_hWnd); + SendMessage(TDM_SET_PROGRESS_BAR_STATE, (WPARAM)nNewState, 0); + } + + inline void CTaskDialog::SetVerificationCheckbox(BOOL bChecked) + // Simulates a click on the verification checkbox of the Task Dialog, if it exists. + { + assert(m_hWnd); + SendMessage(TDM_CLICK_VERIFICATION, (WPARAM)bChecked, (LPARAM)bChecked); + } + + inline void CTaskDialog::SetVerificationCheckboxText(LPCTSTR pszVerificationText) + // Sets the text for the verification check box. + { + assert (m_hWnd == NULL); + StoreText(m_vVerificationText, pszVerificationText); + m_tc.pszVerificationText = &m_vVerificationText.front(); + } + + inline void CTaskDialog::SetWindowTitle(LPCTSTR pszWindowTitle) + // Sets the Task Dialog's window title. + { + assert (m_hWnd == NULL); + StoreText(m_vWindowTitle, pszWindowTitle); + m_tc.pszWindowTitle = &m_vWindowTitle.front(); + } + + inline HRESULT CALLBACK CTaskDialog::StaticTaskDialogProc(HWND hWnd, UINT uNotification, WPARAM wParam, LPARAM lParam, LONG_PTR dwRefData) + // TaskDialogs direct their messages here. + { + UNREFERENCED_PARAMETER(dwRefData); + + assert( GetApp() ); + + try + { + CTaskDialog* t = (CTaskDialog*)GetApp()->GetCWndFromMap(hWnd); + if (0 == t) + { + // The CTaskDialog pointer wasn't found in the map, so add it now + + // Retrieve the pointer to the TLS Data + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + if (NULL == pTLSData) + throw CWinException(_T("Unable to get TLS")); + + // Retrieve pointer to CTaskDialog object from Thread Local Storage TLS + t = (CTaskDialog*)(pTLSData->pCWnd); + if (NULL == t) + throw CWinException(_T("Failed to route message")); + + pTLSData->pCWnd = NULL; + + // Store the CTaskDialog pointer in the HWND map + t->m_hWnd = hWnd; + t->AddToMap(); + } + + return t->TaskDialogProc(uNotification, wParam, lParam); + } + + catch (const CWinException &e) + { + // Most CWinExceptions will end up here unless caught earlier. + e.what(); + } + + return 0L; + + } // LRESULT CALLBACK StaticTaskDialogProc(...) + + inline void CTaskDialog::StoreText(std::vector<WCHAR>& vWChar, LPCTSTR pFromTChar) + { + // Stores a TChar string in a WCHAR vector + + std::vector<TCHAR> vTChar; + + if (IS_INTRESOURCE(pFromTChar)) // support MAKEINTRESOURCE + { + tString ts = LoadString((UINT)pFromTChar); + int len = pFromTChar? ts.length() + 1 : 1; + vTChar.assign(len, _T('\0')); + vWChar.assign(len, _T('\0')); + if (pFromTChar) + lstrcpy( &vTChar.front(), ts.c_str()); + + } + else + { + int len = lstrlen(pFromTChar) +1; + vTChar.assign(len, _T('\0')); + vWChar.assign(len, _T('\0')); + lstrcpy( &vTChar.front(), pFromTChar); + } + + lstrcpyW(&vWChar.front(), T2W(&vTChar.front()) ); + } + + inline LRESULT CTaskDialog::TaskDialogProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + // Handles the Task Dialog's notificaions. + { + switch(uMsg) + { + case TDN_BUTTON_CLICKED: + return OnTDButtonClicked((int)wParam); + + case TDN_CREATED: + OnTDCreated(); + break; + case TDN_DESTROYED: + Cleanup(); // Prepare this CWnd to be reused. + OnTDDestroyed(); + break; + case TDN_DIALOG_CONSTRUCTED: + OnTDConstructed(); + break; + case TDN_EXPANDO_BUTTON_CLICKED: + OnTDExpandButtonClicked((BOOL)wParam); + break; + case TDN_HELP: + OnTDHelp(); + break; + case TDN_HYPERLINK_CLICKED: + OnTDHyperlinkClicked(W2T((LPCWSTR)lParam)); + break; + case TDN_NAVIGATED: + OnTDNavigatePage(); + break; + case TDN_RADIO_BUTTON_CLICKED: + OnTDRadioButtonClicked((int)wParam); + break; + case TDN_TIMER: + return OnTDTimer((DWORD)wParam); + + case TDN_VERIFICATION_CLICKED: + OnTDVerificationCheckboxClicked((BOOL)wParam); + break; + } + + return S_OK; + } + + inline LRESULT CTaskDialog::TaskDialogProc(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + // Override this function in your class derrived from CDialog if you wish to handle messages + // A typical function might look like this: + + // switch (uMsg) + // { + // case MESSAGE1: // Some Windows API message + // OnMessage1(); // A user defined function + // break; // Also do default processing + // case MESSAGE2: + // OnMessage2(); + // return x; // Don't do default processing, but instead return + // // a value recommended by the Windows API documentation + // } + + // Always pass unhandled messages on to TaskDialogProcDefault + return TaskDialogProcDefault(uMsg, wParam, lParam); + } + + inline void CTaskDialog::UpdateElementText(TASKDIALOG_ELEMENTS eElement, LPCTSTR pszNewText) + // Updates a text element on the Task Dialog. + { + assert(m_hWnd); + SendMessage(TDM_UPDATE_ELEMENT_TEXT, (WPARAM)eElement, (LPARAM)(LPCWSTR)T2W(pszNewText)); + } + +} + + + +#endif // _WIN32XX_TASKDIALOG_H_
\ No newline at end of file diff --git a/mmc_updater/depends/win32cpp/thread.h b/mmc_updater/depends/win32cpp/thread.h new file mode 100644 index 00000000..5564d888 --- /dev/null +++ b/mmc_updater/depends/win32cpp/thread.h @@ -0,0 +1,241 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +// The CThread class simplifies the use of threads with Win32++. +// To use threads in your Win32++ application, inherit a class from +// CThread, and override InitInstance. When your class is instanciated, +// a new thread is started, and the InitInstance function is called to +// run in the new thread. + +// If your thread is used to run one or more windows, InitInstance should +// return TRUE, causing the MessageLoop function to be called. If your +// thread doesn't require a MessageLoop, it should return FALSE. Threads +// which don't run a message loop as sometimes referred to as "worker" threads. + +// Note: It is your job to end the thread before CThread ends! +// To end a thread with a message loop, use PostQuitMessage on the thread. +// To end a thread without a message loop, set an event, and end the thread +// when the event is received. + +// Hint: It is never a good idea to use things like TerminateThread or ExitThread to +// end your thread. These represent poor programming techniques, and are likely +// to leak memory and resources. + +// More Hints for thread programming: +// 1) Avoid using SendMessage between threads, as this will cause one thread to wait for +// the other to respond. Use PostMessage between threads to avoid this problem. +// 2) Access to variables and resources shared between threads need to be made thread safe. +// Having one thread modify a resouce or variable while another thread is accessing it is +// a recipe for disaster. +// 3) Thread Local Storage (TLS) can be used to replace global variables to make them thread +// safe. With TLS, each thread gets its own copy of the variable. +// 4) Critical Sections can be used to make shared resources thread safe. +// 5) Window messages (including user defined messages) can be posted between GUI threads to +// communicate information between them. +// 6) Events (created by CreateEvent) can be used to comunicate information between threads +// (both GUI and worker threads). +// 7) Avoid using sleep to synchronise threads. Generally speaking, the various wait +// functions (e.g. WaitForSingleObject) will be better for this. + +// About Threads: +// Each program that executes has a "process" allocated to it. A process has one or more +// threads. Threads run independantly of each other. It is the job of the operating system +// to manage the running of the threads, and do the task switching between threads as required. +// Systems with multiple CPUs will be able to run as many threads simultaneously as there are +// CPUs. + +// Threads behave like a program within a program. When the main thread starts, the application +// runs the WinMain function and ends when WinMain ends. When another thread starts, it too +// will run the function provided to it, and end when that function ends. + + +#ifndef _WIN32XX_WINTHREAD_H_ +#define _WIN32XX_WINTHREAD_H_ + + +#include <process.h> + + +namespace Win32xx +{ + + ////////////////////////////////////// + // Declaration of the CThread class + // + class CThread + { + public: + CThread(); + CThread(LPSECURITY_ATTRIBUTES pSecurityAttributes, unsigned stack_size, unsigned initflag); + virtual ~CThread(); + + // Overridables + virtual BOOL InitInstance(); + virtual int MessageLoop(); + + // Operations + HANDLE GetThread() const; + int GetThreadID() const; + int GetThreadPriority() const; + DWORD ResumeThread() const; + BOOL SetThreadPriority(int nPriority) const; + DWORD SuspendThread() const; + + private: + CThread(const CThread&); // Disable copy construction + CThread& operator = (const CThread&); // Disable assignment operator + void CreateThread(LPSECURITY_ATTRIBUTES pSecurityAttributes, unsigned stack_size, unsigned initflag); + static UINT WINAPI StaticThreadCallback(LPVOID pCThread); + + HANDLE m_hThread; // Handle of this thread + UINT m_nThreadID; // ID of this thread + }; + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +namespace Win32xx +{ + + /////////////////////////////////////// + // Definitions for the CThread class + // + inline CThread::CThread() : m_hThread(0), m_nThreadID(0) + { + CreateThread(0, 0, CREATE_SUSPENDED); + } + + inline CThread::CThread(LPSECURITY_ATTRIBUTES pSecurityAttributes, unsigned stack_size, unsigned initflag) + : m_hThread(0), m_nThreadID(0) + + { + // Valid argument values: + // pSecurityAttributes Either a pointer to SECURITY_ATTRIBUTES or 0 + // stack_size Either the stack size or 0 + // initflag Either CREATE_SUSPENDED or 0 + + CreateThread(pSecurityAttributes, stack_size, initflag); + } + + inline CThread::~CThread() + { + // A thread's state is set to signalled when the thread terminates. + // If your thread is still running at this point, you have a bug. + if (0 != WaitForSingleObject(m_hThread, 0)) + TRACE(_T("*** Error *** Ending CThread before ending its thread\n")); + + // Close the thread's handle + ::CloseHandle(m_hThread); + } + + inline void CThread::CreateThread(LPSECURITY_ATTRIBUTES pSecurityAttributes, unsigned stack_size, unsigned initflag) + { + // NOTE: By default, the thread is created in the default state. + m_hThread = (HANDLE)_beginthreadex(pSecurityAttributes, stack_size, CThread::StaticThreadCallback, (LPVOID) this, initflag, &m_nThreadID); + + if (0 == m_hThread) + throw CWinException(_T("Failed to create thread")); + } + + inline HANDLE CThread::GetThread() const + { + assert(m_hThread); + return m_hThread; + } + + inline int CThread::GetThreadID() const + { + assert(m_hThread); + return m_nThreadID; + } + + inline int CThread::GetThreadPriority() const + { + assert(m_hThread); + return ::GetThreadPriority(m_hThread); + } + + inline BOOL CThread::InitInstance() + { + // Override this function to perform tasks when the thread starts. + + // return TRUE to run a message loop, otherwise return FALSE. + // A thread with a window must run a message loop. + return FALSE; + } + + inline int CThread::MessageLoop() + { + // Override this function if your thread needs a different message loop + return GetApp()->MessageLoop(); + } + + inline DWORD CThread::ResumeThread() const + { + assert(m_hThread); + return ::ResumeThread(m_hThread); + } + + inline DWORD CThread::SuspendThread() const + { + assert(m_hThread); + return ::SuspendThread(m_hThread); + } + + inline BOOL CThread::SetThreadPriority(int nPriority) const + { + assert(m_hThread); + return ::SetThreadPriority(m_hThread, nPriority); + } + + inline UINT WINAPI CThread::StaticThreadCallback(LPVOID pCThread) + // When the thread starts, it runs this function. + { + // Get the pointer for this CMyThread object + CThread* pThread = (CThread*)pCThread; + + if (pThread->InitInstance()) + return pThread->MessageLoop(); + + return 0; + } + +} + +#endif // #define _WIN32XX_WINTHREAD_H_ + diff --git a/mmc_updater/depends/win32cpp/toolbar.h b/mmc_updater/depends/win32cpp/toolbar.h new file mode 100644 index 00000000..1ed005a0 --- /dev/null +++ b/mmc_updater/depends/win32cpp/toolbar.h @@ -0,0 +1,1361 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +#ifndef _WIN32XX_TOOLBAR_H_ +#define _WIN32XX_TOOLBAR_H_ + +#include "wincore.h" +#include "gdi.h" +#include "rebar.h" + + +namespace Win32xx +{ + + struct ToolBarTheme + { + BOOL UseThemes; // TRUE if themes are used + COLORREF clrHot1; // Colour 1 for hot button + COLORREF clrHot2; // Colour 2 for hot button + COLORREF clrPressed1; // Colour 1 for pressed button + COLORREF clrPressed2; // Colour 2 for pressed button + COLORREF clrOutline; // Colour for border outline + }; + + + //////////////////////////////////// + // Declaration of the CToolBar class + // + class CToolBar : public CWnd + { + public: + CToolBar(); + virtual ~CToolBar(); + + // Operations + virtual int AddBitmap(UINT ToolBarID); + virtual BOOL AddButton(UINT nID, BOOL bEnabled = TRUE); + virtual void Destroy(); + virtual BOOL ReplaceBitmap(UINT NewToolBarID); + virtual BOOL SetBitmap(UINT nID); + virtual int SetButtons(const std::vector<UINT>& vToolBarData) const; + virtual BOOL SetButtonText(int idButton, LPCTSTR szText); + virtual BOOL SetImages(COLORREF crMask, UINT ToolBarID, UINT ToolBarHotID, UINT ToolBarDisabledID); + + // Wrappers for Win32 API functions + BOOL AddButtons(UINT uNumButtons, LPTBBUTTON lpButtons) const; + int AddString(UINT nStringID) const; + int AddStrings(LPCTSTR lpszStrings) const; + void Autosize() const; + void CheckButton(int idButton, BOOL fCheck) const; + int CommandToIndex(int idButton) const; + BOOL DeleteButton(int iButton) const; + BOOL DisableButton(int idButton) const; + BOOL EnableButton(int idButton) const; + BOOL GetButton(int iButton, LPTBBUTTON lpButton) const; + int GetButtonCount() const; + DWORD GetButtonSize() const; + UINT GetButtonState(int idButton) const; + BYTE GetButtonStyle(int idButton) const; + CString GetButtonText(int idButton) const; + int GetCommandID(int iIndex) const; + HIMAGELIST GetDisabledImageList() const; + int GetHotItem() const; + HIMAGELIST GetHotImageList() const; + HIMAGELIST GetImageList() const; + CRect GetItemRect(int iIndex) const; + CSize GetMaxSize() const; + DWORD GetPadding() const; + CRect GetRect(int idButton) const; + int GetRows() const; + int GetTextRows() const; + HWND GetToolTips() const; + BOOL HasText() const; + BOOL HideButton(int idButton, BOOL fShow) const; + int HitTest() const; + BOOL Indeterminate(int idButton, BOOL fIndeterminate) const; + BOOL InsertButton(int iButton, LPTBBUTTON lpButton) const; + BOOL IsButtonHidden(int idButton) const; + BOOL IsButtonHighlighted(int idButton) const; + BOOL IsButtonIndeterminate(int idButton) const; + BOOL IsButtonPressed(int idButton) const; + int MapAccelerator(TCHAR chAccel) const; + BOOL MarkButton(int idButton) const; + BOOL MoveButton(UINT uOldPos, UINT uNewPos) const; + BOOL PressButton(int idButton, BOOL fPress) const; + void SaveRestore(BOOL fSave, TBSAVEPARAMS* ptbsp) const; + BOOL SetBitmapSize(int cx, int cy) const; + BOOL SetButtonSize(int cx, int cy) const; + BOOL SetButtonState(int idButton, UINT State) const; + BOOL SetButtonStyle(int idButton, BYTE Style) const; + BOOL SetButtonWidth(int idButton, int nWidth) const; + BOOL SetCommandID(int iIndex, int idButton) const; + HIMAGELIST SetDisableImageList(HIMAGELIST himlNewDisabled) const; + DWORD SetDrawTextFlags(DWORD dwMask, DWORD dwDTFlags) const; + DWORD SetExtendedStyle(DWORD dwExStyle) const; + HIMAGELIST SetHotImageList(HIMAGELIST himlNewHot) const; + int SetHotItem(int iHot) const; + HIMAGELIST SetImageList(HIMAGELIST himlNew) const; + BOOL SetIndent(int iIndent) const; + BOOL SetMaxTextRows(int iMaxRows) const; + BOOL SetPadding(int cx, int cy) const; + void SetToolTips(HWND hwndToolTip) const; + + // Attributes + std::vector<UINT>& GetToolBarData() const {return (std::vector <UINT> &)m_vToolBarData;} + ToolBarTheme& GetToolBarTheme() {return m_Theme;} + void SetToolBarTheme(ToolBarTheme& Theme); + + protected: + // Overridables + virtual void OnCreate(); + virtual void OnDestroy(); + virtual void OnWindowPosChanging(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnCustomDraw(NMHDR* pNMHDR); + virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void PreRegisterClass(WNDCLASS &wc); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CToolBar(const CToolBar&); // Disable copy construction + CToolBar& operator = (const CToolBar&); // Disable assignment operator + + std::vector<UINT> m_vToolBarData; // vector of resource IDs for toolbar buttons + std::map<tString, int> m_StringMap; // a map of strings used in SetButtonText + UINT m_OldToolBarID; // Bitmap Resource ID, used in AddBitmap/ReplaceBitmap + ToolBarTheme m_Theme; // The theme structure + BOOL m_bDrawArrowBkgrnd; // True if a seperate arrow background is to be drawn + + }; // class CToolBar + +} + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +namespace Win32xx +{ + + //////////////////////////////////// + // Definitions for the CToolBar class + // + inline CToolBar::CToolBar() : m_OldToolBarID(0), m_bDrawArrowBkgrnd(FALSE) + { + ZeroMemory(&m_Theme, sizeof(ToolBarTheme)); + } + + inline CToolBar::~CToolBar() + { + } + + inline int CToolBar::AddBitmap(UINT ToolBarID) + // Adds one or more images to the list of button images available for a toolbar. + + // Note: AddBitmap supports a maximum colour depth of 8 bits (256 colours) + // For more colours, use an ImageList instead + { + assert(::IsWindow(m_hWnd)); + + int iNumButtons = 0; + std::vector<UINT>::iterator iter; + for (iter = GetToolBarData().begin(); iter < GetToolBarData().end(); ++iter) + if ((*iter) != 0) ++iNumButtons; + + TBADDBITMAP tbab = {0}; + tbab.hInst = GetApp()->GetResourceHandle(); + tbab.nID = ToolBarID; + int iResult = (int)SendMessage(TB_ADDBITMAP, iNumButtons, (LPARAM)&tbab); + + if (-1 != iResult) + m_OldToolBarID = ToolBarID; + + return iResult; + } + + inline BOOL CToolBar::AddButton(UINT nID, BOOL bEnabled /* = TRUE */) + // Adds Resource IDs to toolbar buttons. + // A resource ID of 0 is a separator + { + assert(::IsWindow(m_hWnd)); + + m_vToolBarData.push_back(nID); + + // TBBUTTON structure for each button in the toolbar + TBBUTTON tbb = {0}; + + std::vector<UINT>::iterator iter; + int iImages = 0; + for(iter = m_vToolBarData.begin(); iter < m_vToolBarData.end(); ++iter) + if (0 != *iter) iImages++; + + ZeroMemory(&tbb, sizeof(TBBUTTON)); + + if (0 == nID) + { + tbb.fsStyle = TBSTYLE_SEP; + } + else + { + tbb.dwData = iImages -1; + tbb.iBitmap = iImages -1; + tbb.idCommand = nID; + tbb.fsState = bEnabled? TBSTATE_ENABLED : 0; + tbb.fsStyle = TBSTYLE_BUTTON; + } + + // Add the button to the toolbar + return (BOOL)SendMessage(TB_ADDBUTTONS, 1L, (LPARAM)&tbb); + } + + inline BOOL CToolBar::AddButtons(UINT uNumButtons, LPTBBUTTON lpButtons) const + // Adds one or more buttons to a toolbar. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_ADDBUTTONS, (LPARAM)uNumButtons, (WPARAM)lpButtons); + } + + inline int CToolBar::AddString(UINT nStringID) const + // Adds a new string, passed as a resource ID, to the toolbar's internal list of strings. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(TB_ADDSTRING, (LPARAM)GetApp()->GetResourceHandle(), (WPARAM)nStringID); + } + + inline int CToolBar::AddStrings(LPCTSTR lpszStrings) const + // Adds a new string or strings to the list of strings available for a toolbar control. + // Strings in the buffer must be separated by a null character. You must ensure that the last string has two null terminators. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(TB_ADDSTRING, 0L, (WPARAM)lpszStrings); + } + + inline void CToolBar::Autosize() const + // Causes a toolbar to be resized. + { + assert(::IsWindow(m_hWnd)); + SendMessage(TB_AUTOSIZE, 0L, 0L); + } + + inline void CToolBar::CheckButton(int idButton, BOOL fCheck) const + // Checks or unchecks a given button in a toolbar. + // When a button is checked, it is displayed in the pressed state. + { + assert(::IsWindow(m_hWnd)); + SendMessage(TB_CHECKBUTTON, (WPARAM)idButton, (LPARAM)MAKELONG(fCheck, 0)); + } + + inline int CToolBar::CommandToIndex(int idButton) const + // Retrieves the zero-based index for the button associated with the specified command identifier + { + assert(::IsWindow(m_hWnd)); + + // returns -1 on fail + return (int)SendMessage(TB_COMMANDTOINDEX, (WPARAM)idButton, 0L); + } + + inline BOOL CToolBar::DeleteButton(int iButton) const + // Deletes a button from the toolbar. + // iButton is the Zero-based index of the button to delete. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(TB_DELETEBUTTON, (WPARAM)iButton, 0L); + } + + inline void CToolBar::Destroy() + // Allows CToolBar to be reused after the window is destroyed + { + CWnd::Destroy(); + m_StringMap.clear(); + } + + inline BOOL CToolBar::DisableButton(int idButton) const + // Disables the specified button in a toolbar + // An example of idButton would be IDM_FILE_OPEN + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_ENABLEBUTTON, (WPARAM)idButton, (LPARAM) MAKELONG(FALSE, 0)); + } + + inline BOOL CToolBar::EnableButton(int idButton) const + // Enables the specified button in a toolbar + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_ENABLEBUTTON, (WPARAM)idButton, (LPARAM) MAKELONG(TRUE,0 )); + } + + inline BOOL CToolBar::GetButton(int iButton, LPTBBUTTON lpButton) const + // Recieves the TBBUTTON structure information from the specified button + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_GETBUTTON, (LPARAM)iButton, (WPARAM)lpButton); + } + + inline int CToolBar::GetButtonCount() const + // Retrieves a count of the buttons currently in the toolbar + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(TB_BUTTONCOUNT, 0L, 0L); + } + + inline DWORD CToolBar::GetButtonSize() const + // Retrieves the current width and height of toolbar buttons, in pixels. + // Returns a DWORD value that contains the width and height values in the low word and high word, respectively. + { + assert(::IsWindow(m_hWnd)); + return (DWORD)SendMessage(TB_GETBUTTONSIZE, 0L, 0L); + } + + inline UINT CToolBar::GetButtonState(int idButton) const + // Get the state of an individual button + // TBSTATE_CHECKED The button has the TBSTYLE_CHECK style and is being clicked. + // TBSTATE_ELLIPSES The button's text is cut off and an ellipsis is displayed. + // TBSTATE_ENABLED The button accepts user input. A button that doesn't have this state is grayed. + // TBSTATE_HIDDEN The button is not visible and cannot receive user input. + // TBSTATE_INDETERMINATE The button is grayed. + // TBSTATE_MARKED The button is marked. The interpretation of a marked item is dependent upon the application. + // TBSTATE_PRESSED The button is being clicked. + // TBSTATE_WRAP The button is followed by a line break. + { + assert(::IsWindow(m_hWnd)); + return (UINT)SendMessage(TB_GETSTATE, (WPARAM) idButton, 0L); + } + + inline BYTE CToolBar::GetButtonStyle(int idButton) const + // Get the the style of the toolbar control. The following button styles are supported: + // TBSTYLE_BUTTON Standard pushbutton (default) + // TBSTYLE_SEP Separator + // TBSTYLE_CHECK Auto check-box button + // TBSTYLE_GROUP Marks the start of a group of buttons + // TBSTYLE_CHECKGROUP Marks the start of a group of check-box buttons + // TBSTYLE_DROPDOWN Creates a drop-down list button + // TBSTYLE_AUTOSIZE The button's width will be calculated based on the text of the button, not on the size of the image + // TBSTYLE_NOPREFIX The button text will not have an accelerator prefix associated with it + { + assert(::IsWindow(m_hWnd)); + + int iIndex = CommandToIndex(idButton); + TBBUTTON tbb = {0}; + SendMessage(TB_GETBUTTON, iIndex, (LPARAM) &tbb); + + return tbb.fsStyle; + } + + inline CString CToolBar::GetButtonText(int idButton) const + // Retrieves the display text of a button on a toolbar. + { + assert(::IsWindow(m_hWnd)); + + int Length = (int)SendMessage(TB_GETBUTTONTEXT, idButton, 0); + CString str; + LPTSTR szStr = str.GetBuffer(Length +1); + SendMessage(TB_GETBUTTONTEXT, (LPARAM)idButton, (WPARAM)szStr); + str.ReleaseBuffer(); + return str; + } + + inline int CToolBar::GetCommandID(int iIndex) const + // Retrieves information about the specified button in a toolbar + { + assert(::IsWindow(m_hWnd)); + TBBUTTON tbb = {0}; + SendMessage(TB_GETBUTTON, iIndex, (WPARAM) &tbb); + + // returns zero if failed + return tbb.idCommand; + } + + inline HIMAGELIST CToolBar::GetDisabledImageList() const + // Retrieves the image list that a toolbar control uses to display inactive buttons. + { + assert(::IsWindow(m_hWnd)); + return (HIMAGELIST)SendMessage(TB_GETDISABLEDIMAGELIST, 0L, 0L); + } + + inline HIMAGELIST CToolBar::GetHotImageList() const + // Retrieves the image list that a toolbar control uses to display hot buttons. + { + assert(::IsWindow(m_hWnd)); + return (HIMAGELIST)SendMessage(TB_GETHOTIMAGELIST, 0L, 0L); + } + + inline int CToolBar::GetHotItem() const + // Retrieves the index of the hot item in a toolbar, or -1 if no hot item is set. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(TB_GETHOTITEM, 0L, 0L); + } + + inline HIMAGELIST CToolBar::GetImageList() const + // Retrieves the image list that a toolbar control uses to display buttons in their default state. + { + assert(::IsWindow(m_hWnd)); + return (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L); + } + + inline CRect CToolBar::GetItemRect(int iIndex) const + // Retrieves the bounding rectangle of a button in a toolbar + { + assert(::IsWindow(m_hWnd)); + CRect rc; + int iCount = (int)SendMessage(TB_BUTTONCOUNT, 0L, 0L); + + if (iCount >= iIndex) + SendMessage(TB_GETITEMRECT, (WPARAM)iIndex, (LPARAM)&rc); + + return rc; + } + + inline CSize CToolBar::GetMaxSize() const + // Retrieves the total size of all of the visible buttons and separators in the toolbar + { + assert(::IsWindow(m_hWnd)); + CSize sz; + SendMessage(TB_GETMAXSIZE, 0L, (LPARAM)&sz); + + // This fixes a Windows bug calculating the size when TBSTYLE_DROPDOWN is used. + int xMaxSize = 0; + for (int i= 0 ; i < GetButtonCount(); ++i) + { + xMaxSize += GetItemRect(i).Width(); + } + + sz.cx = xMaxSize; + return sz; + } + + inline DWORD CToolBar::GetPadding() const + // Returns a DWORD value that contains the horizontal padding in the low word and the vertical padding in the high word, in pixels. + { + assert(::IsWindow(m_hWnd)); + return (DWORD)SendMessage(TB_GETPADDING, 0L, 0L); + } + + inline CRect CToolBar::GetRect(int idButton) const + // Retrieves the bounding rectangle for a specified toolbar button. + { + assert(::IsWindow(m_hWnd)); + CRect rc; + SendMessage(TB_GETRECT, (WPARAM)idButton, (LPARAM)&rc); + return rc; + } + + inline int CToolBar::GetRows() const + // Retrieves the number of rows of buttons in a toolbar with the TBSTYLE_WRAPABLE style. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(TB_GETROWS, 0L, 0L); + } + + inline int CToolBar::GetTextRows() const + // Retrieves the maximum number of text rows that can be displayed on a toolbar button. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(TB_GETTEXTROWS, 0L, 0L); + } + + inline HWND CToolBar::GetToolTips() const + // Retrieves the handle to the ToolTip control, if any, associated with the toolbar. + { + assert(::IsWindow(m_hWnd)); + return (HWND)SendMessage(TB_GETTOOLTIPS, 0L, 0L); + } + + inline BOOL CToolBar::HasText() const + { + assert(::IsWindow(m_hWnd)); + BOOL bReturn = FALSE; + + for (int i = 0 ; i < GetButtonCount(); ++i) + { + if (SendMessage(TB_GETBUTTONTEXT, GetCommandID(i), 0L) != -1) + bReturn = TRUE; + } + + // return TRUE if any button has text + return bReturn; + } + + inline BOOL CToolBar::HideButton(int idButton, BOOL fShow) const + //Hides or shows the specified button in a toolbar. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_HIDEBUTTON, (WPARAM)idButton, (LPARAM)MAKELONG (fShow, 0)); + } + + inline int CToolBar::HitTest() const + // Determines where a point lies in a toolbar control. + + // We do our own hit test since TB_HITTEST is a bit buggy, + // and also doesn't work at all on earliest versions of Win95 + { + assert(::IsWindow(m_hWnd)); + CPoint pt = GetCursorPos(); + ScreenToClient(pt); + + int nButtons = (int)SendMessage(TB_BUTTONCOUNT, 0L, 0L); + int iButton = -1; + + for (int i = 0 ; i < nButtons; ++i) + { + CRect rc = GetItemRect(i); + if (rc.PtInRect(pt)) + iButton = i; + } + + return iButton; + } + + inline BOOL CToolBar::Indeterminate(int idButton, BOOL fIndeterminate) const + //Hides or shows the specified button in a toolbar. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_INDETERMINATE, (WPARAM)idButton, (LPARAM)MAKELONG (fIndeterminate, 0)); + } + + inline BOOL CToolBar::InsertButton(int iButton, LPTBBUTTON lpButton) const + // Inserts a button in a toolbar. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_INSERTBUTTON, (WPARAM)iButton, (LPARAM)lpButton); + } + + inline BOOL CToolBar::IsButtonHidden(int idButton) const + // Determines whether the specified button in a toolbar is hidden. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_ISBUTTONHIDDEN, (WPARAM)idButton, 0L); + } + + inline BOOL CToolBar::IsButtonHighlighted(int idButton) const + // Checks the highlight state of a toolbar button. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_ISBUTTONHIGHLIGHTED, (WPARAM)idButton, 0L); + } + + inline BOOL CToolBar::IsButtonIndeterminate(int idButton) const + // Determines whether the specified button in a toolbar is indeterminate. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_ISBUTTONINDETERMINATE, (WPARAM)idButton, 0L); + } + + inline BOOL CToolBar::IsButtonPressed(int idButton) const + // Determines whether the specified button in a toolbar is pressed. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_ISBUTTONPRESSED, (WPARAM)idButton, 0L); + } + + inline int CToolBar::MapAccelerator(TCHAR chAccel) const + // Determines whether the specified button in a toolbar is pressed. + { + assert(::IsWindow(m_hWnd)); + int uButtonID; + int idButton; + if (SendMessage(TB_MAPACCELERATOR, (WPARAM)chAccel, (LPARAM)&uButtonID)) + idButton = uButtonID; + else + idButton = -1; + + return idButton; + } + + inline BOOL CToolBar::MarkButton(int idButton) const + // Sets the highlight state of a given button in a toolbar control. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_MARKBUTTON, (WPARAM)idButton, 0L); + } + + inline BOOL CToolBar::MoveButton(UINT uOldPos, UINT uNewPos) const + // Moves a button from one index to another. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_MOVEBUTTON, (WPARAM)uOldPos, (LPARAM)uNewPos); + } + + + inline void CToolBar::OnCreate() + { + // We must send this message before sending the TB_ADDBITMAP or TB_ADDBUTTONS message + SendMessage(TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0L); + + // allows buttons to have a separate dropdown arrow + // Note: TBN_DROPDOWN notification is sent by a toolbar control when the user clicks a dropdown button + SendMessage(TB_SETEXTENDEDSTYLE, 0L, TBSTYLE_EX_DRAWDDARROWS); + + // Turn of Double click processing (i.e. treat a double click as two single clicks) + DWORD dwStyle = (DWORD)GetClassLongPtr(GCL_STYLE); + dwStyle &= ~CS_DBLCLKS; + SetClassLongPtr(GCL_STYLE, dwStyle); + + // Add extra styles for toolbars inside a rebar + if (lstrcmp(GetParent()->GetClassName(), _T("ReBarWindow32")) == 0) + { + DWORD style = (DWORD)GetWindowLongPtr(GWL_STYLE); + style |= CCS_NODIVIDER | CCS_NORESIZE; + SetWindowLongPtr(GWL_STYLE, style); + } + + SetButtons(m_vToolBarData); + + // Set rows of text to zero + SendMessage(TB_SETMAXTEXTROWS, 0L, 0L); + } + + inline LRESULT CToolBar::OnCustomDraw(NMHDR* pNMHDR) + // With CustomDraw we manually control the drawing of each toolbar button + { + LPNMTBCUSTOMDRAW lpNMCustomDraw = (LPNMTBCUSTOMDRAW)pNMHDR; + + switch (lpNMCustomDraw->nmcd.dwDrawStage) + { + // Begin paint cycle + case CDDS_PREPAINT: + // Send NM_CUSTOMDRAW item draw, and post-paint notification messages. + return CDRF_NOTIFYITEMDRAW | CDRF_NOTIFYPOSTPAINT ; + + // An item is about to be drawn + case CDDS_ITEMPREPAINT: + { + CDC* pDrawDC = FromHandle(lpNMCustomDraw->nmcd.hdc); + CRect rcRect = lpNMCustomDraw->nmcd.rc; + int nState = lpNMCustomDraw->nmcd.uItemState; + DWORD dwItem = (DWORD)lpNMCustomDraw->nmcd.dwItemSpec; + DWORD dwTBStyle = (DWORD)SendMessage(TB_GETSTYLE, 0L, 0L); + int nStyle = GetButtonStyle(dwItem); + + int nButton = (int)SendMessage(TB_COMMANDTOINDEX, (WPARAM) dwItem, 0L); + TBBUTTON tbb = {0}; + SendMessage(TB_GETBUTTON, nButton, (LPARAM)&tbb); + int iImage = (int)tbb.dwData; + + // Calculate text size + std::vector<TCHAR> vText(MAX_MENU_STRING, _T('\0')); + TCHAR* pszText = &vText[0]; + CSize TextSize; + if (HasText()) // Does any button have text? + { + pDrawDC->SelectObject(GetFont()); + if (SendMessage(TB_GETBUTTONTEXT, dwItem, (LPARAM)pszText)> 0) + { + TextSize = pDrawDC->GetTextExtentPoint32(pszText, lstrlen(pszText)); + } + } + + // Draw outline rectangle + if (nState & (CDIS_HOT | CDIS_SELECTED | CDIS_CHECKED)) + { + pDrawDC->CreatePen(PS_SOLID, 1, m_Theme.clrOutline); + pDrawDC->MoveTo(rcRect.left, rcRect.top); + pDrawDC->LineTo(rcRect.left, rcRect.bottom-1); + pDrawDC->LineTo(rcRect.right-1, rcRect.bottom-1); + pDrawDC->LineTo(rcRect.right-1, rcRect.top); + pDrawDC->LineTo(rcRect.left, rcRect.top); + } + + // Draw filled gradient background + rcRect.InflateRect(-1, -1); + if ((nState & (CDIS_SELECTED|CDIS_CHECKED)) || (GetButtonState(dwItem) & TBSTATE_PRESSED)) + { + pDrawDC->GradientFill(m_Theme.clrPressed1, m_Theme.clrPressed2, rcRect, FALSE); + } + else if (nState & CDIS_HOT) + { + pDrawDC->GradientFill(m_Theme.clrHot1, m_Theme.clrHot2, rcRect, FALSE); + } + + // Get the appropriate image list depending on the button state + HIMAGELIST himlToolBar; + if (nState & CDIS_DISABLED) + { + himlToolBar = (HIMAGELIST)SendMessage(TB_GETDISABLEDIMAGELIST, 0L, 0L); + } + else if (nState & (CDIS_HOT | CDIS_SELECTED | CDIS_CHECKED)) + { + himlToolBar = (HIMAGELIST)SendMessage(TB_GETHOTIMAGELIST, 0L, 0L); + if (0 == himlToolBar) + himlToolBar = (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L); + } + else + { + himlToolBar = (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L); + } + + BOOL IsWin95 = (1400 == (GetWinVersion()) || (2400 == GetWinVersion())); + + // Calculate image position + int cxImage = 0; + int cyImage = 0; + ImageList_GetIconSize(himlToolBar, &cxImage, &cyImage); + + int yImage = (rcRect.bottom - rcRect.top - cyImage - TextSize.cy +2)/2; + int xImage = (rcRect.right + rcRect.left - cxImage)/2 + ((nState & (CDIS_SELECTED|CDIS_CHECKED))? 1:0); + if (dwTBStyle & TBSTYLE_LIST) + { + xImage = rcRect.left + (IsXPThemed()?2:4) + ((nState & CDIS_SELECTED)? 1:0); + yImage = (rcRect.bottom -rcRect.top - cyImage +2)/2 + ((nState & (CDIS_SELECTED|CDIS_CHECKED))? 1:0); + } + + // Handle the TBSTYLE_DROPDOWN and BTNS_WHOLEDROPDOWN styles + if ((nStyle & TBSTYLE_DROPDOWN) || ((nStyle & 0x0080) && (!IsWin95))) + { + // Calculate the dropdown arrow position + int xAPos = (nStyle & TBSTYLE_DROPDOWN)? rcRect.right -6 : (rcRect.right + rcRect.left + cxImage + 4)/2; + int yAPos = (nStyle & TBSTYLE_DROPDOWN)? (rcRect.bottom - rcRect.top +1)/2 : (cyImage)/2; + if (dwTBStyle & TBSTYLE_LIST) + { + xAPos = (nStyle & TBSTYLE_DROPDOWN)?rcRect.right -6:rcRect.right -5; + yAPos = (rcRect.bottom - rcRect.top +1)/2 + ((nStyle & TBSTYLE_DROPDOWN)?0:1); + } + + xImage -= (nStyle & TBSTYLE_DROPDOWN)?((dwTBStyle & TBSTYLE_LIST)? (IsXPThemed()?-4:0):6):((dwTBStyle & TBSTYLE_LIST)? 0:4); + + // Draw separate background for dropdown arrow + if ((m_bDrawArrowBkgrnd) && (nState & CDIS_HOT)) + { + CRect rcArrowBkgnd = rcRect; + rcArrowBkgnd.left = rcArrowBkgnd.right - 13; + pDrawDC->GradientFill(m_Theme.clrPressed1, m_Theme.clrPressed2, rcArrowBkgnd, FALSE); + } + + m_bDrawArrowBkgrnd = FALSE; + + // Manually draw the dropdown arrow + pDrawDC->CreatePen(PS_SOLID, 1, RGB(0,0,0)); + for (int i = 2; i >= 0; --i) + { + pDrawDC->MoveTo(xAPos -i-1, yAPos - i+1); + pDrawDC->LineTo(xAPos +i, yAPos - i+1); + } + + // Draw line between icon and dropdown arrow + if ((nStyle & TBSTYLE_DROPDOWN) && ((nState & CDIS_SELECTED) || nState & CDIS_HOT)) + { + pDrawDC->CreatePen(PS_SOLID, 1, m_Theme.clrOutline); + pDrawDC->MoveTo(rcRect.right - 13, rcRect.top); + pDrawDC->LineTo(rcRect.right - 13, rcRect.bottom); + } + } + + // Draw the button image + if (xImage > 0) + { + ImageList_Draw(himlToolBar, iImage, *pDrawDC, xImage, yImage, ILD_TRANSPARENT); + } + + //Draw Text + if (lstrlen(pszText) > 0) + { + int iWidth = rcRect.right - rcRect.left - ((nStyle & TBSTYLE_DROPDOWN)?13:0); + CRect rcText(0, 0, MIN(TextSize.cx, iWidth), TextSize.cy); + + int xOffset = (rcRect.right + rcRect.left - rcText.right + rcText.left - ((nStyle & TBSTYLE_DROPDOWN)? 11 : 1))/2; + int yOffset = yImage + cyImage +1; + + if (dwTBStyle & TBSTYLE_LIST) + { + xOffset = rcRect.left + cxImage + ((nStyle & TBSTYLE_DROPDOWN)?(IsXPThemed()?10:6): 6) + ((nState & CDIS_SELECTED)? 1:0); + yOffset = (2+rcRect.bottom - rcRect.top - rcText.bottom + rcText.top)/2 + ((nState & CDIS_SELECTED)? 1:0); + rcText.right = MIN(rcText.right, rcRect.right - xOffset); + } + + OffsetRect(&rcText, xOffset, yOffset); + + int iMode = pDrawDC->SetBkMode(TRANSPARENT); + pDrawDC->SelectObject(GetFont()); + + if (nState & (CDIS_DISABLED)) + { + // Draw text twice for embossed look + rcText.OffsetRect(1, 1); + pDrawDC->SetTextColor(RGB(255,255,255)); + pDrawDC->DrawText(pszText, lstrlen(pszText), rcText, DT_LEFT); + rcText.OffsetRect(-1, -1); + pDrawDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT)); + pDrawDC->DrawText(pszText, lstrlen(pszText), rcText, DT_LEFT); + } + else + { + pDrawDC->SetTextColor(GetSysColor(COLOR_BTNTEXT)); + pDrawDC->DrawText(pszText, lstrlen(pszText), rcText, DT_LEFT | DT_END_ELLIPSIS); + } + pDrawDC->SetBkMode(iMode); + } + } + return CDRF_SKIPDEFAULT; // No further drawing + } + return 0L; + } + + inline void CToolBar::OnDestroy() + { + HIMAGELIST himlToolBar = (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L); + HIMAGELIST himlToolBarHot = (HIMAGELIST)SendMessage(TB_GETHOTIMAGELIST, 0L, 0L); + HIMAGELIST himlToolBarDis = (HIMAGELIST)SendMessage(TB_GETDISABLEDIMAGELIST, 0L, 0L); + ImageList_Destroy(himlToolBar); + ImageList_Destroy(himlToolBarHot); + ImageList_Destroy(himlToolBarDis); + } + + inline LRESULT CToolBar::OnNotifyReflect(WPARAM wParam, LPARAM lParam) + // Notifications sent to the parent window are reflected back here + { + UNREFERENCED_PARAMETER(wParam); + + switch (((LPNMHDR)lParam)->code) + { + case NM_CUSTOMDRAW: + { + if (m_Theme.UseThemes) + return OnCustomDraw((LPNMHDR) lParam); + } + break; + + case TBN_DROPDOWN: + { + int iItem = ((LPNMTOOLBAR) lParam)->iItem; + + // a boolean expression + m_bDrawArrowBkgrnd = (GetButtonStyle(iItem) & TBSTYLE_DROPDOWN); + } + break; + } + return 0L; + } + + inline void CToolBar::OnWindowPosChanging(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + + // Adjust size for toolbars inside a rebar + CWnd* pParent = GetParent(); + if (lstrcmp(pParent->GetClassName(), _T("ReBarWindow32")) == 0) + { + ReBarTheme* pTheme = (ReBarTheme*)pParent->SendMessage(UWM_GETREBARTHEME, 0, 0); + + if (pTheme && pTheme->UseThemes && pTheme->ShortBands) + { + LPWINDOWPOS pWinPos = (LPWINDOWPOS)lParam; + pWinPos->cx = GetMaxSize().cx+2; + } + } + } + + inline void CToolBar::PreCreate(CREATESTRUCT &cs) + { + // Sets the CREATESTRUCT parameters prior to window creation + cs.style = WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS | TBSTYLE_FLAT; + } + + inline void CToolBar::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = TOOLBARCLASSNAME; + } + + inline BOOL CToolBar::PressButton(int idButton, BOOL fPress) const + // Presses or releases the specified button in a toolbar. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_PRESSBUTTON, (WPARAM)idButton, (LPARAM)MAKELONG(fPress, 0)); + } + + inline BOOL CToolBar::ReplaceBitmap(UINT NewToolBarID) + // Replaces an existing bitmap with a new bitmap. + + // Note: ReplaceBitmap supports a maximum colour depth of 8 bits (256 colours) + // For more colours, use an ImageList instead + { + assert(::IsWindow(m_hWnd)); + + int iNumButtons = 0; + std::vector<UINT>::iterator iter; + for (iter = GetToolBarData().begin(); iter < GetToolBarData().end(); ++iter) + if ((*iter) != 0) ++iNumButtons; + + TBREPLACEBITMAP tbrb = {0}; + tbrb.hInstNew = GetApp()->GetResourceHandle(); + tbrb.hInstOld = GetApp()->GetResourceHandle(); + tbrb.nIDNew = NewToolBarID; + tbrb.nIDOld = m_OldToolBarID; + tbrb.nButtons = iNumButtons; + + BOOL bResult = (BOOL)SendMessage(TB_REPLACEBITMAP, iNumButtons, (LPARAM)&tbrb); + if (bResult) + m_OldToolBarID = NewToolBarID; + + return bResult; + } + + inline void CToolBar::SaveRestore(BOOL fSave, TBSAVEPARAMS* ptbsp) const + // Presses or releases the specified button in a toolbar. + { + assert(::IsWindow(m_hWnd)); + SendMessage(TB_PRESSBUTTON, (WPARAM)fSave, (LPARAM)ptbsp); + } + + inline BOOL CToolBar::SetBitmap(UINT nID) + // Set the button images + { + assert(::IsWindow(m_hWnd)); + + CBitmap Bitmap(nID); + assert (Bitmap.GetHandle()); + BITMAP bm = Bitmap.GetBitmapData(); + + int iNumButtons = 0; + std::vector<UINT>::iterator iter; + for (iter = GetToolBarData().begin(); iter < GetToolBarData().end(); ++iter) + if ((*iter) != 0) ++iNumButtons; + + int iImageWidth = bm.bmWidth / iNumButtons; + int iImageHeight = bm.bmHeight; + + // Set the bitmap size first + SetBitmapSize(iImageWidth, iImageHeight); + + BOOL bResult = FALSE; + if (m_OldToolBarID) + bResult = ReplaceBitmap(nID); + else + bResult = (BOOL)AddBitmap(nID); + + return bResult; + } + + inline BOOL CToolBar::SetBitmapSize(int cx, int cy) const + // Sets the size of the bitmapped images to be added to a toolbar. + + // Needs to be used when the image size is not the default 16 x 15 + // Call this function before using AddBitmap or ReplaceBitmap + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_SETBITMAPSIZE, 0L, MAKELONG(cx, cy)); + } + + inline int CToolBar::SetButtons(const std::vector<UINT>& vToolBarData) const + // Assigns a resource ID to each toolbar button + { + assert(::IsWindow(m_hWnd)); + + int iImages = 0; + UINT iNumButtons = (UINT)vToolBarData.size(); + + // Remove any existing buttons + while (SendMessage(TB_BUTTONCOUNT, 0L, 0L) > 0) + { + if(!SendMessage(TB_DELETEBUTTON, 0L, 0L)) + break; + } + + if (iNumButtons > 0) + { + // TBBUTTON structure for each button in the toolbar + TBBUTTON tbb = {0}; + + for (UINT j = 0 ; j < iNumButtons; ++j) + { + ZeroMemory(&tbb, sizeof(TBBUTTON)); + + if (0 == vToolBarData[j]) + { + tbb.fsStyle = TBSTYLE_SEP; + } + else + { + tbb.dwData = iImages; + tbb.iBitmap = iImages; + tbb.idCommand = vToolBarData[j]; + tbb.fsState = TBSTATE_ENABLED; + tbb.fsStyle = TBSTYLE_BUTTON; + } + + // Add the button to the toolbar + if (SendMessage(TB_ADDBUTTONS, 1L, (LPARAM)&tbb)) + iImages++; + else + break; + } + } + + return iImages; + } + + inline BOOL CToolBar::SetButtonSize(int cx, int cy) const + // Sets the size of the buttons to be added to a toolbar + // The size can be set only before adding any buttons to the toolbar + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_SETBUTTONSIZE, 0L, MAKELONG(cx, cy)); + } + + inline BOOL CToolBar::SetButtonState(int idButton, UINT State) const + { + // Set the state of an individual button + // TBSTATE_CHECKED The button has the TBSTYLE_CHECK style and is being clicked. + // TBSTATE_ELLIPSES The button's text is cut off and an ellipsis is displayed. + // TBSTATE_ENABLED The button accepts user input. A button that doesn't have this state is grayed. + // TBSTATE_HIDDEN The button is not visible and cannot receive user input. + // TBSTATE_INDETERMINATE The button is grayed. + // TBSTATE_MARKED The button is marked. The interpretation of a marked item is dependent upon the application. + // TBSTATE_PRESSED The button is being clicked. + // TBSTATE_WRAP The button is followed by a line break. + + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_SETSTATE, (WPARAM) idButton, (LPARAM)MAKELONG (State, 0)); + } + + inline BOOL CToolBar::SetButtonStyle(int idButton, BYTE Style) const + // The the style of the toolbar control. The following button styles are supported: + // TBSTYLE_BUTTON Standard pushbutton (default) + // TBSTYLE_SEP Separator + // TBSTYLE_CHECK Auto check-box button + // TBSTYLE_GROUP Marks the start of a group of buttons + // TBSTYLE_CHECKGROUP Marks the start of a group of check-box buttons + // TBSTYLE_DROPDOWN Creates a drop-down list button + // TBSTYLE_AUTOSIZE The button's width will be calculated based on the text of the button, not on the size of the image + // TBSTYLE_NOPREFIX The button text will not have an accelerator prefix associated with it + { + assert(::IsWindow(m_hWnd)); + + TBBUTTONINFO tbbi = {0}; + tbbi.cbSize = sizeof(TBBUTTONINFO); + tbbi.dwMask = TBIF_STYLE; + tbbi.fsStyle = Style; + + // Note: TB_SETBUTTONINFO requires comctl32.dll version 4.71 or later + // i.e. Win95 with IE4 / NT with IE4 or later + return (BOOL)SendMessage(TB_SETBUTTONINFO, idButton, (LPARAM) &tbbi); + } + + inline BOOL CToolBar::SetButtonText(int idButton, LPCTSTR szText) + // This rather convoluted approach to setting toolbar button text supports + // all versions of Windows, including Win95 with COMCTL32.DLL version 4.0 + { + assert(::IsWindow(m_hWnd)); + int iIndex = CommandToIndex(idButton); + assert(-1 != iIndex); + + BOOL Succeeded = TRUE; + tString sString = szText; + std::map<tString, int>::iterator m; + int iString; + + // Check to see if the string is already added + m = m_StringMap.find(sString); + if (m_StringMap.end() == m) + { + if (0 == m_StringMap.size()) + { + // Place a blank string first in the string table, in case some + // buttons don't have text + TCHAR szString[3] = _T(" "); + szString[2] = _T('\0'); // Double-null terminate + SendMessage(TB_ADDSTRING, 0L, (LPARAM)szString); + } + + // No index for this string exists, so create it now + TCHAR szBuf[80] = _T(""); + lstrcpyn(szBuf, szText, 79); + szBuf[lstrlen(szBuf)+1] = _T('\0'); // Double-null terminate + + iString = (int)SendMessage(TB_ADDSTRING, 0L, (LPARAM)szBuf); + if (-1 == iString ) + Succeeded = FALSE; + + // Save the string its index in our map + m_StringMap.insert(std::make_pair(sString, iString)); + } + else + { + // String found, use the index from our map + iString = m->second; + } + + if (Succeeded) + { + TBBUTTON tbb = {0}; + Succeeded = (BOOL)SendMessage(TB_GETBUTTON, iIndex, (LPARAM)&tbb); + + tbb.iString = iString; + + // Turn off ToolBar drawing + SendMessage(WM_SETREDRAW, FALSE, 0L); + + if (Succeeded) + Succeeded = (BOOL)SendMessage(TB_DELETEBUTTON, iIndex, 0L); + + if (Succeeded) + Succeeded = (BOOL)SendMessage(TB_INSERTBUTTON, iIndex, (LPARAM)&tbb); + + // Ensure the button now includes some text rows + if (0 == SendMessage(TB_GETTEXTROWS, 0L, 0L)) + SendMessage(TB_SETMAXTEXTROWS, 1L, 0L); + + // Turn on ToolBar drawing + SendMessage(WM_SETREDRAW, TRUE, 0L); + } + // Redraw button + CRect r = GetItemRect(iIndex); + InvalidateRect(&r, TRUE); + + return Succeeded; + } + + inline BOOL CToolBar::SetButtonWidth(int idButton, int nWidth) const + // The set button width can adjust the width of the button after it is created. + // This is useful when replacing a button with a ComboBox or other control. + // Note: TB_SETBUTTONINFO requires comctl32.dll version 4.71 or later + // i.e. Win95 with IE4 / NT with IE4 or later + { + assert(::IsWindow(m_hWnd)); + + TBBUTTONINFO tbbi = {0}; + tbbi.cbSize = sizeof(TBBUTTONINFO); + tbbi.dwMask = TBIF_SIZE; + tbbi.cx = (WORD)nWidth; + BOOL bResult = (BOOL)SendMessage(TB_SETBUTTONINFO, (WPARAM)idButton, (LPARAM)&tbbi); + + // Send a changed message to the parent (used by the rebar) + SIZE MaxSize = GetMaxSize(); + GetParent()->SendMessage(UWM_TOOLBAR_RESIZE, (WPARAM)m_hWnd, (LPARAM)&MaxSize); + + return bResult; + } + + inline BOOL CToolBar::SetCommandID(int iIndex, int idButton) const + // Sets the command identifier of a toolbar button + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_SETCMDID, iIndex, idButton); + } + + inline HIMAGELIST CToolBar::SetDisableImageList(HIMAGELIST himlNewDisabled) const + // Sets the image list that the toolbar control will use to display disabled buttons. + { + assert(::IsWindow(m_hWnd)); + return (HIMAGELIST)SendMessage(TB_SETDISABLEDIMAGELIST, 0L, (LPARAM)himlNewDisabled); + } + + inline DWORD CToolBar::SetDrawTextFlags(DWORD dwMask, DWORD dwDTFlags) const + // Sets the text drawing flags for the toolbar. + { + assert(::IsWindow(m_hWnd)); + return (DWORD)SendMessage(TB_SETDRAWTEXTFLAGS, (WPARAM)dwMask, (LPARAM)dwDTFlags); + } + + inline DWORD CToolBar::SetExtendedStyle(DWORD dwExStyle) const + // Sets the text drawing flags for the toolbar. + // Extended styles include: TBSTYLE_EX_DRAWDDARROWS, TBSTYLE_EX_HIDECLIPPEDBUTTONS, TBSTYLE_EX_DOUBLEBUFFER and TBSTYLE_EX_MIXEDBUTTONS + { + assert(::IsWindow(m_hWnd)); + return (DWORD)SendMessage(TB_SETEXTENDEDSTYLE, 0L, (LPARAM)dwExStyle); + } + + inline HIMAGELIST CToolBar::SetHotImageList(HIMAGELIST himlNewHot) const + // Sets the image list that the toolbar control will use to display hot buttons. + { + assert(::IsWindow(m_hWnd)); + return (HIMAGELIST)SendMessage(TB_SETHOTIMAGELIST, 0L, (LPARAM)himlNewHot); + } + + inline int CToolBar::SetHotItem(int iHot) const + // Sets the hot item in a toolbar. + { + assert(::IsWindow(m_hWnd)); + return (int)SendMessage(TB_SETHOTITEM, (WPARAM)iHot, 0L); + } + + inline HIMAGELIST CToolBar::SetImageList(HIMAGELIST himlNew) const + // Sets the image list that the toolbar will use to display buttons that are in their default state. + { + assert(::IsWindow(m_hWnd)); + return (HIMAGELIST)SendMessage(TB_SETIMAGELIST, 0L, (LPARAM)himlNew); + } + + inline BOOL CToolBar::SetImages(COLORREF crMask, UINT ToolBarID, UINT ToolBarHotID, UINT ToolBarDisabledID) + // Either sets the imagelist or adds/replaces bitmap depending on ComCtl32.dll version + // Assumes the width of the button image = bitmap_size / buttons + // Assumes buttons have been already been added via AdddToolBarButton + // The colour mask is often grey RGB(192,192,192) or magenta (255,0,255); + // The color mask is ignored for 32bit bitmap resources + // The Hot and disiabled bitmap resources can be 0 + { + assert(::IsWindow(m_hWnd)); + + // ToolBar ImageLists require Comctl32.dll version 4.7 or later + if (400 == GetComCtlVersion()) + { + // We are using COMCTL32.DLL version 4.0, so we can't use an imagelist. + // Instead we simply set the bitmap. + return SetBitmap(ToolBarID); + } + + int iNumButtons = 0; + std::vector<UINT>::iterator iter; + for (iter = GetToolBarData().begin(); iter < GetToolBarData().end(); ++iter) + if ((*iter) != 0) ++iNumButtons; + + if (iNumButtons > 0) + { + // Set the button images + CBitmap Bitmap(ToolBarID); + assert(Bitmap.GetHandle()); + + BITMAP bm = Bitmap.GetBitmapData(); + int iImageWidth = bm.bmWidth / iNumButtons; + int iImageHeight = bm.bmHeight; + + HIMAGELIST himlToolBar = (HIMAGELIST)SendMessage(TB_GETIMAGELIST, 0L, 0L); + HIMAGELIST himlToolBarHot = (HIMAGELIST)SendMessage(TB_GETHOTIMAGELIST, 0L, 0L); + HIMAGELIST himlToolBarDis = (HIMAGELIST)SendMessage(TB_GETDISABLEDIMAGELIST, 0L, 0L); + ImageList_Destroy(himlToolBar); + ImageList_Destroy(himlToolBarHot); + ImageList_Destroy(himlToolBarDis); + + himlToolBar = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iNumButtons, 0); + assert(himlToolBar); + + ImageList_AddMasked(himlToolBar, Bitmap, crMask); + SendMessage(TB_SETIMAGELIST, 0L, (LPARAM)himlToolBar); + + if (ToolBarHotID) + { + CBitmap BitmapHot(ToolBarHotID); + assert(BitmapHot); + + himlToolBarHot = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iNumButtons, 0); + assert(himlToolBarHot); + + ImageList_AddMasked(himlToolBarHot, BitmapHot, crMask); + SendMessage(TB_SETHOTIMAGELIST, 0L, (LPARAM)himlToolBarHot); + } + + if (ToolBarDisabledID) + { + CBitmap BitmapDisabled(ToolBarDisabledID); + assert(BitmapDisabled); + + himlToolBarDis = ImageList_Create(iImageWidth, iImageHeight, ILC_COLOR32 | ILC_MASK, iNumButtons, 0); + assert(himlToolBarDis); + + ImageList_AddMasked(himlToolBarDis, BitmapDisabled, crMask); + SendMessage(TB_SETDISABLEDIMAGELIST, 0L, (LPARAM)himlToolBarDis); + } + else + { + himlToolBarDis = CreateDisabledImageList(himlToolBar); + SendMessage(TB_SETDISABLEDIMAGELIST, 0L, (LPARAM)himlToolBarDis); + } + + // Inform the parent of the change (rebar needs this) + SIZE MaxSize = GetMaxSize(); + GetParent()->SendMessage(UWM_TOOLBAR_RESIZE, (WPARAM)m_hWnd, (LPARAM)&MaxSize); + } + + return TRUE; + } + + inline BOOL CToolBar::SetIndent(int iIndent) const + // Sets the indentation for the first button in a toolbar control. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_SETINDENT, (WPARAM)iIndent, 0L); + } + + inline BOOL CToolBar::SetMaxTextRows(int iMaxRows) const + // Sets the maximum number of text rows displayed on a toolbar button. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_SETMAXTEXTROWS, (WPARAM)iMaxRows, 0L); + } + + inline BOOL CToolBar::SetPadding(int cx, int cy) const + // Sets the padding for a toolbar control. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)SendMessage(TB_SETPADDING, 0L, (WPARAM)MAKELONG(cx, cy)); + } + + inline void CToolBar::SetToolBarTheme(ToolBarTheme& Theme) + { + m_Theme.UseThemes = Theme.UseThemes; + m_Theme.clrHot1 = Theme.clrHot1; + m_Theme.clrHot2 = Theme.clrHot2; + m_Theme.clrPressed1 = Theme.clrPressed1; + m_Theme.clrPressed2 = Theme.clrPressed2; + m_Theme.clrOutline = Theme.clrOutline; + + if (IsWindow()) + Invalidate(); + } + + inline void CToolBar::SetToolTips(HWND hwndToolTip) const + // Associates a ToolTip control with a toolbar. + { + assert(::IsWindow(m_hWnd)); + SendMessage(TB_SETTOOLTIPS, (WPARAM)hwndToolTip, 0L); + } + + inline LRESULT CToolBar::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_DESTROY: + OnDestroy(); + break; + case UWM_GETTOOLBARTHEME: + { + ToolBarTheme& tt = GetToolBarTheme(); + return (LRESULT)&tt; + } + case WM_WINDOWPOSCHANGING: + OnWindowPosChanging(wParam, lParam); + break; + } + + // pass unhandled messages on for default processing + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + +} // namespace Win32xx + +#endif // #ifndef _WIN32XX_TOOLBAR_H_ diff --git a/mmc_updater/depends/win32cpp/treeview.h b/mmc_updater/depends/win32cpp/treeview.h new file mode 100644 index 00000000..4186e9ce --- /dev/null +++ b/mmc_updater/depends/win32cpp/treeview.h @@ -0,0 +1,624 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + + + +#ifndef _WIN32XX_TREEVIEW_H_ +#define _WIN32XX_TREEVIEW_H_ + +#include "wincore.h" +#include "commctrl.h" + +// Disable macros from Windowsx.h +#undef GetNextSibling +#undef GetPrevSibling + +namespace Win32xx +{ + + class CTreeView : public CWnd + { + public: + CTreeView() {} + virtual ~CTreeView() {} + virtual void PreRegisterClass(WNDCLASS &wc); + +// Attributes + COLORREF GetBkColor() const; + HTREEITEM GetChild(HTREEITEM hItem) const; + UINT GetCount() const; + HTREEITEM GetDropHiLightItem() const; + HWND GetEditControl() const; + HTREEITEM GetFirstVisible() const; + HIMAGELIST GetImageList(int iImageType) const; + UINT GetIndent() const; + COLORREF GetInsertMarkColor() const; + BOOL GetItem(TVITEM& Item) const; + DWORD_PTR GetItemData(HTREEITEM hItem) const; + int GetItemHeight() const; + BOOL GetItemImage(HTREEITEM hItem, int& nImage, int& nSelectedImage ) const; + BOOL GetItemRect(HTREEITEM hItem, CRect& rc, BOOL bTextOnly) const; + tString GetItemText(HTREEITEM hItem, UINT nTextMax /* = 260 */) const; + HTREEITEM GetLastVisible() const; + HTREEITEM GetNextItem(HTREEITEM hItem, UINT nCode) const; + HTREEITEM GetNextSibling(HTREEITEM hItem) const; + HTREEITEM GetNextVisible(HTREEITEM hItem) const; + HTREEITEM GetParentItem(HTREEITEM hItem) const; + HTREEITEM GetPrevSibling(HTREEITEM hItem) const; + HTREEITEM GetPrevVisible(HTREEITEM hItem) const; + HTREEITEM GetRootItem() const; + int GetScrollTime() const; + HTREEITEM GetSelection() const; + COLORREF GetTextColor() const; + HWND GetToolTips() const; + UINT GetVisibleCount() const; + BOOL ItemHasChildren(HTREEITEM hItem) const; + COLORREF SetBkColor(COLORREF clrBk) const; + HIMAGELIST SetImageList(HIMAGELIST himl, int nType) const; + void SetIndent(int indent) const; + BOOL SetInsertMark(HTREEITEM hItem, BOOL fAfter = TRUE) const; + COLORREF SetInsertMarkColor(COLORREF clrInsertMark) const; + BOOL SetItem(TVITEM& Item) const; + BOOL SetItem(HTREEITEM hItem, UINT nMask, LPCTSTR szText, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) const; + BOOL SetItemData(HTREEITEM hItem, DWORD_PTR dwData) const; + int SetItemHeight(SHORT cyItem) const; + BOOL SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage) const; + BOOL SetItemText(HTREEITEM hItem, LPCTSTR szText) const; + UINT SetScrollTime(UINT uScrollTime) const; + COLORREF SetTextColor(COLORREF clrText) const; + HWND SetToolTips(HWND hwndTooltip) const; + +// Operations + HIMAGELIST CreateDragImage(HTREEITEM hItem) const; + BOOL DeleteAllItems() const; + BOOL DeleteItem(HTREEITEM hItem) const; + HWND EditLabel(HTREEITEM hItem) const; + BOOL EndEditLabelNow(BOOL fCancel) const; + BOOL EnsureVisible(HTREEITEM hItem) const; + BOOL Expand(HTREEITEM hItem, UINT nCode) const; + HTREEITEM HitTest(TVHITTESTINFO& ht) const; + HTREEITEM InsertItem(TVINSERTSTRUCT& tvIS) const; + BOOL Select(HTREEITEM hitem, UINT flag) const; + BOOL SelectDropTarget(HTREEITEM hItem) const; + BOOL SelectItem(HTREEITEM hItem) const; + BOOL SelectSetFirstVisible(HTREEITEM hItem) const; + BOOL SortChildren(HTREEITEM hItem, BOOL fRecurse) const; + BOOL SortChildrenCB(TVSORTCB& sort, BOOL fRecurse) const; + + private: + CTreeView(const CTreeView&); // Disable copy construction + CTreeView& operator = (const CTreeView&); // Disable assignment operator + + }; + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +namespace Win32xx +{ + + inline void CTreeView::PreRegisterClass(WNDCLASS &wc) + { + // Set the Window Class + wc.lpszClassName = WC_TREEVIEW; + } + +// Attributes + inline COLORREF CTreeView::GetBkColor() const + // Retrieves the current background color of the control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetBkColor( m_hWnd ); + } + + inline HTREEITEM CTreeView::GetChild(HTREEITEM hItem) const + // Retrieves the first child item of the specified tree-view item. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetChild(m_hWnd, hItem); + } + + inline UINT CTreeView::GetCount() const + // Retrieves a count of the items in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetCount( m_hWnd ); + } + + inline HTREEITEM CTreeView::GetDropHiLightItem() const + // Retrieves the tree-view item that is the target of a drag-and-drop operation. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetDropHilight(m_hWnd); + } + + inline HWND CTreeView::GetEditControl() const + // Retrieves the handle to the edit control being used to edit a tree-view item's text. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetEditControl( m_hWnd ); + } + + inline HTREEITEM CTreeView::GetFirstVisible() const + // Retrieves the first visible item in a tree-view control window. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetFirstVisible(m_hWnd); + } + + inline HIMAGELIST CTreeView::GetImageList(int iImageType) const + // Retrieves the handle to the normal or state image list associated with a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetImageList( m_hWnd, iImageType ); + } + + inline UINT CTreeView::GetIndent() const + // Retrieves the amount, in pixels, that child items are indented relative to their parent items. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetIndent( m_hWnd ); + } + + inline COLORREF CTreeView::GetInsertMarkColor() const + // Retrieves the color used to draw the insertion mark for the tree view. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetInsertMarkColor( m_hWnd ); + } + + inline BOOL CTreeView::GetItem(TVITEM& Item) const + // Retrieves some or all of a tree-view item's attributes. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetItem( m_hWnd, &Item ); + } + + inline DWORD_PTR CTreeView::GetItemData(HTREEITEM hItem) const + // Retrieves a tree-view item's application data. + { + assert(::IsWindow(m_hWnd)); + + TVITEM tvi = {0}; + tvi.mask = TVIF_PARAM; + tvi.hItem = hItem; + TreeView_GetItem( m_hWnd, &tvi ); + return tvi.lParam; + } + + inline int CTreeView::GetItemHeight() const + // Retrieves the current height of the tree-view item. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetItemHeight( m_hWnd ); + } + + inline BOOL CTreeView::GetItemImage(HTREEITEM hItem, int& nImage, int& nSelectedImage ) const + // Retrieves the index of the tree-view item's image and selected image. + { + assert(::IsWindow(m_hWnd)); + + TVITEM tvi = {0}; + tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE; + tvi.hItem = hItem; + BOOL bResult = TreeView_GetItem( m_hWnd, &tvi ); + nImage = tvi.iImage; + nSelectedImage = tvi.iSelectedImage; + return bResult; + } + + inline BOOL CTreeView::GetItemRect(HTREEITEM hItem, CRect& rc, BOOL bTextOnly) const + // Retrieves the bounding rectangle for a tree-view item and indicates whether the item is visible. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetItemRect( m_hWnd, hItem, &rc, bTextOnly ); + } + + inline tString CTreeView::GetItemText(HTREEITEM hItem, UINT nTextMax /* = 260 */) const + // Retrieves the text for a tree-view item. + // Note: Although the tree-view control allows any length string to be stored + // as item text, only the first 260 characters are displayed. + { + assert(::IsWindow(m_hWnd)); + + tString t; + if (nTextMax > 0) + { + TVITEM tvi = {0}; + tvi.hItem = hItem; + tvi.mask = TVIF_TEXT; + tvi.cchTextMax = nTextMax; + std::vector<TCHAR> vTChar(nTextMax +1, _T('\0')); + TCHAR* pTCharArray = &vTChar.front(); + tvi.pszText = pTCharArray; + ::SendMessage(m_hWnd, TVM_GETITEM, 0L, (LPARAM)&tvi); + t = tvi.pszText; + } + return t; + } + + inline HTREEITEM CTreeView::GetLastVisible() const + // Retrieves the last expanded item in a tree-view control. + // This does not retrieve the last item visible in the tree-view window. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetLastVisible(m_hWnd); + } + + inline HTREEITEM CTreeView::GetNextItem(HTREEITEM hItem, UINT nCode) const + // Retrieves the tree-view item that bears the specified relationship to a specified item. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetNextItem( m_hWnd, hItem, nCode); + } + + inline HTREEITEM CTreeView::GetNextSibling(HTREEITEM hItem) const + // Retrieves the next sibling item of a specified item in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetNextSibling(m_hWnd, hItem); + } + + inline HTREEITEM CTreeView::GetNextVisible(HTREEITEM hItem) const + // Retrieves the next visible item that follows a specified item in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetNextVisible(m_hWnd, hItem); + } + + inline HTREEITEM CTreeView::GetParentItem(HTREEITEM hItem) const + // Retrieves the parent item of the specified tree-view item. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetParent(m_hWnd, hItem); + } + + inline HTREEITEM CTreeView::GetPrevSibling(HTREEITEM hItem) const + // Retrieves the previous sibling item of a specified item in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetPrevSibling(m_hWnd, hItem); + } + + inline HTREEITEM CTreeView::GetPrevVisible(HTREEITEM hItem) const + // Retrieves the first visible item that precedes a specified item in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetPrevSibling(m_hWnd, hItem); + } + + inline HTREEITEM CTreeView::GetRootItem() const + // Retrieves the topmost or very first item of the tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetRoot(m_hWnd); + } + + inline int CTreeView::GetScrollTime() const + // Retrieves the maximum scroll time for the tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetScrollTime( m_hWnd ); + } + + inline HTREEITEM CTreeView::GetSelection() const + // Retrieves the currently selected item in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetSelection(m_hWnd); + } + + inline COLORREF CTreeView::GetTextColor() const + // Retrieves the current text color of the control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetTextColor( m_hWnd ); + } + + inline HWND CTreeView::GetToolTips() const + // Retrieves the handle to the child ToolTip control used by a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetToolTips( m_hWnd ); + } + + inline UINT CTreeView::GetVisibleCount() const + // Obtains the number of items that can be fully visible in the client window of a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_GetVisibleCount( m_hWnd ); + } + + inline BOOL CTreeView::ItemHasChildren(HTREEITEM hItem) const + // Returns true of the tree-view item has one or more children + { + assert(::IsWindow(m_hWnd)); + + if (TreeView_GetChild( m_hWnd, hItem )) + return TRUE; + + return FALSE; + } + + inline COLORREF CTreeView::SetBkColor(COLORREF clrBk) const + // Sets the background color of the control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetBkColor( m_hWnd, clrBk ); + } + + inline HIMAGELIST CTreeView::SetImageList(HIMAGELIST himl, int nType) const + // Sets the normal or state image list for a tree-view control + // and redraws the control using the new images. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetImageList( m_hWnd, himl, nType ); + } + + inline void CTreeView::SetIndent(int indent) const + // Sets the width of indentation for a tree-view control + // and redraws the control to reflect the new width. + { + assert(::IsWindow(m_hWnd)); + TreeView_SetIndent( m_hWnd, indent ); + } + + inline BOOL CTreeView::SetInsertMark(HTREEITEM hItem, BOOL fAfter/* = TRUE*/) const + // Sets the insertion mark in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetInsertMark( m_hWnd, hItem, fAfter ); + } + + inline COLORREF CTreeView::SetInsertMarkColor(COLORREF clrInsertMark) const + // Sets the color used to draw the insertion mark for the tree view. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetInsertMarkColor( m_hWnd, clrInsertMark ); + } + + inline BOOL CTreeView::SetItem(TVITEM& Item) const + // Sets some or all of a tree-view item's attributes. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetItem( m_hWnd, &Item ); + } + + inline BOOL CTreeView::SetItem(HTREEITEM hItem, UINT nMask, LPCTSTR szText, int nImage, int nSelectedImage, UINT nState, UINT nStateMask, LPARAM lParam) const + // Sets some or all of a tree-view item's attributes. + { + assert(::IsWindow(m_hWnd)); + + TVITEM tvi = {0}; + tvi.hItem = hItem; + tvi.mask = nMask; + tvi.pszText = (LPTSTR)szText; + tvi.iImage = nImage; + tvi.iSelectedImage = nSelectedImage; + tvi.state = nState; + tvi.stateMask = nStateMask; + tvi.lParam = lParam; + return TreeView_SetItem( m_hWnd, &tvi ); + } + + inline BOOL CTreeView::SetItemData(HTREEITEM hItem, DWORD_PTR dwData) const + // Sets the tree-view item's application data. + { + assert(::IsWindow(m_hWnd)); + + TVITEM tvi = {0}; + tvi.hItem = hItem; + tvi.mask = TVIF_PARAM; + tvi.lParam = dwData; + return TreeView_SetItem( m_hWnd, &tvi ); + } + + inline int CTreeView::SetItemHeight(SHORT cyItem) const + // Sets the height of the tree-view items. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetItemHeight( m_hWnd, cyItem ); + } + + inline BOOL CTreeView::SetItemImage(HTREEITEM hItem, int nImage, int nSelectedImage) const + // Sets the tree-view item's application image. + { + assert(::IsWindow(m_hWnd)); + + TVITEM tvi = {0}; + tvi.hItem = hItem; + tvi.iImage = nImage; + tvi.iSelectedImage = nSelectedImage; + tvi.mask = TVIF_IMAGE | TVIF_SELECTEDIMAGE; + return TreeView_SetItem(m_hWnd, &tvi ); + } + + inline BOOL CTreeView::SetItemText(HTREEITEM hItem, LPCTSTR szText) const + // Sets the tree-view item's application text. + { + assert(::IsWindow(m_hWnd)); + + TVITEM tvi = {0}; + tvi.hItem = hItem; + tvi.pszText = (LPTSTR)szText; + tvi.mask = TVIF_TEXT; + return TreeView_SetItem(m_hWnd, &tvi ); + } + + inline UINT CTreeView::SetScrollTime(UINT uScrollTime) const + // Sets the maximum scroll time for the tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetScrollTime( m_hWnd, uScrollTime ); + } + + inline COLORREF CTreeView::SetTextColor(COLORREF clrText) const + // Sets the text color of the control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetTextColor( m_hWnd, clrText ); + } + + inline HWND CTreeView::SetToolTips(HWND hwndTooltip) const + // Sets a tree-view control's child ToolTip control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SetToolTips( m_hWnd, hwndTooltip ); + } + + // Operations + + inline HIMAGELIST CTreeView::CreateDragImage(HTREEITEM hItem) const + // Creates a dragging bitmap for the specified item in a tree-view control. + // It also creates an image list for the bitmap and adds the bitmap to the image list. + // An application can display the image when dragging the item by using the image list functions. + { + assert(::IsWindow(m_hWnd)); + return TreeView_CreateDragImage( m_hWnd, hItem ); + } + + inline BOOL CTreeView::DeleteAllItems() const + // Deletes all items from a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_DeleteAllItems( m_hWnd ); + } + + inline BOOL CTreeView::DeleteItem(HTREEITEM hItem) const + // Removes an item and all its children from a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_DeleteItem( m_hWnd, hItem ); + } + + inline HWND CTreeView::EditLabel(HTREEITEM hItem) const + // Begins in-place editing of the specified item's text, replacing the text of the item + // with a single-line edit control containing the text. + // The specified item is implicitly selected and focused. + { + assert(::IsWindow(m_hWnd)); + return TreeView_EditLabel( m_hWnd, hItem ); + } + + inline BOOL CTreeView::EndEditLabelNow(BOOL fCancel) const + // Ends the editing of a tree-view item's label. + { + assert(::IsWindow(m_hWnd)); + return TreeView_EndEditLabelNow(m_hWnd, fCancel); + } + + inline BOOL CTreeView::EnsureVisible(HTREEITEM hItem) const + // Ensures that a tree-view item is visible, expanding the parent item or + // scrolling the tree-view control, if necessary. + { + assert(::IsWindow(m_hWnd)); + return TreeView_EnsureVisible( m_hWnd, hItem ); + } + + inline BOOL CTreeView::Expand(HTREEITEM hItem, UINT nCode) const + // The TreeView_Expand macro expands or collapses the list of child items associated + // with the specified parent item, if any. + { + assert(::IsWindow(m_hWnd)); + return TreeView_Expand( m_hWnd, hItem, nCode ); + } + + inline HTREEITEM CTreeView::HitTest(TVHITTESTINFO& ht) const + // Determines the location of the specified point relative to the client area of a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_HitTest( m_hWnd, &ht ); + } + + inline HTREEITEM CTreeView::InsertItem(TVINSERTSTRUCT& tvIS) const + // Inserts a new item in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_InsertItem( m_hWnd, &tvIS ); + } + + inline BOOL CTreeView::Select(HTREEITEM hitem, UINT flag) const + // Selects the specified tree-view item, scrolls the item into view, or redraws + // the item in the style used to indicate the target of a drag-and-drop operation. + { + assert(::IsWindow(m_hWnd)); + return TreeView_Select(m_hWnd, hitem, flag ); + } + + inline BOOL CTreeView::SelectDropTarget(HTREEITEM hItem) const + // Redraws a specified tree-view control item in the style used to indicate the + // target of a drag-and-drop operation. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SelectDropTarget(m_hWnd, hItem); + } + + inline BOOL CTreeView::SelectItem(HTREEITEM hItem) const + // Selects the specified tree-view item. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SelectItem(m_hWnd, hItem); + } + + inline BOOL CTreeView::SelectSetFirstVisible(HTREEITEM hItem) const + // Scrolls the tree-view control vertically to ensure that the specified item is visible. + // If possible, the specified item becomes the first visible item at the top of the control's window. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SelectSetFirstVisible(m_hWnd, hItem); + } + + inline BOOL CTreeView::SortChildren(HTREEITEM hItem, BOOL fRecurse) const + // Sorts the child items of the specified parent item in a tree-view control. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SortChildren( m_hWnd, hItem, fRecurse ); + } + + inline BOOL CTreeView::SortChildrenCB(TVSORTCB& sort, BOOL fRecurse) const + // Sorts tree-view items using an application-defined callback function that compares the items. + { + assert(::IsWindow(m_hWnd)); + return TreeView_SortChildrenCB( m_hWnd, &sort, fRecurse ); + } + + +} // namespace Win32xx + +#endif // #ifndef _WIN32XX_TREEVIEW_H_ + diff --git a/mmc_updater/depends/win32cpp/wceframe.h b/mmc_updater/depends/win32cpp/wceframe.h new file mode 100644 index 00000000..f3aa67ef --- /dev/null +++ b/mmc_updater/depends/win32cpp/wceframe.h @@ -0,0 +1,420 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +////////////////////////////////////////////////////// +// WceFrame.h +// Definitions for the CCmdBar and CWceFrame + +// These classes are provide a frame window for use on Window CE devices such +// as Pocket PCs. The frame uses CommandBar (a control unique to the Windows CE +// operating systems) to display the menu and toolbar. +// +// Use the PocketPCWceFrame generic application as the starting point for your own +// frame based applications on the Pocket PC. +// +// Refer to the Scribble demo application for an example of how these classes +// can be used. + + +#ifndef _WIN32XX_WCEFRAME_H_ +#define _WIN32XX_WCEFRAME_H_ + + +#include "wincore.h" +#include <commctrl.h> +#include <vector> +#include "default_resource.h" + +#if defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP) + #define SHELL_AYGSHELL +#endif + +#ifdef SHELL_AYGSHELL + #include <aygshell.h> + #pragma comment(lib, "aygshell.lib") +#endif // SHELL_AYGSHELL + +#if (_WIN32_WCE < 0x500 && defined(SHELL_AYGSHELL)) || _WIN32_WCE == 420 + #pragma comment(lib, "ccrtrtti.lib") +#endif + + +namespace Win32xx +{ + + //////////////////////////////////// + // Declaration of the CCmdBar class + // + class CCmdBar : public CWnd + { + public: + CCmdBar(); + virtual ~CCmdBar(); + virtual BOOL AddAdornments(DWORD dwFlags); + virtual int AddBitmap(int idBitmap, int iNumImages, int iImageWidth, int iImageHeight); + virtual BOOL AddButtons(int nButtons, TBBUTTON* pTBButton); + virtual HWND Create(HWND hwndParent); + virtual int GetHeight() const; + virtual HWND InsertComboBox(int iWidth, UINT dwStyle, WORD idComboBox, WORD iButton); + virtual BOOL IsVisible(); + virtual BOOL Show(BOOL fShow); + + private: + +#ifdef SHELL_AYGSHELL + SHMENUBARINFO m_mbi; +#endif + + }; + + + ////////////////////////////////////// + // Declaration of the CWceFrame class + // A mini frame based on CCmdBar + class CWceFrame : public CWnd + { + public: + CWceFrame(); + virtual ~CWceFrame(); + virtual void AddToolBarButton(UINT nID); + CRect GetViewRect() const; + CCmdBar& GetMenuBar() const {return (CCmdBar&)m_MenuBar;} + virtual void OnActivate(WPARAM wParam, LPARAM lParam); + virtual void OnCreate(); + virtual void PreCreate(CREATESTRUCT &cs); + virtual void RecalcLayout(); + virtual void SetButtons(const std::vector<UINT> ToolBarData); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + protected: + std::vector<UINT> m_ToolBarData; + + private: + CCmdBar m_MenuBar; + tString m_tsAppName; + +#ifdef SHELL_AYGSHELL + SHACTIVATEINFO m_sai; +#endif + + }; + + ////////////////////////////////////////// + // Definitions for the CCmdBar class + // This class wraps CommandBar_Create which + // creates a CommandBar at the top of the window + inline CCmdBar::CCmdBar() + { + } + + inline CCmdBar::~CCmdBar() + { + if (IsWindow()) + ::CommandBar_Destroy(m_hWnd); + } + + + inline BOOL CCmdBar::AddAdornments(DWORD dwFlags) + { + BOOL bReturn = CommandBar_AddAdornments(m_hWnd, dwFlags, 0); + + if (!bReturn) + throw CWinException(_T("AddAdornments failed")); + + return bReturn; + } + + inline int CCmdBar::AddBitmap(int idBitmap, int iNumImages, int iImageWidth, int iImageHeight) + { + HINSTANCE hInst = GetApp()->GetInstanceHandle(); + return CommandBar_AddBitmap(m_hWnd, hInst, idBitmap, iNumImages, iImageWidth, iImageHeight); + } + + inline BOOL CCmdBar::AddButtons(int nButtons, TBBUTTON* pTBButton) + { + BOOL bReturn = CommandBar_AddButtons(m_hWnd, nButtons, pTBButton); + if (!bReturn) + throw CWinException(_T("Failed to add buttons to commandbar")); + + return bReturn; + } + + inline HWND CCmdBar::Create(HWND hParent) + { +#ifdef SHELL_AYGSHELL + SHMENUBARINFO mbi; + + memset(&mbi, 0, sizeof(SHMENUBARINFO)); + mbi.cbSize = sizeof(SHMENUBARINFO); + mbi.hwndParent = hParent; + mbi.nToolBarId = IDW_MAIN; + mbi.hInstRes = GetApp()->GetInstanceHandle(); + mbi.nBmpId = 0; + mbi.cBmpImages = 0; + + if (SHCreateMenuBar(&mbi)) + { + m_hWnd = mbi.hwndMB; + } + else + throw CWinException(_T("Failed to create MenuBar")); + +#else + m_hWnd = CommandBar_Create(GetApp()->GetInstanceHandle(), hParent, IDW_MENUBAR); + + if (m_hWnd == NULL) + throw CWinException(_T("Failed to create CommandBar")); + + CommandBar_InsertMenubar(m_hWnd, GetApp()->GetInstanceHandle(), IDW_MAIN, 0); +#endif + return m_hWnd; + } + + inline int CCmdBar::GetHeight() const + { + return CommandBar_Height(m_hWnd); + } + + inline HWND CCmdBar::InsertComboBox(int iWidth, UINT dwStyle, WORD idComboBox, WORD iButton) + { + HINSTANCE hInst = GetApp()->GetInstanceHandle(); + HWND hWnd = CommandBar_InsertComboBox(m_hWnd, hInst, iWidth, dwStyle, idComboBox, iButton); + + if (!hWnd) + throw CWinException(_T("InsertComboBox failed")); + + return hWnd; + } + + inline BOOL CCmdBar::IsVisible() + { + return ::CommandBar_IsVisible(m_hWnd); + } + + inline BOOL CCmdBar::Show(BOOL fShow) + { + return ::CommandBar_Show(m_hWnd, fShow); + } + + + ///////////////////////////////////////// + // Definitions for the CWceFrame class + // This class creates a simple frame using CCmdBar + inline CWceFrame::CWceFrame() + { +#ifdef SHELL_AYGSHELL + // Initialize the shell activate info structure + memset (&m_sai, 0, sizeof (m_sai)); + m_sai.cbSize = sizeof (m_sai); +#endif + } + + inline CWceFrame::~CWceFrame() + { + } + + inline void CWceFrame::AddToolBarButton(UINT nID) + // Adds Resource IDs to toolbar buttons. + // A resource ID of 0 is a separator + { + m_ToolBarData.push_back(nID); + } + + inline CRect CWceFrame::GetViewRect() const + { + CRect r; + ::GetClientRect(m_hWnd, &r); + +#ifndef SHELL_AYGSHELL + // Reduce the size of the client rectange, by the commandbar height + r.top += m_MenuBar.GetHeight(); +#endif + + return r; + } + + inline void CWceFrame::OnCreate() + { + // Create the Commandbar + m_MenuBar.Create(m_hWnd); + + // Set the keyboard accelerators + HACCEL hAccel = LoadAccelerators(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_MAIN)); + GetApp()->SetAccelerators(hAccel, this); + + // Add the toolbar buttons + if (m_ToolBarData.size() > 0) + SetButtons(m_ToolBarData); + +#ifndef SHELL_AYGSHELL + // Add close button + m_MenuBar.AddAdornments(0); +#endif + + } + + inline void CWceFrame::OnActivate(WPARAM wParam, LPARAM lParam) + { +#ifdef SHELL_AYGSHELL + // Notify shell of our activate message + SHHandleWMActivate(m_hWnd, wParam, lParam, &m_sai, FALSE); + + UINT fActive = LOWORD(wParam); + if ((fActive == WA_ACTIVE) || (fActive == WA_CLICKACTIVE)) + { + // Reposition the window when it's activated + RecalcLayout(); + } +#endif + } + + inline void CWceFrame::PreCreate(CREATESTRUCT &cs) + { + cs.style = WS_VISIBLE; + m_tsAppName = _T("Win32++ Application"); + + // Choose a unique class name for this app + if (LoadString(IDW_MAIN) != _T("")) + { + m_tsAppName = LoadString(IDW_MAIN); + } + + cs.lpszClass = m_tsAppName.c_str(); + } + +/* inline BOOL CWceFrame::PreTranslateMessage(MSG* pMsg) + { + HACCEL hAccelTable = ::LoadAccelerators(GetApp()->GetResourceHandle(), MAKEINTRESOURCE(IDW_MAIN)); + if (WM_KEYFIRST <= pMsg->message && pMsg->message <= WM_KEYLAST) + { + if (TranslateAccelerator(m_hWnd, hAccelTable, pMsg)) + return TRUE; + } + return CWnd::PreTranslateMessage(pMsg); + } */ + + inline void CWceFrame::RecalcLayout() + { + HWND hwndCB = m_MenuBar.GetHwnd(); + if (hwndCB) + { + CRect rc; // Desktop window size + CRect rcMenuBar; // MenuBar window size + + ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0); + ::GetWindowRect(hwndCB, &rcMenuBar); + rc.bottom -= (rcMenuBar.bottom - rcMenuBar.top); + + MoveWindow(rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, FALSE); + } + + ShowWindow(TRUE); + UpdateWindow(); + } + + inline void CWceFrame::SetButtons(const std::vector<UINT> ToolBarData) + // Define the resource IDs for the toolbar like this in the Frame's constructor + // m_ToolBarData.push_back ( 0 ); // Separator + // m_ToolBarData.clear(); + // m_ToolBarData.push_back ( IDM_FILE_NEW ); + // m_ToolBarData.push_back ( IDM_FILE_OPEN ); + // m_ToolBarData.push_back ( IDM_FILE_SAVE ); + + { + int iImages = 0; + int iNumButtons = (int)ToolBarData.size(); + + + if (iNumButtons > 0) + { + // Create the TBBUTTON array for each button + std::vector<TBBUTTON> vTBB(iNumButtons); + TBBUTTON* tbbArray = &vTBB.front(); + + for (int j = 0 ; j < iNumButtons; j++) + { + ZeroMemory(&tbbArray[j], sizeof(TBBUTTON)); + + if (ToolBarData[j] == 0) + { + tbbArray[j].fsStyle = TBSTYLE_SEP; + } + else + { + tbbArray[j].iBitmap = iImages++; + tbbArray[j].idCommand = ToolBarData[j]; + tbbArray[j].fsState = TBSTATE_ENABLED; + tbbArray[j].fsStyle = TBSTYLE_BUTTON; + tbbArray[j].iString = -1; + } + } + + // Add the bitmap + GetMenuBar().AddBitmap(IDW_MAIN, iImages , 16, 16); + + // Add the buttons + GetMenuBar().AddButtons(iNumButtons, tbbArray); + } + } + + inline LRESULT CWceFrame::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch (uMsg) + { + case WM_DESTROY: + PostQuitMessage(0); + break; + case WM_ACTIVATE: + OnActivate(wParam, lParam); + break; + +#ifdef SHELL_AYGSHELL + + case WM_SETTINGCHANGE: + SHHandleWMSettingChange(m_hWnd, wParam, lParam, &m_sai); + break; +#endif + + } + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + + +} // namespace Win32xx + +#endif // _WIN32XX_WCEFRAME_H_ + diff --git a/mmc_updater/depends/win32cpp/wcestddef.h b/mmc_updater/depends/win32cpp/wcestddef.h new file mode 100644 index 00000000..f7b22833 --- /dev/null +++ b/mmc_updater/depends/win32cpp/wcestddef.h @@ -0,0 +1,58 @@ + +#pragma once + +#pragma comment(linker, "/nodefaultlib:libc.lib") +#pragma comment(linker, "/nodefaultlib:libcd.lib") + + +#include <ceconfig.h> +#if defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP) + #define SHELL_AYGSHELL +#endif + +#ifdef _CE_DCOM + #define _ATL_APARTMENT_THREADED +#endif + +#if defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP) + #ifndef _DEVICE_RESOLUTION_AWARE + #define _DEVICE_RESOLUTION_AWARE + #endif +#endif + + +#if _WIN32_WCE == 420 || _WIN32_WCE == 0x420 + // For Pocket PC 2003 + #pragma comment(lib, "ccrtrtti.lib") +#endif + +#if _MSC_VER >= 1300 + + // NOTE - this value is not strongly correlated to the Windows CE OS version being targeted + #undef WINVER + #define WINVER _WIN32_WCE + + #ifdef _DEVICE_RESOLUTION_AWARE + #include "DeviceResolutionAware.h" + #endif + + #if _WIN32_WCE < 0x500 && ( defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP) ) + #ifdef _X86_ + #if defined(_DEBUG) + #pragma comment(lib, "libcmtx86d.lib") + #else + #pragma comment(lib, "libcmtx86.lib") + #endif + #endif + #endif + + #include <altcecrt.h> + +#endif// _MSC_VER >= 1300 + +#ifdef SHELL_AYGSHELL + #include <aygshell.h> + #pragma comment(lib, "aygshell.lib") +#endif // SHELL_AYGSHELL + +// TODO: reference additional headers your program requires here diff --git a/mmc_updater/depends/win32cpp/webbrowser.h b/mmc_updater/depends/win32cpp/webbrowser.h new file mode 100644 index 00000000..5a5b5f41 --- /dev/null +++ b/mmc_updater/depends/win32cpp/webbrowser.h @@ -0,0 +1,760 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + +#ifndef _WIN32XX_WEBBROWSER_H_ +#define _WIN32XX_WEBBROWSER_H_ + +#include <exdisp.h> +#include <ocidl.h> + + +namespace Win32xx +{ + /////////////////////////////////////////////////// + // Declaration of the CAXWindow class + // This class implements an ActiveX control container + class CAXWindow : public IOleClientSite, public IOleInPlaceSite, public IOleInPlaceFrame, + public IOleControlSite, public IDispatch + { + public: + CAXWindow(); + virtual ~CAXWindow(); + virtual void Activate(BOOL fFocus); + virtual void CreateControl(BSTR bstrClsid); + virtual void CreateControl(CLSID clsid); + virtual void Remove(); + virtual void SetParent(HWND hWndParent); + virtual void SetLocation(int x, int y, int width, int height); + virtual void SetVisible(BOOL fVisible); + virtual void SetStatusWindow(HWND hWndStatus); + virtual void TranslateKey(MSG msg); + IDispatch* GetDispatch(); + IUnknown* GetUnknown(); + + // IUnknown Methods + STDMETHODIMP QueryInterface(REFIID riid, void** ppvObject); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IOleClientSite Methods + STDMETHODIMP SaveObject(); + STDMETHODIMP GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMk); + STDMETHODIMP GetContainer(LPOLECONTAINER* ppContainer); + STDMETHODIMP ShowObject(); + STDMETHODIMP OnShowWindow(BOOL fShow); + STDMETHODIMP RequestNewObjectLayout(); + + // IOleWindow Methods + STDMETHODIMP GetWindow(HWND* phwnd); + STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); + + // IOleInPlaceSite Methods + STDMETHODIMP CanInPlaceActivate(); + STDMETHODIMP OnInPlaceActivate(); + STDMETHODIMP OnUIActivate(); + STDMETHODIMP GetWindowContext(IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo); + STDMETHODIMP Scroll(SIZE scrollExtent); + STDMETHODIMP OnUIDeactivate(BOOL fUndoable); + STDMETHODIMP OnInPlaceDeactivate(); + STDMETHODIMP DiscardUndoState(); + STDMETHODIMP DeactivateAndUndo(); + STDMETHODIMP OnPosRectChange(LPCRECT lprcPosRect); + + // IOleInPlaceUIWindow Methods + STDMETHODIMP GetBorder(LPRECT lprectBorder); + STDMETHODIMP RequestBorderSpace(LPCBORDERWIDTHS lpborderwidths); + STDMETHODIMP SetBorderSpace(LPCBORDERWIDTHS lpborderwidths); + STDMETHODIMP SetActiveObject(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR lpszObjName); + + // IOleInPlaceFrame Methods + STDMETHODIMP InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths); + STDMETHODIMP SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject); + STDMETHODIMP RemoveMenus(HMENU hmenuShared); + STDMETHODIMP SetStatusText(LPCOLESTR pszStatusText); + STDMETHODIMP EnableModeless(BOOL fEnable); + STDMETHODIMP TranslateAccelerator(LPMSG lpmsg, WORD wID); + + // IOleControlSite Methods + STDMETHODIMP OnControlInfoChanged(); + STDMETHODIMP LockInPlaceActive(BOOL fLock); + STDMETHODIMP GetExtendedControl(IDispatch** ppDisp); + STDMETHODIMP TransformCoords(POINTL* pptlHimetric, POINTF* pptfContainer, DWORD dwFlags); + STDMETHODIMP TranslateAccelerator(LPMSG pMsg, DWORD grfModifiers); + STDMETHODIMP OnFocus(BOOL fGotFocus); + STDMETHODIMP ShowPropertyFrame(); + + // IDispatch Methods + STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, unsigned int cNames, LCID lcid, DISPID* rgdispid); + STDMETHODIMP GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo** pptinfo); + STDMETHODIMP GetTypeInfoCount(unsigned int* pctinfo); + STDMETHODIMP Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexecinfo, unsigned int* puArgErr); + + private: + ULONG m_cRefs; // ref count + HWND m_hWnd; // window handle of the container + HWND m_hWndStatus; // status window handle + IUnknown* m_pUnk; // IUnknown of contained object + CRect m_rcControl; // size of control + }; + + + /////////////////////////////////////////////// + // Declaration of the CWebBrowser class + // This class uses an AciveX Container provided by + // CAXWindow to host the IWebBrower2 interface. + class CWebBrowser : public CWnd + { + public: + CWebBrowser(); + virtual ~CWebBrowser(); + virtual void AddWebBrowserControl(void); + virtual CAXWindow& GetAXWindow() const { return (CAXWindow&)m_AXContainer; } + virtual IWebBrowser2* GetIWebBrowser2() const { return m_pIWebBrowser2; } + virtual void Navigate(LPCTSTR str); + + protected: + virtual void OnCreate(); + virtual void OnSize(int width, int height); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + private: + CAXWindow m_AXContainer; // The ActiveX Container + IWebBrowser2* m_pIWebBrowser2;// Interface to the ActiveX web browser control + }; + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +namespace Win32xx +{ + ///////////////////////////////////////// + // Definitions for the CAXWindow class + // + inline CAXWindow::CAXWindow() : m_cRefs(1), m_hWnd(NULL), m_pUnk(NULL) + { + } + + inline CAXWindow::~CAXWindow() + { + } + + inline void CAXWindow::CreateControl(BSTR bstrClsid) + { + CLSID clsid; + CLSIDFromString(bstrClsid, &clsid); + CreateControl(clsid); + } + + inline void CAXWindow::Activate(BOOL fFocus) + { + if (!m_pUnk) + return; + + if (fFocus) + { + IOleObject* pioo; + HRESULT hr = m_pUnk->QueryInterface(IID_IOleObject, (void**)&pioo); + if (FAILED(hr)) + return; + + pioo->DoVerb(OLEIVERB_UIACTIVATE, NULL, this, 0, m_hWnd, &m_rcControl); + pioo->Release(); + } + } + + inline void CAXWindow::CreateControl(CLSID clsid) + { + CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_IUnknown, (void**)&m_pUnk); + + if (!m_pUnk) + return; + + IOleObject* pioo; + HRESULT hr = m_pUnk->QueryInterface(IID_IOleObject, (void**)&pioo); + if (FAILED(hr)) + return; + + pioo->SetClientSite(this); + pioo->Release(); + + IPersistStreamInit* ppsi; + hr = m_pUnk->QueryInterface(IID_IPersistStreamInit, (void**)&ppsi); + if (SUCCEEDED(hr)) + { + ppsi->InitNew(); + ppsi->Release(); + } + } + + inline STDMETHODIMP_(ULONG) CAXWindow::AddRef() + { + return ++m_cRefs; + } + + inline STDMETHODIMP CAXWindow::CanInPlaceActivate() + { + return S_OK; + } + + inline STDMETHODIMP CAXWindow::ContextSensitiveHelp(BOOL fEnterMode) + { + UNREFERENCED_PARAMETER(fEnterMode); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::DeactivateAndUndo() + { + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::DiscardUndoState() + { + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::EnableModeless(BOOL fEnable) + { + UNREFERENCED_PARAMETER(fEnable); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::GetBorder(LPRECT lprectBorder) + { + UNREFERENCED_PARAMETER(lprectBorder); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::GetContainer(LPOLECONTAINER* ppContainer) + { + UNREFERENCED_PARAMETER(ppContainer); + return E_NOINTERFACE; + } + + inline IDispatch* CAXWindow::GetDispatch() + { + if (!m_pUnk) + return NULL; + + HRESULT hr; + IDispatch* pdisp; + + hr = m_pUnk->QueryInterface(IID_IDispatch, (void**)&pdisp); + return pdisp; + } + + inline STDMETHODIMP CAXWindow::GetExtendedControl(IDispatch** ppDisp) + { + if (ppDisp == NULL) + return E_INVALIDARG; + + *ppDisp = (IDispatch*)this; + (*ppDisp)->AddRef(); + + return S_OK; + } + + inline STDMETHODIMP CAXWindow::GetIDsOfNames(REFIID riid, OLECHAR** rgszNames, unsigned int cNames, LCID lcid, DISPID* rgdispid) + { + UNREFERENCED_PARAMETER((IID)riid); // IID cast required for the MinGW compiler + UNREFERENCED_PARAMETER(rgszNames); + UNREFERENCED_PARAMETER(cNames); + UNREFERENCED_PARAMETER(lcid); + + *rgdispid = DISPID_UNKNOWN; + return DISP_E_UNKNOWNNAME; + } + + inline STDMETHODIMP CAXWindow::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, LPMONIKER* ppMk) + { + UNREFERENCED_PARAMETER(dwAssign); + UNREFERENCED_PARAMETER(dwWhichMoniker); + UNREFERENCED_PARAMETER(ppMk); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo** pptinfo) + { + UNREFERENCED_PARAMETER(itinfo); + UNREFERENCED_PARAMETER(lcid); + UNREFERENCED_PARAMETER(pptinfo); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::GetTypeInfoCount(unsigned int* pctinfo) + { + UNREFERENCED_PARAMETER(pctinfo); + return E_NOTIMPL; + } + + inline IUnknown* CAXWindow::GetUnknown() + { + if (!m_pUnk) + return NULL; + + m_pUnk->AddRef(); + return m_pUnk; + } + + inline STDMETHODIMP CAXWindow::GetWindow(HWND* lphwnd) + { + if (!IsWindow(m_hWnd)) + return S_FALSE; + + *lphwnd = m_hWnd; + return S_OK; + } + + inline STDMETHODIMP CAXWindow::GetWindowContext (IOleInPlaceFrame** ppFrame, IOleInPlaceUIWindow** ppIIPUIWin, + LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo) + { + *ppFrame = (IOleInPlaceFrame*)this; + *ppIIPUIWin = NULL; + + RECT rect; + GetClientRect(m_hWnd, &rect); + lprcPosRect->left = 0; + lprcPosRect->top = 0; + lprcPosRect->right = rect.right; + lprcPosRect->bottom = rect.bottom; + + CopyRect(lprcClipRect, lprcPosRect); + + lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO); + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->hwndFrame = m_hWnd; + lpFrameInfo->haccel = 0; + lpFrameInfo->cAccelEntries = 0; + + (*ppFrame)->AddRef(); + return S_OK; + } + + inline STDMETHODIMP CAXWindow::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) + { + UNREFERENCED_PARAMETER(hmenuShared); + UNREFERENCED_PARAMETER(lpMenuWidths); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pdispparams, VARIANT* pvarResult, EXCEPINFO* pexecinfo, unsigned int* puArgErr) + { + UNREFERENCED_PARAMETER(dispid); + UNREFERENCED_PARAMETER((IID)riid); // IID cast required for the MinGW compiler + UNREFERENCED_PARAMETER(lcid); + UNREFERENCED_PARAMETER(wFlags); + UNREFERENCED_PARAMETER(pdispparams); + UNREFERENCED_PARAMETER(pvarResult); + UNREFERENCED_PARAMETER(pexecinfo); + UNREFERENCED_PARAMETER(puArgErr); + return DISP_E_MEMBERNOTFOUND; + } + + inline STDMETHODIMP CAXWindow::LockInPlaceActive(BOOL fLock) + { + UNREFERENCED_PARAMETER(fLock); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::OnControlInfoChanged() + { + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::OnFocus(BOOL fGotFocus) + { + UNREFERENCED_PARAMETER(fGotFocus); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::OnInPlaceActivate() + { + return S_OK; + } + + inline STDMETHODIMP CAXWindow::OnInPlaceDeactivate() + { + return S_OK; + } + + inline STDMETHODIMP CAXWindow::OnPosRectChange(LPCRECT lprcPosRect) + { + UNREFERENCED_PARAMETER(lprcPosRect); + return S_OK; + } + + inline STDMETHODIMP CAXWindow::OnShowWindow(BOOL fShow) + { + UNREFERENCED_PARAMETER(fShow); + return S_OK; + } + + inline STDMETHODIMP CAXWindow::OnUIActivate() + { + return S_OK; + } + + inline STDMETHODIMP CAXWindow::OnUIDeactivate(BOOL fUndoable) + { + UNREFERENCED_PARAMETER(fUndoable); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::QueryInterface(REFIID riid, void** ppvObject) + { + if (!ppvObject) + return E_POINTER; + + if (IsEqualIID(riid, IID_IOleClientSite)) + *ppvObject = (IOleClientSite*)this; + else if (IsEqualIID(riid, IID_IOleInPlaceSite)) + *ppvObject = (IOleInPlaceSite*)this; + else if (IsEqualIID(riid, IID_IOleInPlaceFrame)) + *ppvObject = (IOleInPlaceFrame*)this; + else if (IsEqualIID(riid, IID_IOleInPlaceUIWindow)) + *ppvObject = (IOleInPlaceUIWindow*)this; + else if (IsEqualIID(riid, IID_IOleControlSite)) + *ppvObject = (IOleControlSite*)this; + else if (IsEqualIID(riid, IID_IOleWindow)) + *ppvObject = this; + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch*)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + + inline STDMETHODIMP_(ULONG) CAXWindow::Release() + { + return --m_cRefs; + } + + inline void CAXWindow::Remove() + { + if (!m_pUnk) + return; + + IOleObject* pioo; + HRESULT hr = m_pUnk->QueryInterface(IID_IOleObject, (void**)&pioo); + if (SUCCEEDED(hr)) + { + pioo->Close(OLECLOSE_NOSAVE); + pioo->SetClientSite(NULL); + pioo->Release(); + } + + IOleInPlaceObject* pipo; + hr = m_pUnk->QueryInterface(IID_IOleInPlaceObject, (void**)&pipo); + if (SUCCEEDED(hr)) + { + pipo->UIDeactivate(); + pipo->InPlaceDeactivate(); + pipo->Release(); + } + + m_pUnk->Release(); + m_pUnk = NULL; + } + + inline STDMETHODIMP CAXWindow::RemoveMenus(HMENU hmenuShared) + { + UNREFERENCED_PARAMETER(hmenuShared); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::RequestBorderSpace(LPCBORDERWIDTHS lpborderwidths) + { + UNREFERENCED_PARAMETER(lpborderwidths); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::RequestNewObjectLayout() + { + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::SaveObject() + { + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::Scroll(SIZE scrollExtent) + { + UNREFERENCED_PARAMETER(scrollExtent); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::SetActiveObject(IOleInPlaceActiveObject* pActiveObject, LPCOLESTR lpszObjName) + { + UNREFERENCED_PARAMETER(pActiveObject); + UNREFERENCED_PARAMETER(lpszObjName); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::SetBorderSpace(LPCBORDERWIDTHS lpborderwidths) + { + UNREFERENCED_PARAMETER(lpborderwidths); + return E_NOTIMPL; + } + + inline void CAXWindow::SetLocation(int x, int y, int width, int height) + { + m_rcControl.SetRect(x, y, x + width, y + height); + + if (!m_pUnk) + return; + + IOleInPlaceObject* pipo; + HRESULT hr = m_pUnk->QueryInterface(IID_IOleInPlaceObject, (void**)&pipo); + if (FAILED(hr)) + return; + + pipo->SetObjectRects(&m_rcControl, &m_rcControl); + pipo->Release(); + } + + inline STDMETHODIMP CAXWindow::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject) + { + UNREFERENCED_PARAMETER(hmenuShared); + UNREFERENCED_PARAMETER(holemenu); + UNREFERENCED_PARAMETER(hwndActiveObject); + return E_NOTIMPL; + } + + inline void CAXWindow::SetParent(HWND hWndParent) + { + m_hWnd = hWndParent; + } + + inline STDMETHODIMP CAXWindow::SetStatusText(LPCOLESTR pszStatusText) + { + if (NULL == pszStatusText) + return E_POINTER; + + #ifndef _UNICODE + char status[MAX_PATH]; + // Convert the Wide string to char + WideCharToMultiByte(CP_ACP, 0, pszStatusText, -1, status, MAX_PATH, NULL, NULL); + + if (IsWindow(m_hWndStatus)) + SendMessage(m_hWndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)status); + #else + if (IsWindow(m_hWndStatus)) + SendMessage(m_hWndStatus, SB_SETTEXT, (WPARAM)0, (LPARAM)pszStatusText); + #endif + + return (S_OK); + } + + inline void CAXWindow::SetStatusWindow(HWND hWndStatus) + { + m_hWndStatus = hWndStatus; + } + + inline void CAXWindow::SetVisible(BOOL fVisible) + { + if (!m_pUnk) + return; + + IOleObject* pioo; + HRESULT hr = m_pUnk->QueryInterface(IID_IOleObject, (void**)&pioo); + if (FAILED(hr)) + return; + + if (fVisible) + { + pioo->DoVerb(OLEIVERB_INPLACEACTIVATE, NULL, this, 0, m_hWnd, &m_rcControl); + pioo->DoVerb(OLEIVERB_SHOW, NULL, this, 0, m_hWnd, &m_rcControl); + } + else + pioo->DoVerb(OLEIVERB_HIDE, NULL, this, 0, m_hWnd, NULL); + + pioo->Release(); + } + + inline STDMETHODIMP CAXWindow::ShowObject() + { + return S_OK; + } + + inline STDMETHODIMP CAXWindow::ShowPropertyFrame() + { + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::TransformCoords(POINTL* pptlHimetric, POINTF* pptfContainer, DWORD dwFlags) + { + UNREFERENCED_PARAMETER(pptlHimetric); + UNREFERENCED_PARAMETER(pptfContainer); + UNREFERENCED_PARAMETER(dwFlags); + return E_NOTIMPL; + } + + inline STDMETHODIMP CAXWindow::TranslateAccelerator(LPMSG lpmsg, WORD wID) + { + UNREFERENCED_PARAMETER(lpmsg); + UNREFERENCED_PARAMETER(wID); + return S_OK; + } + + inline STDMETHODIMP CAXWindow::TranslateAccelerator(LPMSG pMsg, DWORD grfModifiers) + { + UNREFERENCED_PARAMETER(pMsg); + UNREFERENCED_PARAMETER(grfModifiers); + return S_FALSE; + } + + inline void CAXWindow::TranslateKey(MSG msg) + { + if (!m_pUnk) + return; + + IOleInPlaceActiveObject* pao; + HRESULT hr = m_pUnk->QueryInterface(IID_IOleInPlaceActiveObject, (void**)&pao); + if (FAILED(hr)) + return; + + pao->TranslateAccelerator(&msg); + pao->Release(); + } + + + //////////////////////////////////////// + // Definitions for the CWebBrowser class + // + inline CWebBrowser::CWebBrowser() : m_pIWebBrowser2(0) + { + OleInitialize(NULL); + } + + inline CWebBrowser::~CWebBrowser() + { + if (m_pIWebBrowser2) + { + m_pIWebBrowser2->Stop(); + m_pIWebBrowser2->Release(); + } + + OleUninitialize(); + } + + inline void CWebBrowser::AddWebBrowserControl() + { + GetAXWindow().CreateControl(CLSID_WebBrowser); + GetAXWindow().SetParent(m_hWnd); + GetAXWindow().SetVisible(TRUE); + GetAXWindow().Activate(TRUE); + + IUnknown* pUnk = GetAXWindow().GetUnknown(); + if(pUnk) + { + // Store the pointer to the WebBrowser control + HRESULT hr = pUnk->QueryInterface(IID_IWebBrowser2, (void**)&m_pIWebBrowser2); + pUnk->Release(); + + // Navigate to an empty page + if (SUCCEEDED(hr)) + { + VARIANT vURL; + vURL.vt = VT_BSTR; + vURL.bstrVal = SysAllocString(L"about:blank"); + VARIANT ve1, ve2, ve3, ve4; + ve1.vt = VT_EMPTY; + ve2.vt = VT_EMPTY; + ve3.vt = VT_EMPTY; + ve4.vt = VT_EMPTY; + + m_pIWebBrowser2->Navigate2(&vURL, &ve1, &ve2, &ve3, &ve4); + + VariantClear(&vURL); + } + } + } + + inline void CWebBrowser::Navigate(LPCTSTR pTChar) + { + // Navigate to our web page + VARIANT vURL; + vURL.vt = VT_BSTR; + vURL.bstrVal = SysAllocString(T2W(pTChar)); + VARIANT ve1, ve2, ve3, ve4; + ve1.vt = VT_EMPTY; + ve2.vt = VT_EMPTY; + ve3.vt = VT_EMPTY; + ve4.vt = VT_EMPTY; + + GetIWebBrowser2()->Navigate2(&vURL, &ve1, &ve2, &ve3, &ve4); + + VariantClear(&vURL); // Also frees memory allocated by SysAllocateString + } + + inline void CWebBrowser::OnCreate() + { + AddWebBrowserControl(); + } + + inline void CWebBrowser::OnSize(int width, int height) + { + // position the container + GetAXWindow().SetLocation(0, 0, width, height); + } + + inline LRESULT CWebBrowser::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + switch(uMsg) + { + case WM_SIZE: + OnSize(LOWORD(lParam), HIWORD(lParam)); + break; + case WM_DESTROY: + GetAXWindow().Remove(); + break; + } + + return CWnd::WndProcDefault(uMsg, wParam, lParam); + } + +} + +#endif // _WIN32XX_WEBBROWSER_H_ + diff --git a/mmc_updater/depends/win32cpp/wincore.h b/mmc_updater/depends/win32cpp/wincore.h new file mode 100644 index 00000000..d6b1f9b6 --- /dev/null +++ b/mmc_updater/depends/win32cpp/wincore.h @@ -0,0 +1,2977 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////// +// wincore.h +// Declaration of the following classes: +// CWinApp, CWnd, CWinException, CCriticalSection, +// CPoint, CRect, and CSize +// +// This file contains the declarations for the core set of classes required to +// create simple windows using Win32++. +// +// 1) CCriticalSection: This class is used internally to manage thread access +// to shared resources. You can also use this class to lock and +// release your own critical sections. +// +// 2) CWinException: This class is used internally by Win32++ to handle +// exceptions. You can also use it to throw and catch exceptions. +// +// 3) WinApp: This class is used start Win32++ and run the message loop. You +// should inherit from this class to start Win32++ in your own +// application. +// +// 4) CWnd: This class is used to represent a window. It provides a means +// of creating the window, and handling its messages. Inherit +// from this class to define and control windows. +// +// +// Note: This header file (or another Win32++ header file which includes it) +// should be included before all other header files. It sets some +// important macros which need to be set before including Windows.h +// Including this file first also allows it to disable some pointless +// warning messages (see below). + + + +#ifndef _WIN32XX_WINCORE_H_ +#define _WIN32XX_WINCORE_H_ + + +// Remove pointless warning messages +#ifdef _MSC_VER + #pragma warning (disable : 4996) // function or variable may be unsafe (deprecated) + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS // eliminate deprecation warnings for VS2005/VS2010 + #endif + #if _MSC_VER < 1500 + #pragma warning (disable : 4511) // copy operator could not be generated + #pragma warning (disable : 4512) // assignment operator could not be generated + #pragma warning (disable : 4702) // unreachable code (bugs in Microsoft's STL) + #pragma warning (disable : 4786) // identifier was truncated + #endif +#endif + +#ifdef __BORLANDC__ + #pragma option -w-8019 // code has no effect + #pragma option -w-8026 // functions with exception specifiations are not expanded inline + #pragma option -w-8027 // function not expanded inline + #define STRICT 1 +#endif + +#ifdef __GNUC__ + #pragma GCC diagnostic ignored "-Wmissing-braces" + #pragma GCC diagnostic ignored "-Wunused-value" +#endif + +#ifdef _WIN32_WCE + #include "wcestddef.h" +#endif + +#define _WINSOCKAPI_ // Prevent winsock.h #include's. + +#include <assert.h> +#include <vector> +#include <algorithm> +#include <string> +#include <map> +#include <windows.h> +#include <commctrl.h> +#include <stdio.h> +#include <tchar.h> +#include <shlwapi.h> +#include "shared_ptr.h" +//#include "winutils.h" // included later in this file +//#include "cstring.h" // included later in this file +//#include "gdi.h" // included later in this file +//#include "menu.h" // included later in this file + +// For compilers lacking Win64 support +#ifndef GetWindowLongPtr + #define GetWindowLongPtr GetWindowLong + #define SetWindowLongPtr SetWindowLong + #define GWLP_WNDPROC GWL_WNDPROC + #define GWLP_HINSTANCE GWL_HINSTANCE + #define GWLP_ID GWL_ID + #define GWLP_USERDATA GWL_USERDATA + #define DWLP_DLGPROC DWL_DLGPROC + #define DWLP_MSGRESULT DWL_MSGRESULT + #define DWLP_USER DWL_USER + #define DWORD_PTR DWORD + #define LONG_PTR LONG + #define ULONG_PTR LONG +#endif +#ifndef GetClassLongPtr + #define GetClassLongPtr GetClassLong + #define SetClassLongPtr SetClassLong + #define GCLP_HBRBACKGROUND GCL_HBRBACKGROUND + #define GCLP_HCURSOR GCL_HCURSOR + #define GCLP_HICON GCL_HICON + #define GCLP_HICONSM GCL_HICONSM + #define GCLP_HMODULE GCL_HMODULE + #define GCLP_MENUNAME GCL_MENUNAME + #define GCLP_WNDPROC GCL_WNDPROC +#endif + + +// Messages defined by Win32++ +#define UWM_POPUPMENU (WM_APP + 1) // Message - creates the menubar popup menu +#define UWM_DOCK_START (WM_APP + 2) // Notification - about to start undocking +#define UWM_DOCK_MOVE (WM_APP + 3) // Notification - undocked docker is being moved +#define UWM_DOCK_END (WM_APP + 4) // Notification - docker has been docked +#define UWM_BAR_START (WM_APP + 5) // Notification - docker bar selected for move +#define UWM_BAR_MOVE (WM_APP + 6) // Notification - docker bar moved +#define UWM_BAR_END (WM_APP + 7) // Notification - end of docker bar move +#define UWM_UNDOCKED (WM_APP + 8) // Notification - sent by docker when undocked +#define UWM_FRAMELOSTFOCUS (WM_APP + 9) // Notification - sent by frame to view window when focus lost +#define UWM_FRAMEGOTFOCUS (WM_APP + 10) // Notification - sent by frame to view window when focus acquired +#define UWM_DOCK_DESTROYED (WM_APP + 11) // Message - posted when docker is destroyed +#define UWM_TAB_CHANGED (WM_APP + 12) // Notification - tab layout changed +#define UWM_TOOLBAR_RESIZE (WM_APP + 13) // Message - sent by toolbar to parent. Used by the rebar +#define UWM_UPDATE_COMMAND (WM_APP + 14) // Message - sent before a menu is displayed. Used by OnUpdate +#define UWM_DOCK_ACTIVATED (WM_APP + 15) // Message - sent to dock ancestor when a docker is activated or deactivated. +#define UWM_GETMENUTHEME (WM_APP + 16) // Message - returns a pointer to MenuTheme +#define UWM_GETREBARTHEME (WM_APP + 17) // Message - returns a pointer to CToolBar +#define UWM_GETTOOLBARTHEME (WM_APP + 18) // Message - returns a pointer to ToolBarTheme +#define UWM_CLEANUPTEMPS (WM_APP + 19) // Message - posted to cleanup temporary CDCs + + +// Automatically include the Win32xx namespace +// define NO_USING_NAMESPACE to skip this step +namespace Win32xx {} +#ifndef NO_USING_NAMESPACE + using namespace Win32xx; +#endif + +// Required for WinCE +#ifndef TLS_OUT_OF_INDEXES + #define TLS_OUT_OF_INDEXES ((DWORD_PTR) -1) +#endif +#ifndef WM_PARENTNOTIFY + #define WM_PARENTNOTIFY 0x0210 +#endif + + +namespace Win32xx +{ + + //////////////////////////////////////////////// + // Forward declarations. + // These classes are defined later or elsewhere + class CDC; + class CGDIObject; + class CMenu; + class CWinApp; + class CWnd; + class CBitmap; + class CBrush; + class CFont; + class CPalette; + class CPen; + class CRgn; + + // tString is a TCHAR std::string + typedef std::basic_string<TCHAR> tString; + + // tStringStream is a TCHAR std::stringstream + typedef std::basic_stringstream<TCHAR> tStringStream; + + // Some useful smart pointers + typedef Shared_Ptr<CDC> DCPtr; + typedef Shared_Ptr<CGDIObject> GDIPtr; + typedef Shared_Ptr<CMenu> MenuPtr; + typedef Shared_Ptr<CWnd> WndPtr; + typedef Shared_Ptr<CBitmap> BitmapPtr; + typedef Shared_Ptr<CBrush> BrushPtr; + typedef Shared_Ptr<CFont> FontPtr; + typedef Shared_Ptr<CPalette> PalettePtr; + typedef Shared_Ptr<CPen> PenPtr; + typedef Shared_Ptr<CRgn> RgnPtr; + + enum Constants // Defines the maximum size for TCHAR strings + { + MAX_MENU_STRING = 80, + MAX_STRING_SIZE = 255, + }; + + struct CompareHDC // The comparison function object used by CWinApp::m_mapHDC + { + bool operator()(HDC const a, const HDC b) const + {return ((DWORD_PTR)a < (DWORD_PTR)b);} + }; + + struct CompareGDI // The comparison function object used by CWinApp::m_mapGDI + { + bool operator()(HGDIOBJ const a, const HGDIOBJ b) const + {return ((DWORD_PTR)a < (DWORD_PTR)b);} + }; + + struct CompareHMENU // The comparison function object used by CWinApp::m_mapHMENU + { + bool operator()(HMENU const a, const HMENU b) const + {return ((DWORD_PTR)a < (DWORD_PTR)b);} + }; + + struct CompareHWND // The comparison function object used by CWinApp::m_mapHWND + { + bool operator()(HWND const a, const HWND b) const + {return ((DWORD_PTR)a < (DWORD_PTR)b);} + }; + + struct TLSData // Used for Thread Local Storage (TLS) + { + CWnd* pCWnd; // pointer to CWnd object for Window creation + CWnd* pMenuBar; // pointer to CMenuBar object used for the WH_MSGFILTER hook + HHOOK hHook; // WH_MSGFILTER hook for CMenuBar and Modeless Dialogs + + std::vector<DCPtr> vTmpDCs; // A vector of temporary CDC pointers + std::vector<GDIPtr> vTmpGDIs; // A vector of temporary CGDIObject pointers + std::vector<WndPtr> vTmpWnds; // A vector of temporary CWnd pointers + TLSData() : pCWnd(0), pMenuBar(0), hHook(0) {} + +#ifndef _WIN32_WCE + std::vector<MenuPtr> vTmpMenus; // A vector of temporary CMenu pointers +#endif + }; + + + ///////////////////////////////////////// + // Declarations for the CCriticalSection class + // This class is used for thread synchronisation + class CCriticalSection + { + public: + CCriticalSection() { ::InitializeCriticalSection(&m_cs); } + ~CCriticalSection() { ::DeleteCriticalSection(&m_cs); } + + void Lock() { ::EnterCriticalSection(&m_cs); } + void Release() { ::LeaveCriticalSection(&m_cs); } + + private: + CCriticalSection ( const CCriticalSection& ); + CCriticalSection& operator = ( const CCriticalSection& ); + + CRITICAL_SECTION m_cs; + }; + + + //////////////////////////////////////// + // Declaration of the CWinException class + // + // Note: Each function guarantees not to throw an exception + + class CWinException : public std::exception + { + public: + CWinException(LPCTSTR pszText) throw (); + ~CWinException() throw() {} + DWORD GetError() const throw (); + LPCTSTR GetErrorString() const throw (); + const char * what () const throw (); + + private: + DWORD m_Error; + LPCTSTR m_pszText; + TCHAR m_szErrorString[MAX_STRING_SIZE]; + }; + + + /////////////////////////////////// + // Declaration of the CWinApp class + // + class CWinApp + { + // Provide these access to CWinApp's private members: + friend class CDC; + friend class CDialog; + friend class CGDIObject; + friend class CMenu; + friend class CMenuBar; + friend class CPropertyPage; + friend class CPropertySheet; + friend class CTaskDialog; + friend class CWnd; + friend CWinApp* GetApp(); + friend CGDIObject* FromHandle(HGDIOBJ hObject); + friend CBitmap* FromHandle(HBITMAP hBitmap); + friend CBrush* FromHandle(HBRUSH hBrush); + friend CFont* FromHandle(HFONT hFont); + friend CPalette* FromHandle(HPALETTE hPalette); + friend CPen* FromHandle(HPEN hPen); + friend CRgn* FromHandle(HRGN hRgn); + friend CDC* FromHandle(HDC hDC); + friend CWnd* FromHandle(HWND hWnd); +#ifndef _WIN32_WCE + friend CMenu* FromHandle(HMENU hMenu); +#endif + + typedef Shared_Ptr<TLSData> TLSDataPtr; + + public: + CWinApp(); + virtual ~CWinApp(); + + HACCEL GetAccelerators() const { return m_hAccel; } + HINSTANCE GetInstanceHandle() const { return m_hInstance; } + HINSTANCE GetResourceHandle() const { return (m_hResource ? m_hResource : m_hInstance); } + void SetAccelerators(HACCEL hAccel, CWnd* pWndAccel); + void SetResourceHandle(HINSTANCE hResource); + + // These are the functions you might wish to override + virtual BOOL InitInstance(); + virtual int MessageLoop(); + virtual int Run(); + + protected: + virtual BOOL OnIdle(LONG lCount); + virtual BOOL PreTranslateMessage(MSG Msg); + + private: + CWinApp(const CWinApp&); // Disable copy construction + CWinApp& operator = (const CWinApp&); // Disable assignment operator + CDC* GetCDCFromMap(HDC hDC); + CGDIObject* GetCGDIObjectFromMap(HGDIOBJ hObject); + CMenu* GetCMenuFromMap(HMENU hMenu); + CWnd* GetCWndFromMap(HWND hWnd); + + void AddTmpDC(CDC* pDC); + void AddTmpGDI(CGDIObject* pObject); + CMenu* AddTmpMenu(HMENU hMenu); + CWnd* AddTmpWnd(HWND hWnd); + void CleanupTemps(); + DWORD GetTlsIndex() const {return m_dwTlsIndex;} + void SetCallback(); + TLSData* SetTlsIndex(); + static CWinApp* SetnGetThis(CWinApp* pThis = 0); + + std::map<HDC, CDC*, CompareHDC> m_mapHDC; // maps device context handles to CDC objects + std::map<HGDIOBJ, CGDIObject*, CompareGDI> m_mapGDI; // maps GDI handles to CGDIObjects. + std::map<HMENU, CMenu*, CompareHMENU> m_mapHMENU; // maps menu handles to CMenu objects + std::map<HWND, CWnd*, CompareHWND> m_mapHWND; // maps window handles to CWnd objects + std::vector<TLSDataPtr> m_vTLSData; // vector of TLSData smart pointers, one for each thread + CCriticalSection m_csMapLock; // thread synchronisation for m_mapHWND + CCriticalSection m_csTLSLock; // thread synchronisation for m_vTLSData + CCriticalSection m_csAppStart; // thread synchronisation for application startup + HINSTANCE m_hInstance; // handle to the applications instance + HINSTANCE m_hResource; // handle to the applications resources + DWORD m_dwTlsIndex; // Thread Local Storage index + WNDPROC m_Callback; // callback address of CWnd::StaticWndowProc + HACCEL m_hAccel; // handle to the accelerator table + CWnd* m_pWndAccel; // handle to the window for accelerator keys + + }; + +} + +#include "winutils.h" +#include "cstring.h" + + +namespace Win32xx +{ + //////////////////////////////// + // Declaration of the CWnd class + // + class CWnd + { + friend class CMDIChild; + friend class CDialog; + friend class CPropertyPage; + friend class CTaskDialog; + friend class CWinApp; + + public: + CWnd(); // Constructor + virtual ~CWnd(); // Destructor + + // These virtual functions can be overridden + virtual BOOL Attach(HWND hWnd); + virtual BOOL AttachDlgItem(UINT nID, CWnd* pParent); + virtual void CenterWindow() const; + virtual HWND Create(CWnd* pParent = NULL); + virtual HWND CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, CWnd* pParent, CMenu* pMenu, LPVOID lpParam = NULL); + virtual HWND CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rc, CWnd* pParent, CMenu* pMenu, LPVOID lpParam = NULL); + virtual void Destroy(); + virtual HWND Detach(); + virtual HICON SetIconLarge(int nIcon); + virtual HICON SetIconSmall(int nIcon); + + // Attributes + HWND GetHwnd() const { return m_hWnd; } + WNDPROC GetPrevWindowProc() const { return m_PrevWindowProc; } + + // Wrappers for Win32 API functions + // These functions aren't virtual, and shouldn't be overridden + CDC* BeginPaint(PAINTSTRUCT& ps) const; + BOOL BringWindowToTop() const; + LRESULT CallWindowProc(WNDPROC lpPrevWndFunc, UINT Msg, WPARAM wParam, LPARAM lParam) const; + BOOL CheckDlgButton(int nIDButton, UINT uCheck) const; + BOOL CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton) const; + CWnd* ChildWindowFromPoint(POINT pt) const; + BOOL ClientToScreen(POINT& pt) const; + BOOL ClientToScreen(RECT& rc) const; + LRESULT DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) const; + HDWP DeferWindowPos(HDWP hWinPosInfo, HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags) const; + HDWP DeferWindowPos(HDWP hWinPosInfo, HWND hWndInsertAfter, const RECT& rc, UINT uFlags) const; + BOOL DrawMenuBar() const; + BOOL EnableWindow(BOOL bEnable = TRUE) const; + BOOL EndPaint(PAINTSTRUCT& ps) const; + CWnd* GetActiveWindow() const; + CWnd* GetAncestor(UINT gaFlag = 3 /*= GA_ROOTOWNER*/) const; + CWnd* GetCapture() const; + ULONG_PTR GetClassLongPtr(int nIndex) const; + CString GetClassName() const; + CRect GetClientRect() const; + CDC* GetDC() const; + CDC* GetDCEx(HRGN hrgnClip, DWORD flags) const; + CWnd* GetDesktopWindow() const; + CWnd* GetDlgItem(int nIDDlgItem) const; + UINT GetDlgItemInt(int nIDDlgItem, BOOL* lpTranslated, BOOL bSigned) const; + CString GetDlgItemText(int nIDDlgItem) const; + CWnd* GetFocus() const; + CFont* GetFont() const; + HICON GetIcon(BOOL bBigIcon) const; + CWnd* GetNextDlgGroupItem(CWnd* pCtl, BOOL bPrevious) const; + CWnd* GetNextDlgTabItem(CWnd* pCtl, BOOL bPrevious) const; + CWnd* GetParent() const; + BOOL GetScrollInfo(int fnBar, SCROLLINFO& si) const; + CRect GetUpdateRect(BOOL bErase) const; + int GetUpdateRgn(CRgn* pRgn, BOOL bErase) const; + CWnd* GetWindow(UINT uCmd) const; + CDC* GetWindowDC() const; + LONG_PTR GetWindowLongPtr(int nIndex) const; + CRect GetWindowRect() const; + CString GetWindowText() const; + int GetWindowTextLength() const; + void Invalidate(BOOL bErase = TRUE) const; + BOOL InvalidateRect(LPCRECT lpRect, BOOL bErase = TRUE) const; + BOOL InvalidateRgn(CRgn* pRgn, BOOL bErase = TRUE) const; + BOOL IsChild(CWnd* pChild) const; + BOOL IsDialogMessage(LPMSG lpMsg) const; + UINT IsDlgButtonChecked(int nIDButton) const; + BOOL IsWindow() const; + BOOL IsWindowEnabled() const; + BOOL IsWindowVisible() const; + BOOL KillTimer(UINT_PTR uIDEvent) const; + int MessageBox(LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) const; + void MapWindowPoints(CWnd* pWndTo, POINT& pt) const; + void MapWindowPoints(CWnd* pWndTo, RECT& rc) const; + void MapWindowPoints(CWnd* pWndTo, LPPOINT ptArray, UINT nCount) const; + BOOL MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint = TRUE) const; + BOOL MoveWindow(const RECT& rc, BOOL bRepaint = TRUE) const; + BOOL PostMessage(UINT uMsg, WPARAM wParam = 0L, LPARAM lParam = 0L) const; + BOOL PostMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) const; + BOOL RedrawWindow(LPCRECT lpRectUpdate = NULL, CRgn* pRgn = NULL, UINT flags = RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASE | RDW_ALLCHILDREN) const; + int ReleaseDC(CDC* pDC) const; + BOOL ScreenToClient(POINT& Point) const; + BOOL ScreenToClient(RECT& rc) const; + LRESULT SendDlgItemMessage(int nIDDlgItem, UINT Msg, WPARAM wParam, LPARAM lParam) const; + LRESULT SendMessage(UINT uMsg, WPARAM wParam = 0L, LPARAM lParam = 0L) const; + LRESULT SendMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) const; + BOOL SendNotifyMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const; + CWnd* SetActiveWindow() const; + CWnd* SetCapture() const; + ULONG_PTR SetClassLongPtr(int nIndex, LONG_PTR dwNewLong) const; + BOOL SetDlgItemInt(int nIDDlgItem, UINT uValue, BOOL bSigned) const; + BOOL SetDlgItemText(int nIDDlgItem, LPCTSTR lpString) const; + CWnd* SetFocus() const; + void SetFont(CFont* pFont, BOOL bRedraw = TRUE) const; + BOOL SetForegroundWindow() const; + HICON SetIcon(HICON hIcon, BOOL bBigIcon) const; + CWnd* SetParent(CWnd* pWndParent) const; + BOOL SetRedraw(BOOL bRedraw = TRUE) const; + int SetScrollInfo(int fnBar, const SCROLLINFO& si, BOOL fRedraw) const; + UINT_PTR SetTimer(UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc) const; + LONG_PTR SetWindowLongPtr(int nIndex, LONG_PTR dwNewLong) const; + BOOL SetWindowPos(HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags) const; + BOOL SetWindowPos(HWND hWndInsertAfter, const RECT& rc, UINT uFlags) const; + int SetWindowRgn(CRgn* pRgn, BOOL bRedraw = TRUE) const; + BOOL SetWindowText(LPCTSTR lpString) const; + HRESULT SetWindowTheme(LPCWSTR pszSubAppName, LPCWSTR pszSubIdList) const; + BOOL ShowWindow(int nCmdShow = SW_SHOWNORMAL) const; + BOOL UpdateWindow() const; + BOOL ValidateRect(LPCRECT prc) const; + BOOL ValidateRgn(CRgn* pRgn) const; + static CWnd* WindowFromPoint(POINT pt); + + #ifndef _WIN32_WCE + BOOL CloseWindow() const; + int DlgDirList(LPTSTR lpPathSpec, int nIDListBox, int nIDStaticPath, UINT uFileType) const; + int DlgDirListComboBox(LPTSTR lpPathSpec, int nIDComboBox, int nIDStaticPath, UINT uFiletype) const; + BOOL DlgDirSelectEx(LPTSTR lpString, int nCount, int nIDListBox) const; + BOOL DlgDirSelectComboBoxEx(LPTSTR lpString, int nCount, int nIDComboBox) const; + BOOL DrawAnimatedRects(int idAni, RECT& rcFrom, RECT& rcTo) const; + BOOL DrawCaption(CDC* pDC, RECT& rc, UINT uFlags) const; + BOOL EnableScrollBar(UINT uSBflags, UINT uArrows) const; + CWnd* GetLastActivePopup() const; + CMenu* GetMenu() const; + int GetScrollPos(int nBar) const; + BOOL GetScrollRange(int nBar, int& MinPos, int& MaxPos) const; + CMenu* GetSystemMenu(BOOL bRevert) const; + CWnd* GetTopWindow() const; + BOOL GetWindowPlacement(WINDOWPLACEMENT& pWndpl) const; + BOOL HiliteMenuItem(CMenu* pMenu, UINT uItemHilite, UINT uHilite) const; + BOOL IsIconic() const; + BOOL IsZoomed() const; + BOOL LockWindowUpdate() const; + BOOL OpenIcon() const; + void Print(CDC* pDC, DWORD dwFlags) const; + BOOL SetMenu(CMenu* pMenu) const; + BOOL ScrollWindow(int XAmount, int YAmount, LPCRECT lprcScroll, LPCRECT lprcClip) const; + int ScrollWindowEx(int dx, int dy, LPCRECT lprcScroll, LPCRECT lprcClip, CRgn* prgnUpdate, LPRECT lprcUpdate, UINT flags) const; + int SetScrollPos(int nBar, int nPos, BOOL bRedraw) const; + BOOL SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw) const; + BOOL SetWindowPlacement(const WINDOWPLACEMENT& wndpl) const; + BOOL ShowOwnedPopups(BOOL fShow) const; + BOOL ShowScrollBar(int nBar, BOOL bShow) const; + BOOL ShowWindowAsync(int nCmdShow) const; + BOOL UnLockWindowUpdate() const; + CWnd* WindowFromDC(CDC* pDC) const; + + #ifndef WIN32_LEAN_AND_MEAN + void DragAcceptFiles(BOOL fAccept) const; + #endif + #endif + + static LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + operator HWND() const { return m_hWnd; } + + protected: + // Override these functions as required + virtual LRESULT FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam); + virtual void OnCreate(); + virtual void OnDraw(CDC* pDC); + virtual BOOL OnEraseBkgnd(CDC* pDC); + virtual void OnInitialUpdate(); + virtual void OnMenuUpdate(UINT nID); + virtual LRESULT OnMessageReflect(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotify(WPARAM wParam, LPARAM lParam); + virtual LRESULT OnNotifyReflect(WPARAM wParam, LPARAM lParam); + virtual void PreCreate(CREATESTRUCT& cs); + virtual void PreRegisterClass(WNDCLASS& wc); + virtual BOOL PreTranslateMessage(MSG* pMsg); + virtual LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual LRESULT WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + HWND m_hWnd; // handle to this object's window + + private: + CWnd(const CWnd&); // Disable copy construction + CWnd& operator = (const CWnd&); // Disable assignment operator + void AddToMap(); + void Cleanup(); + LRESULT MessageReflect(HWND hwndParent, UINT uMsg, WPARAM wParam, LPARAM lParam); + BOOL RegisterClass(WNDCLASS& wc); + BOOL RemoveFromMap(); + void Subclass(HWND hWnd); + + Shared_Ptr<WNDCLASS> m_pwc; // defines initialisation parameters for PreRegisterClass + Shared_Ptr<CREATESTRUCT> m_pcs; // defines initialisation parameters for PreCreate and Create + WNDPROC m_PrevWindowProc; // pre-subclassed Window Procedure + BOOL m_IsTmpWnd; // True if this CWnd is a TmpWnd + + }; // class CWnd + +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +#include "gdi.h" +#include "menu.h" + +namespace Win32xx +{ + + ////////////////////////////////////////// + // Definitions for the CWinException class + // + inline CWinException::CWinException(LPCTSTR pszText) throw () : m_Error(::GetLastError()), m_pszText(pszText) + { + memset(m_szErrorString, 0, MAX_STRING_SIZE * sizeof(TCHAR)); + + if (m_Error != 0) + { + DWORD dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS; + ::FormatMessage(dwFlags, NULL, m_Error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), m_szErrorString, MAX_STRING_SIZE-1, NULL); + } + } + + inline DWORD CWinException::GetError() const throw () + { + return m_Error; + } + + inline LPCTSTR CWinException::GetErrorString() const throw () + { + return m_szErrorString; + } + + inline const char * CWinException::what() const throw () + { + // Sends the last error string to the debugger (typically displayed in the IDE's output window). + ::OutputDebugString(m_szErrorString); + return "CWinException thrown"; + } + + + //////////////////////////////////// + // Definitions for the CWinApp class + // + + // To begin Win32++, inherit your application class from this one. + // You must run only one instance of the class inherited from this. + inline CWinApp::CWinApp() : m_Callback(NULL), m_hAccel(0), m_pWndAccel(0) + { + try + { + m_csAppStart.Lock(); + assert( 0 == SetnGetThis() ); // Test if this is the first instance of CWinApp + + m_dwTlsIndex = ::TlsAlloc(); + if (m_dwTlsIndex == TLS_OUT_OF_INDEXES) + { + // We only get here in the unlikely event that all TLS indexes are already allocated by this app + // At least 64 TLS indexes per process are allowed. Win32++ requires only one TLS index. + m_csAppStart.Release(); + throw CWinException(_T("CWinApp::CWinApp Failed to allocate TLS Index")); + } + + SetnGetThis(this); + m_csAppStart.Release(); + + // Set the instance handle + #ifdef _WIN32_WCE + m_hInstance = (HINSTANCE)GetModuleHandle(0); + #else + MEMORY_BASIC_INFORMATION mbi = {0}; + VirtualQuery( (LPCVOID)SetnGetThis, &mbi, sizeof(mbi) ); + assert(mbi.AllocationBase); + m_hInstance = (HINSTANCE)mbi.AllocationBase; + #endif + + m_hResource = m_hInstance; + SetCallback(); + } + + catch (const CWinException &e) + { + e.what(); + throw; + } + } + + inline CWinApp::~CWinApp() + { + std::vector<TLSDataPtr>::iterator iter; + for (iter = m_vTLSData.begin(); iter < m_vTLSData.end(); ++iter) + { + (*iter)->vTmpDCs.clear(); +#ifndef _WIN32_WCE + (*iter)->vTmpMenus.clear(); +#endif + (*iter)->vTmpWnds.clear(); + } + + // Check that all CWnd windows are destroyed + std::map<HWND, CWnd*, CompareHWND>::iterator m; + for (m = m_mapHWND.begin(); m != m_mapHWND.end(); ++m) + { + HWND hWnd = (*m).first; + if (::IsWindow(hWnd)) + ::DestroyWindow(hWnd); + } + m_mapHWND.clear(); + m_mapGDI.clear(); + m_mapHDC.clear(); + m_mapHMENU.clear(); + + // Do remaining tidy up + if (m_dwTlsIndex != TLS_OUT_OF_INDEXES) + { + ::TlsSetValue(GetTlsIndex(), NULL); + ::TlsFree(m_dwTlsIndex); + } + + SetnGetThis((CWinApp*)-1); + } + + inline void CWinApp::AddTmpDC(CDC* pDC) + { + // The TmpMenus are created by GetSybMenu. + // They are removed by CleanupTemps + assert(pDC); + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + pTLSData->vTmpDCs.push_back(pDC); // save pDC as a smart pointer + } + + inline void CWinApp::AddTmpGDI(CGDIObject* pObject) + { + // The temporary CGDIObjects are removed by CleanupTemps + assert(pObject); + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + pTLSData->vTmpGDIs.push_back(pObject); // save pObject as a smart pointer + } + +#ifndef _WIN32_WCE + inline CMenu* CWinApp::AddTmpMenu(HMENU hMenu) + { + // The TmpMenus are created by GetSybMenu. + // They are removed by CleanupTemps + assert(::IsMenu(hMenu)); + assert(!GetCMenuFromMap(hMenu)); + + CMenu* pMenu = new CMenu; + pMenu->m_hMenu = hMenu; + m_csMapLock.Lock(); + m_mapHMENU.insert(std::make_pair(hMenu, pMenu)); + m_csMapLock.Release(); + pMenu->m_IsTmpMenu = TRUE; + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + pTLSData->vTmpMenus.push_back(pMenu); // save pMenu as a smart pointer + return pMenu; + } +#endif + + inline CWnd* CWinApp::AddTmpWnd(HWND hWnd) + { + // TmpWnds are created if required to support functions like CWnd::GetParent. + // They are removed by CleanupTemps + assert(::IsWindow(hWnd)); + assert(!GetCWndFromMap(hWnd)); + + CWnd* pWnd = new CWnd; + pWnd->m_hWnd = hWnd; + pWnd->AddToMap(); + pWnd->m_IsTmpWnd = TRUE; + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + pTLSData->vTmpWnds.push_back(pWnd); // save pWnd as a smart pointer + return pWnd; + } + + inline void CWinApp::CleanupTemps() + // Removes all Temporary CWnds and CMenus belonging to this thread + { + // Retrieve the pointer to the TLS Data + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + assert(pTLSData); + + pTLSData->vTmpDCs.clear(); + pTLSData->vTmpGDIs.clear(); + pTLSData->vTmpWnds.clear(); + + + #ifndef _WIN32_WCE + pTLSData->vTmpMenus.clear(); + #endif + } + + inline CDC* CWinApp::GetCDCFromMap(HDC hDC) + { + // Allocate an iterator for our HWND map + std::map<HDC, CDC*, CompareHDC>::iterator m; + + // Find the CDC pointer mapped to this HDC + CDC* pDC = 0; + m_csMapLock.Lock(); + m = m_mapHDC.find(hDC); + + if (m != m_mapHDC.end()) + pDC = m->second; + + m_csMapLock.Release(); + return pDC; + } + + inline CGDIObject* CWinApp::GetCGDIObjectFromMap(HGDIOBJ hObject) + { + // Allocate an iterator for our HWND map + std::map<HGDIOBJ, CGDIObject*, CompareGDI>::iterator m; + + // Find the CGDIObject pointer mapped to this HGDIOBJ + CGDIObject* pObject = 0; + m_csMapLock.Lock(); + m = m_mapGDI.find(hObject); + + if (m != m_mapGDI.end()) + pObject = m->second; + + m_csMapLock.Release(); + return pObject; + } + + inline CMenu* CWinApp::GetCMenuFromMap(HMENU hMenu) + { + std::map<HMENU, CMenu*, CompareHMENU>::iterator m; + + // Find the CMenu pointer mapped to this HMENU + CMenu* pMenu = 0; + m_csMapLock.Lock(); + m = m_mapHMENU.find(hMenu); + + if (m != m_mapHMENU.end()) + pMenu = m->second; + + m_csMapLock.Release(); + return pMenu; + } + + inline CWnd* CWinApp::GetCWndFromMap(HWND hWnd) + { + // Allocate an iterator for our HWND map + std::map<HWND, CWnd*, CompareHWND>::iterator m; + + // Find the CWnd pointer mapped to this HWND + CWnd* pWnd = 0; + m_csMapLock.Lock(); + m = m_mapHWND.find(hWnd); + + if (m != m_mapHWND.end()) + pWnd = m->second; + + m_csMapLock.Release(); + return pWnd; + } + + inline BOOL CWinApp::InitInstance() + { + // InitInstance contains the initialization code for your application + // You should override this function with the code to run when the application starts. + + // return TRUE to indicate success. FALSE will end the application + return TRUE; + } + + inline int CWinApp::MessageLoop() + { + // This gets any messages queued for the application, and dispatches them. + MSG Msg = {0}; + int status = 1; + LONG lCount = 0; + + while (status != 0) + { + // While idle, perform idle processing until OnIdle returns FALSE + while (!::PeekMessage(&Msg, 0, 0, 0, PM_NOREMOVE) && OnIdle(lCount) == TRUE) + { + ++lCount; + } + + lCount = 0; + + // Now wait until we get a message + if ((status = ::GetMessage(&Msg, NULL, 0, 0)) == -1) + return -1; + + if (!PreTranslateMessage(Msg)) + { + ::TranslateMessage(&Msg); + ::DispatchMessage(&Msg); + } + } + + return LOWORD(Msg.wParam); + } + + inline BOOL CWinApp::OnIdle(LONG lCount) + { + if (lCount == 0) + CleanupTemps(); + + return FALSE; + } + + inline BOOL CWinApp::PreTranslateMessage(MSG Msg) + { + // This functions is called by the MessageLoop. It processes the + // keyboard accelerator keys and calls CWnd::PreTranslateMessage for + // keyboard and mouse events. + + BOOL Processed = FALSE; + + // only pre-translate mouse and keyboard input events + if ((Msg.message >= WM_KEYFIRST && Msg.message <= WM_KEYLAST) || + (Msg.message >= WM_MOUSEFIRST && Msg.message <= WM_MOUSELAST)) + { + // Process keyboard accelerators + if (m_pWndAccel && ::TranslateAccelerator(*m_pWndAccel, m_hAccel, &Msg)) + Processed = TRUE; + else + { + // Search the chain of parents for pretranslated messages. + for (HWND hWnd = Msg.hwnd; hWnd != NULL; hWnd = ::GetParent(hWnd)) + { + CWnd* pWnd = GetCWndFromMap(hWnd); + if (pWnd) + { + Processed = pWnd->PreTranslateMessage(&Msg); + if(Processed) + break; + } + } + } + } + + return Processed; + } + + inline int CWinApp::Run() + { + // InitInstance runs the App's initialization code + if (InitInstance()) + { + // Dispatch the window messages + return MessageLoop(); + } + else + { + TRACE(_T("InitInstance failed! Terminating program\n")); + ::PostQuitMessage(-1); + return -1; + } + } + + inline void CWinApp::SetAccelerators(HACCEL hAccel, CWnd* pWndAccel) + // nID is the resource ID of the accelerator table + // pWndAccel is the window pointer for translated messages + { + assert (hAccel); + assert (pWndAccel); + + m_pWndAccel = pWndAccel; + m_hAccel = hAccel; + } + + inline void CWinApp::SetCallback() + { + // Registers a temporary window class so we can get the callback + // address of CWnd::StaticWindowProc. + // This technique works for all Window versions, including WinCE. + + WNDCLASS wcDefault = {0}; + + LPCTSTR szClassName = _T("Win32++ Temporary Window Class"); + wcDefault.hInstance = GetInstanceHandle(); + wcDefault.lpfnWndProc = CWnd::StaticWindowProc; + wcDefault.lpszClassName = szClassName; + + ::RegisterClass(&wcDefault); + + // Retrieve the class information + ZeroMemory(&wcDefault, sizeof(wcDefault)); + ::GetClassInfo(GetInstanceHandle(), szClassName, &wcDefault); + + // Save the callback address of CWnd::StaticWindowProc + assert(wcDefault.lpfnWndProc); // Assert fails when running UNICODE build on ANSI OS. + m_Callback = wcDefault.lpfnWndProc; + ::UnregisterClass(szClassName, GetInstanceHandle()); + } + + inline CWinApp* CWinApp::SetnGetThis(CWinApp* pThis /*= 0*/) + { + // This function stores the 'this' pointer in a static variable. + // Once stored, it can be used later to return the 'this' pointer. + // CWinApp's Destructor calls this function with a value of -1. + + static CWinApp* pWinApp = 0; + + if ((CWinApp*)-1 == pThis) + pWinApp = 0; + else if (0 == pWinApp) + pWinApp = pThis; + + return pWinApp; + } + + inline void CWinApp::SetResourceHandle(HINSTANCE hResource) + { + // This function can be used to load a resource dll. + // A resource dll can be used to define resources in different languages. + // To use this function, place code like this in InitInstance + // + // HINSTANCE hResource = LoadLibrary(_T("MyResourceDLL.dll")); + // SetResourceHandle(hResource); + + m_hResource = hResource; + } + + inline TLSData* CWinApp::SetTlsIndex() + { + TLSData* pTLSData = (TLSData*)::TlsGetValue(GetTlsIndex()); + if (NULL == pTLSData) + { + pTLSData = new TLSData; + + m_csTLSLock.Lock(); + m_vTLSData.push_back(pTLSData); // store as a Shared_Ptr + m_csTLSLock.Release(); + + ::TlsSetValue(GetTlsIndex(), pTLSData); + } + + return pTLSData; + } + + + //////////////////////////////////////// + // Definitions for the CWnd class + // + inline CWnd::CWnd() : m_hWnd(NULL), m_PrevWindowProc(NULL), m_IsTmpWnd(FALSE) + { + // Note: m_hWnd is set in CWnd::CreateEx(...) + m_pcs = new CREATESTRUCT; // store the CREATESTRICT in a smart pointer + m_pwc = new WNDCLASS; // store the WNDCLASS in a smart pointer + ::ZeroMemory(m_pcs.get(), sizeof(CREATESTRUCT)); + ::ZeroMemory(m_pwc.get(), sizeof(WNDCLASS)); + } + + inline CWnd::~CWnd() + { + // Destroys the window for this object and cleans up resources. + Destroy(); + } + + inline void CWnd::AddToMap() + // Store the window handle and CWnd pointer in the HWND map + { + assert( GetApp() ); + GetApp()->m_csMapLock.Lock(); + + assert(::IsWindow(m_hWnd)); + assert(!GetApp()->GetCWndFromMap(m_hWnd)); + + GetApp()->m_mapHWND.insert(std::make_pair(m_hWnd, this)); + GetApp()->m_csMapLock.Release(); + } + + inline BOOL CWnd::Attach(HWND hWnd) + // Subclass an existing window and attach it to a CWnd + { + assert( GetApp() ); + assert(::IsWindow(hWnd)); + + // Ensure this thread has the TLS index set + // Note: Perform the attach from the same thread as the window's message loop + GetApp()->SetTlsIndex(); + + if (m_PrevWindowProc) + Detach(); + + Subclass(hWnd); + + // Store the CWnd pointer in the HWND map + AddToMap(); + OnCreate(); + OnInitialUpdate(); + + return TRUE; + } + + inline BOOL CWnd::AttachDlgItem(UINT nID, CWnd* pParent) + // Converts a dialog item to a CWnd object + { + assert(pParent->IsWindow()); + + HWND hWnd = ::GetDlgItem(pParent->GetHwnd(), nID); + return Attach(hWnd); + } + + inline void CWnd::CenterWindow() const + // Centers this window over it's parent + { + + // required for multi-monitor support with Dev-C++ and VC6 + #ifndef _WIN32_WCE + #ifndef MONITOR_DEFAULTTONEAREST + #define MONITOR_DEFAULTTONEAREST 0x00000002 + #endif + #ifndef HMONITOR + DECLARE_HANDLE(HMONITOR); + #endif + #ifndef MONITORINFO + typedef struct tagMONITORINFO + { + DWORD cbSize; + RECT rcMonitor; + RECT rcWork; + DWORD dwFlags; + } MONITORINFO, *LPMONITORINFO; + #endif // MONITOR_DEFAULTTONEAREST + #endif // _WIN32_WCE + + assert(::IsWindow(m_hWnd)); + + CRect rc = GetWindowRect(); + CRect rcParent; + CRect rcDesktop; + + // Get screen dimensions excluding task bar + ::SystemParametersInfo(SPI_GETWORKAREA, 0, &rcDesktop, 0); + + // Get the parent window dimensions (parent could be the desktop) + if (GetParent() != NULL) rcParent = GetParent()->GetWindowRect(); + else rcParent = rcDesktop; + + #ifndef _WIN32_WCE + // Import the GetMonitorInfo and MonitorFromWindow functions + HMODULE hUser32 = LoadLibrary(_T("USER32.DLL")); + typedef BOOL (WINAPI* LPGMI)(HMONITOR hMonitor, LPMONITORINFO lpmi); + typedef HMONITOR (WINAPI* LPMFW)(HWND hwnd, DWORD dwFlags); + LPMFW pfnMonitorFromWindow = (LPMFW)::GetProcAddress(hUser32, "MonitorFromWindow"); + #ifdef _UNICODE + LPGMI pfnGetMonitorInfo = (LPGMI)::GetProcAddress(hUser32, "GetMonitorInfoW"); + #else + LPGMI pfnGetMonitorInfo = (LPGMI)::GetProcAddress(hUser32, "GetMonitorInfoA"); + #endif + + // Take multi-monitor systems into account + if (pfnGetMonitorInfo && pfnMonitorFromWindow) + { + HMONITOR hActiveMonitor = pfnMonitorFromWindow(m_hWnd, MONITOR_DEFAULTTONEAREST); + MONITORINFO mi = { sizeof(mi), 0}; + + if(pfnGetMonitorInfo(hActiveMonitor, &mi)) + { + rcDesktop = mi.rcWork; + if (GetParent() == NULL) rcParent = mi.rcWork; + } + } + FreeLibrary(hUser32); + #endif + + // Calculate point to center the dialog over the portion of parent window on this monitor + rcParent.IntersectRect(rcParent, rcDesktop); + int x = rcParent.left + (rcParent.Width() - rc.Width())/2; + int y = rcParent.top + (rcParent.Height() - rc.Height())/2; + + // Keep the dialog wholly on the monitor display + x = (x < rcDesktop.left)? rcDesktop.left : x; + x = (x > rcDesktop.right - rc.Width())? rcDesktop.right - rc.Width() : x; + y = (y < rcDesktop.top) ? rcDesktop.top: y; + y = (y > rcDesktop.bottom - rc.Height())? rcDesktop.bottom - rc.Height() : y; + + SetWindowPos(HWND_TOP, x, y, 0, 0, SWP_NOSIZE); + } + + inline void CWnd::Cleanup() + // Returns the CWnd to its default state + { + if ( GetApp() ) RemoveFromMap(); + m_hWnd = NULL; + m_PrevWindowProc = NULL; + m_IsTmpWnd = FALSE; + } + + inline HWND CWnd::Create(CWnd* pParent /* = NULL */) + // Creates the window. This is the default method of window creation. + { + + // Test if Win32++ has been started + assert( GetApp() ); + + // Set the WNDCLASS parameters + PreRegisterClass(*m_pwc); + if (m_pwc->lpszClassName) + { + RegisterClass(*m_pwc); + m_pcs->lpszClass = m_pwc->lpszClassName; + } + + // Set the CREATESTRUCT parameters + PreCreate(*m_pcs); + + // Set the Window Class Name + if (!m_pcs->lpszClass) + m_pcs->lpszClass = _T("Win32++ Window"); + + // Set Parent + HWND hWndParent = pParent? pParent->GetHwnd() : 0; + if (!hWndParent && m_pcs->hwndParent) + hWndParent = m_pcs->hwndParent; + + // Set the window style + DWORD dwStyle; + DWORD dwOverlappedStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + if (m_pcs->style) + dwStyle = m_pcs->style; + else + dwStyle = WS_VISIBLE | ((hWndParent)? WS_CHILD : dwOverlappedStyle); + + // Set window size and position + int x = (m_pcs->cx || m_pcs->cy)? m_pcs->x : CW_USEDEFAULT; + int cx = (m_pcs->cx || m_pcs->cy)? m_pcs->cx : CW_USEDEFAULT; + int y = (m_pcs->cx || m_pcs->cy)? m_pcs->y : CW_USEDEFAULT; + int cy = (m_pcs->cx || m_pcs->cy)? m_pcs->cy : CW_USEDEFAULT; + + // Create the window +#ifndef _WIN32_WCE + CreateEx(m_pcs->dwExStyle, m_pcs->lpszClass, m_pcs->lpszName, dwStyle, x, y, + cx, cy, pParent, FromHandle(m_pcs->hMenu), m_pcs->lpCreateParams); +#else + CreateEx(m_pcs->dwExStyle, m_pcs->lpszClass, m_pcs->lpszName, dwStyle, x, y, + cx, cy, pParent, 0, m_pcs->lpCreateParams); +#endif + + return m_hWnd; + } + + inline HWND CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rc, CWnd* pParent, CMenu* pMenu, LPVOID lpParam /*= NULL*/) + // Creates the window by specifying all the window creation parameters + { + int x = rc.left; + int y = rc.top; + int cx = rc.right - rc.left; + int cy = rc.bottom - rc.top; + return CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, x, y, cx, cy, pParent, pMenu, lpParam); + } + + inline HWND CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, int x, int y, int nWidth, int nHeight, CWnd* pParent, CMenu* pMenu, LPVOID lpParam /*= NULL*/) + // Creates the window by specifying all the window creation parameters + { + + assert( GetApp() ); // Test if Win32++ has been started + assert(!::IsWindow(m_hWnd)); // Only one window per CWnd instance allowed + + try + { + // Prepare the CWnd if it has been reused + Destroy(); + + // Ensure a window class is registered + std::vector<TCHAR> vTChar( MAX_STRING_SIZE+1, _T('\0') ); + TCHAR* ClassName = &vTChar[0]; + if (0 == lpszClassName || 0 == lstrlen(lpszClassName) ) + lstrcpyn (ClassName, _T("Win32++ Window"), MAX_STRING_SIZE); + else + // Create our own local copy of szClassName. + lstrcpyn(ClassName, lpszClassName, MAX_STRING_SIZE); + + WNDCLASS wc = {0}; + wc.lpszClassName = ClassName; + wc.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH); + wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + + // Register the window class (if not already registered) + if (!RegisterClass(wc)) + throw CWinException(_T("Failed to register window class")); + + HWND hWndParent = pParent? pParent->GetHwnd() : 0; + + // Ensure this thread has the TLS index set + TLSData* pTLSData = GetApp()->SetTlsIndex(); + + // Store the CWnd pointer in thread local storage + pTLSData->pCWnd = this; + + // Create window +#ifdef _WIN32_WCE + m_hWnd = ::CreateWindowEx(dwExStyle, ClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, + hWndParent, 0, GetApp()->GetInstanceHandle(), lpParam); +#else + HMENU hMenu = pMenu? pMenu->GetHandle() : NULL; + m_hWnd = ::CreateWindowEx(dwExStyle, ClassName, lpszWindowName, dwStyle, x, y, nWidth, nHeight, + hWndParent, hMenu, GetApp()->GetInstanceHandle(), lpParam); +#endif + + // Now handle window creation failure + if (!m_hWnd) + throw CWinException(_T("Failed to Create Window")); + + // Automatically subclass predefined window class types + ::GetClassInfo(GetApp()->GetInstanceHandle(), lpszClassName, &wc); + if (wc.lpfnWndProc != GetApp()->m_Callback) + { + Subclass(m_hWnd); + + // Send a message to force the HWND to be added to the map + SendMessage(WM_NULL, 0L, 0L); + + OnCreate(); // We missed the WM_CREATE message, so call OnCreate now + } + + // Clear the CWnd pointer from TLS + pTLSData->pCWnd = NULL; + } + + catch (const CWinException &e) + { + TRACE(_T("\n*** Failed to create window ***\n")); + e.what(); // Display the last error message. + + // eat the exception (don't rethrow) + } + + // Window creation is complete. Now call OnInitialUpdate + OnInitialUpdate(); + + return m_hWnd; + } + + inline void CWnd::Destroy() + // Destroys the window and returns the CWnd back to its default state, ready for reuse. + { + if (m_IsTmpWnd) + m_hWnd = NULL; + + if (IsWindow()) + ::DestroyWindow(m_hWnd); + + // Return the CWnd to its default state + Cleanup(); + } + + inline HWND CWnd::Detach() + // Reverse an Attach + { + assert(::IsWindow(m_hWnd)); + assert(0 != m_PrevWindowProc); // Only a subclassed window can be detached + + SetWindowLongPtr(GWLP_WNDPROC, (LONG_PTR)m_PrevWindowProc); + HWND hWnd = m_hWnd; + Cleanup(); + + return hWnd; + } + + inline LRESULT CWnd::FinalWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) + // Pass messages on to the appropriate default window procedure + // CMDIChild and CMDIFrame override this function + { + return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam); + } + + inline CWnd* CWnd::GetAncestor(UINT gaFlags /*= GA_ROOTOWNER*/) const + // The GetAncestor function retrieves a pointer to the ancestor (root parent) + // of the window. Supports Win95. + { + assert(::IsWindow(m_hWnd)); + HWND hWnd; + +#if (WINVER < 0x0500) // Win2000 and above + UNREFERENCED_PARAMETER(gaFlags); + hWnd = m_hWnd; + HWND hWndParent = ::GetParent(hWnd); + while (::IsChild(hWndParent, hWnd)) + { + hWnd = hWndParent; + hWndParent = ::GetParent(hWnd); + } +#else + hWnd = ::GetAncestor(m_hWnd, gaFlags); +#endif + + return FromHandle(hWnd); + + } + + inline CString CWnd::GetClassName() const + // Retrieves the name of the class to which the specified window belongs. + { + assert(::IsWindow(m_hWnd)); + + CString str; + LPTSTR szStr = str.GetBuffer(MAX_STRING_SIZE+1); + ::GetClassName(m_hWnd, szStr, MAX_STRING_SIZE+1); + str.ReleaseBuffer(); + return str; + } + + inline CString CWnd::GetDlgItemText(int nIDDlgItem) const + // Retrieves the title or text associated with a control in a dialog box. + { + assert(::IsWindow(m_hWnd)); + + int nLength = ::GetWindowTextLength(::GetDlgItem(m_hWnd, nIDDlgItem)); + CString str; + LPTSTR szStr = str.GetBuffer(nLength+1); + ::GetDlgItemText(m_hWnd, nIDDlgItem, szStr, nLength+1); + str.ReleaseBuffer(); + return str; + } + + inline CString CWnd::GetWindowText() const + // Retrieves the text of the window's title bar. + { + assert(::IsWindow(m_hWnd)); + + int nLength = ::GetWindowTextLength(m_hWnd); + CString str; + LPTSTR szStr = str.GetBuffer(nLength+1); + ::GetWindowText(m_hWnd, szStr, nLength+1); + str.ReleaseBuffer(); + return str; + } + + inline BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + // Override this to handle WM_COMMAND messages, for example + + // switch (LOWORD(wParam)) + // { + // case IDM_FILE_NEW: + // OnFileNew(); + // TRUE; // return TRUE for handled commands + // } + + // return FALSE for unhandled commands + return FALSE; + } + + inline void CWnd::OnCreate() + { + // This function is called when a WM_CREATE message is recieved + // Override it in your derived class to automatically perform tasks + // during window creation. + } + + inline void CWnd::OnDraw(CDC* pDC) + // Called when part of the client area of the window needs to be drawn + { + UNREFERENCED_PARAMETER(pDC); + + // Override this function in your derived class to perform drawing tasks. + } + + inline BOOL CWnd::OnEraseBkgnd(CDC* pDC) + // Called when the background of the window's client area needs to be erased. + { + UNREFERENCED_PARAMETER(pDC); + + // Override this function in your derived class to perform drawing tasks. + + // Return Value: Return FALSE to also permit default erasure of the background + // Return TRUE to prevent default erasure of the background + + return FALSE; + } + + + inline void CWnd::OnInitialUpdate() + { + // This function is called automatically once the window is created + // Override it in your derived class to automatically perform tasks + // after window creation. + } + + inline LRESULT CWnd::MessageReflect(HWND hWndParent, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + // A function used to call OnMessageReflect. You shouldn't need to call or + // override this function. + + HWND hWnd = NULL; + switch (uMsg) + { + case WM_COMMAND: + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORDLG: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + case WM_CHARTOITEM: + case WM_VKEYTOITEM: + case WM_HSCROLL: + case WM_VSCROLL: + hWnd = (HWND)lParam; + break; + + case WM_DRAWITEM: + case WM_MEASUREITEM: + case WM_DELETEITEM: + case WM_COMPAREITEM: + hWnd = ::GetDlgItem(hWndParent, (int)wParam); + break; + + case WM_PARENTNOTIFY: + switch(LOWORD(wParam)) + { + case WM_CREATE: + case WM_DESTROY: + hWnd = (HWND)lParam; + break; + } + } + + CWnd* Wnd = GetApp()->GetCWndFromMap(hWnd); + + if (Wnd != NULL) + return Wnd->OnMessageReflect(uMsg, wParam, lParam); + + return 0L; + } + + inline LRESULT CWnd::OnMessageReflect(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(uMsg); + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + // This function processes those special messages (see above) sent + // by some older controls, and reflects them back to the originating CWnd object. + // Override this function in your derrived class to handle these special messages. + + // Your overriding function should look like this ... + + // switch (uMsg) + // { + // Handle your reflected messages here + // } + + // return 0L for unhandled messages + return 0L; + } + + inline LRESULT CWnd::OnNotify(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + // You can use either OnNotifyReflect or OnNotify to handle notifications + // Override OnNotifyReflect to handle notifications in the CWnd class that + // generated the notification. OR + // Override OnNotify to handle notifications in the PARENT of the CWnd class + // that generated the notification. + + // Your overriding function should look like this ... + + // switch (((LPNMHDR)lParam)->code) + // { + // Handle your notifications from the CHILD window here + // Return the value recommended by the Windows API documentation. + // For many notifications, the return value doesn't matter, but for some it does. + // } + + // return 0L for unhandled notifications + return 0L; + } + + inline LRESULT CWnd::OnNotifyReflect(WPARAM wParam, LPARAM lParam) + { + UNREFERENCED_PARAMETER(wParam); + UNREFERENCED_PARAMETER(lParam); + + // Override OnNotifyReflect to handle notifications in the CWnd class that + // generated the notification. + + // Your overriding function should look like this ... + + // switch (((LPNMHDR)lParam)->code) + // { + // Handle your notifications from this window here + // Return the value recommended by the Windows API documentation. + // } + + // return 0L for unhandled notifications + return 0L; + } + + inline void CWnd::OnMenuUpdate(UINT nID) + // Called when menu items are about to be displayed + { + UNREFERENCED_PARAMETER(nID); + + // Override this function to modify the behaviour of menu items, + // such as adding or removing checkmarks + } + + inline void CWnd::PreCreate(CREATESTRUCT& cs) + // Called by CWnd::Create to set some window parameters + { + // Test if Win32++ has been started + assert(GetApp()); // Test if Win32++ has been started + + m_pcs->cx = cs.cx; + m_pcs->cy = cs.cy; + m_pcs->dwExStyle = cs.dwExStyle; + m_pcs->hInstance = GetApp()->GetInstanceHandle(); + m_pcs->hMenu = cs.hMenu; + m_pcs->hwndParent = cs.hwndParent; + m_pcs->lpCreateParams = cs.lpCreateParams; + m_pcs->lpszClass = cs.lpszClass; + m_pcs->lpszName = cs.lpszName; + m_pcs->style = cs.style; + m_pcs->x = cs.x; + m_pcs->y = cs.y; + + // Overide this function in your derived class to set the + // CREATESTRUCT values prior to window creation. + // The cs.lpszClass parameter should NOT be specified if the + // PreRegisterClass function is used to create a window class. + } + + inline void CWnd::PreRegisterClass(WNDCLASS& wc) + // Called by CWnd::Create to set some window parameters + // Useful for setting the background brush and cursor + { + // Test if Win32++ has been started + assert( GetApp() ); + + m_pwc->style = wc.style; + m_pwc->lpfnWndProc = CWnd::StaticWindowProc; + m_pwc->cbClsExtra = wc.cbClsExtra; + m_pwc->cbWndExtra = wc.cbWndExtra; + m_pwc->hInstance = GetApp()->GetInstanceHandle(); + m_pwc->hIcon = wc.hIcon; + m_pwc->hCursor = wc.hCursor; + m_pwc->hbrBackground = wc.hbrBackground; + m_pwc->lpszMenuName = wc.lpszMenuName; + m_pwc->lpszClassName = wc.lpszClassName; + + // Overide this function in your derived class to set the + // WNDCLASS values prior to window creation. + + // ADDITIONAL NOTES: + // 1) The lpszClassName must be set for this function to take effect. + // 2) The lpfnWndProc is always CWnd::StaticWindowProc. + // 3) No other defaults are set, so the following settings might prove useful + // wc.hCursor = ::LoadCursor(NULL, IDC_ARROW); + // wc.hbrBackground = (HBRUSH)::GetStockObject(WHITE_BRUSH); + // wc.hIcon = ::LoadIcon(NULL, IDI_APPLICATION); + // 4) The styles that can be set here are WNDCLASS styles. These are a different + // set of styles to those set by CREATESTRUCT (used in PreCreate). + // 5) RegisterClassEx is not used because its not supported on WinCE. + // To set a small icon for the window, use SetIconSmall. + } + + inline BOOL CWnd::PreTranslateMessage(MSG* pMsg) + { + UNREFERENCED_PARAMETER(pMsg); + + // Override this function if your class requires input messages to be + // translated before normal processing. Function which translate messages + // include TranslateAccelerator, TranslateMDISysAccel and IsDialogMessage. + // Return TRUE if the message is translated. + + return FALSE; + } + + inline BOOL CWnd::RegisterClass(WNDCLASS& wc) + // A private function used by the PreRegisterClass function to register a + // window class prior to window creation + { + assert( GetApp() ); + assert( (0 != lstrlen(wc.lpszClassName) && ( lstrlen(wc.lpszClassName) <= MAX_STRING_SIZE) ) ); + + // Check to see if this classname is already registered + WNDCLASS wcTest = {0}; + BOOL Done = FALSE; + + if (::GetClassInfo(GetApp()->GetInstanceHandle(), wc.lpszClassName, &wcTest)) + { + wc = wcTest; + Done = TRUE; + } + + if (!Done) + { + // Set defaults + wc.hInstance = GetApp()->GetInstanceHandle(); + wc.lpfnWndProc = CWnd::StaticWindowProc; + + // Register the WNDCLASS structure + if ( !::RegisterClass(&wc) ) + throw CWinException(_T("Failed to register window class")); + + Done = TRUE; + } + + return Done; + } + + inline BOOL CWnd::RemoveFromMap() + { + BOOL Success = FALSE; + + if (GetApp()) + { + + // Allocate an iterator for our HWND map + std::map<HWND, CWnd*, CompareHWND>::iterator m; + + CWinApp* pApp = GetApp(); + if (pApp) + { + // Erase the CWnd pointer entry from the map + pApp->m_csMapLock.Lock(); + for (m = pApp->m_mapHWND.begin(); m != pApp->m_mapHWND.end(); ++m) + { + if (this == m->second) + { + pApp->m_mapHWND.erase(m); + Success = TRUE; + break; + } + } + + pApp->m_csMapLock.Release(); + } + } + + return Success; + } + + inline HICON CWnd::SetIconLarge(int nIcon) + // Sets the large icon associated with the window + { + assert( GetApp() ); + assert(::IsWindow(m_hWnd)); + + HICON hIconLarge = (HICON) (::LoadImage (GetApp()->GetResourceHandle(), MAKEINTRESOURCE (nIcon), IMAGE_ICON, + ::GetSystemMetrics (SM_CXICON), ::GetSystemMetrics (SM_CYICON), 0)); + + if (hIconLarge) + SendMessage (WM_SETICON, WPARAM (ICON_BIG), LPARAM (hIconLarge)); + else + TRACE(_T("**WARNING** SetIconLarge Failed\n")); + + return hIconLarge; + } + + inline HICON CWnd::SetIconSmall(int nIcon) + // Sets the small icon associated with the window + { + assert( GetApp() ); + assert(::IsWindow(m_hWnd)); + + HICON hIconSmall = (HICON) (::LoadImage (GetApp()->GetResourceHandle(), MAKEINTRESOURCE (nIcon), IMAGE_ICON, + ::GetSystemMetrics (SM_CXSMICON), ::GetSystemMetrics (SM_CYSMICON), 0)); + + if (hIconSmall) + SendMessage (WM_SETICON, WPARAM (ICON_SMALL), LPARAM (hIconSmall)); + else + TRACE(_T("**WARNING** SetIconSmall Failed\n")); + + return hIconSmall; + } + + inline LRESULT CALLBACK CWnd::StaticWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) + // All CWnd windows direct their messages here. This function redirects the message + // to the CWnd's WndProc function. + { + assert( GetApp() ); + + CWnd* w = GetApp()->GetCWndFromMap(hWnd); + if (0 == w) + { + // The CWnd pointer wasn't found in the map, so add it now + + // Retrieve the pointer to the TLS Data + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + assert(pTLSData); + + // Retrieve pointer to CWnd object from Thread Local Storage TLS + w = pTLSData->pCWnd; + assert(w); // pTLSData->pCWnd is assigned in CreateEx + pTLSData->pCWnd = NULL; + + // Store the CWnd pointer in the HWND map + w->m_hWnd = hWnd; + w->AddToMap(); + } + + return w->WndProc(uMsg, wParam, lParam); + + } // LRESULT CALLBACK StaticWindowProc(...) + + inline void CWnd::Subclass(HWND hWnd) + // A private function used by CreateEx, Attach and AttachDlgItem + { + assert(::IsWindow(hWnd)); + + m_PrevWindowProc = (WNDPROC)::SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)CWnd::StaticWindowProc); + m_hWnd = hWnd; + } + + inline LRESULT CWnd::WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) + { + // Override this function in your class derrived from CWnd to handle + // window messages. A typical function might look like this: + + // switch (uMsg) + // { + // case MESSAGE1: // Some Windows API message + // OnMessage1(); // A user defined function + // break; // Also do default processing + // case MESSAGE2: + // OnMessage2(); + // return x; // Don't do default processing, but instead return + // // a value recommended by the Windows API documentation + // } + + // Always pass unhandled messages on to WndProcDefault + return WndProcDefault(uMsg, wParam, lParam); + } + + inline LRESULT CWnd::WndProcDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) + // All WndProc functions should pass unhandled window messages to this function + { + LRESULT lr = 0L; + + switch (uMsg) + { + case UWM_CLEANUPTEMPS: + { + TLSData* pTLSData = (TLSData*)TlsGetValue(GetApp()->GetTlsIndex()); + pTLSData->vTmpWnds.clear(); + } + break; + case WM_COMMAND: + { + // Refelect this message if it's from a control + CWnd* pWnd = GetApp()->GetCWndFromMap((HWND)lParam); + if (pWnd != NULL) + lr = pWnd->OnCommand(wParam, lParam); + + // Handle user commands + if (!lr) + lr = OnCommand(wParam, lParam); + + if (lr) return 0L; + } + break; // Note: Some MDI commands require default processing + case WM_CREATE: + OnCreate(); + break; + // An example of how to end the application when the window closes + // If needed, put this in the class you inherit from CWnd + // case WM_DESTROY: + // ::PostQuitMessage(0); + // return 0L; + case WM_NOTIFY: + { + // Do Notification reflection if it came from a CWnd object + HWND hwndFrom = ((LPNMHDR)lParam)->hwndFrom; + CWnd* pWndFrom = GetApp()->GetCWndFromMap(hwndFrom); + + if (lstrcmp(GetClassName(), _T("ReBarWindow32")) != 0) // Skip notification reflection for rebars to avoid double handling + { + if (pWndFrom != NULL) + lr = pWndFrom->OnNotifyReflect(wParam, lParam); + else + { + // Some controls (eg ListView) have child windows. + // Reflect those notifications too. + CWnd* pWndFromParent = GetApp()->GetCWndFromMap(::GetParent(hwndFrom)); + if (pWndFromParent != NULL) + lr = pWndFromParent->OnNotifyReflect(wParam, lParam); + } + } + + // Handle user notifications + if (!lr) lr = OnNotify(wParam, lParam); + if (lr) return lr; + } + break; + + case WM_PAINT: + { + // Subclassed controls expect to do their own painting. + // CustomDraw or OwnerDraw are normally used to modify the drawing of controls. + if (m_PrevWindowProc) break; + + if (::GetUpdateRect(m_hWnd, NULL, FALSE)) + { + CPaintDC dc(this); + OnDraw(&dc); + } + else + // RedrawWindow can require repainting without an update rect + { + CClientDC dc(this); + OnDraw(&dc); + } + } + return 0L; + + case WM_ERASEBKGND: + { + CDC dc((HDC)wParam); + BOOL bResult = OnEraseBkgnd(&dc); + dc.Detach(); + if (bResult) return TRUE; + } + break; + + // A set of messages to be reflected back to the control that generated them + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORDLG: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + case WM_DRAWITEM: + case WM_MEASUREITEM: + case WM_DELETEITEM: + case WM_COMPAREITEM: + case WM_CHARTOITEM: + case WM_VKEYTOITEM: + case WM_HSCROLL: + case WM_VSCROLL: + case WM_PARENTNOTIFY: + { + // if (m_PrevWindowProc) break; // Suppress for subclassed windows + + LRESULT lr = MessageReflect(m_hWnd, uMsg, wParam, lParam); + if (lr) return lr; // Message processed so return + } + break; // Do default processing when message not already processed + + case UWM_UPDATE_COMMAND: + OnMenuUpdate((UINT)wParam); // Perform menu updates + break; + + } // switch (uMsg) + + // Now hand all messages to the default procedure + if (m_PrevWindowProc) + return ::CallWindowProc(m_PrevWindowProc, m_hWnd, uMsg, wParam, lParam); + else + return FinalWindowProc(uMsg, wParam, lParam); + + } // LRESULT CWnd::WindowProc(...) + + + // + // Wrappers for Win32 API functions + // + + inline CDC* CWnd::BeginPaint(PAINTSTRUCT& ps) const + // The BeginPaint function prepares the specified window for painting and fills a PAINTSTRUCT structure with + // information about the painting. + { + assert(::IsWindow(m_hWnd)); + return FromHandle(::BeginPaint(m_hWnd, &ps)); + } + + inline BOOL CWnd::BringWindowToTop() const + // The BringWindowToTop function brings the specified window to the top + // of the Z order. If the window is a top-level window, it is activated. + { + assert(::IsWindow(m_hWnd)); + return ::BringWindowToTop(m_hWnd); + } + + inline LRESULT CWnd::CallWindowProc(WNDPROC lpPrevWndFunc, UINT Msg, WPARAM wParam, LPARAM lParam) const + { + assert(::IsWindow(m_hWnd)); + return ::CallWindowProc(lpPrevWndFunc, m_hWnd, Msg, wParam, lParam); + } + + inline BOOL CWnd::CheckDlgButton(int nIDButton, UINT uCheck) const + // The CheckDlgButton function changes the check state of a button control. + { + assert(::IsWindow(m_hWnd)); + return ::CheckDlgButton(m_hWnd, nIDButton, uCheck); + } + + inline BOOL CWnd::CheckRadioButton(int nIDFirstButton, int nIDLastButton, int nIDCheckButton) const + // The CheckRadioButton function adds a check mark to (checks) a specified radio button in a group + // and removes a check mark from (clears) all other radio buttons in the group. + { + assert(::IsWindow(m_hWnd)); + return ::CheckRadioButton(m_hWnd, nIDFirstButton, nIDLastButton, nIDCheckButton); + } + + inline CWnd* CWnd::ChildWindowFromPoint(POINT pt) const + // determines which, if any, of the child windows belonging to a parent window contains + // the specified point. The search is restricted to immediate child windows. + // Grandchildren, and deeper descendant windows are not searched. + { + assert(::IsWindow(m_hWnd)); + return FromHandle(::ChildWindowFromPoint(m_hWnd, pt)); + } + + inline BOOL CWnd::ClientToScreen(POINT& pt) const + // The ClientToScreen function converts the client-area coordinates of a specified point to screen coordinates. + { + assert(::IsWindow(m_hWnd)); + return ::ClientToScreen(m_hWnd, &pt); + } + + inline BOOL CWnd::ClientToScreen(RECT& rc) const + // The ClientToScreen function converts the client-area coordinates of a specified RECT to screen coordinates. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)::MapWindowPoints(m_hWnd, NULL, (LPPOINT)&rc, 2); + } + + inline HDWP CWnd::DeferWindowPos(HDWP hWinPosInfo, HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags) const + // The DeferWindowPos function updates the specified multiple-window – position structure for the window. + { + assert(::IsWindow(m_hWnd)); + return ::DeferWindowPos(hWinPosInfo, m_hWnd, hWndInsertAfter, x, y, cx, cy, uFlags); + } + + inline HDWP CWnd::DeferWindowPos(HDWP hWinPosInfo, HWND hWndInsertAfter, const RECT& rc, UINT uFlags) const + // The DeferWindowPos function updates the specified multiple-window – position structure for the window. + { + assert(::IsWindow(m_hWnd)); + return ::DeferWindowPos(hWinPosInfo, m_hWnd, hWndInsertAfter, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, uFlags); + } + + inline LRESULT CWnd::DefWindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) const + // This function provides default processing for any window messages that an application does not process. + { + assert(::IsWindow(m_hWnd)); + return ::DefWindowProc(m_hWnd, uMsg, wParam, lParam); + } + + inline BOOL CWnd::DrawMenuBar() const + // The DrawMenuBar function redraws the menu bar of the specified window. If the menu bar changes after + // the system has created the window, this function must be called to draw the changed menu bar. + { + assert(::IsWindow(m_hWnd)); + return ::DrawMenuBar(m_hWnd); + } + + inline BOOL CWnd::EnableWindow(BOOL bEnable /*= TRUE*/) const + // The EnableWindow function enables or disables mouse and + // keyboard input to the window. + { + assert(::IsWindow(m_hWnd)); + return ::EnableWindow(m_hWnd, bEnable); + } + + inline BOOL CWnd::EndPaint(PAINTSTRUCT& ps) const + // The EndPaint function marks the end of painting in the specified window. This function is required for + // each call to the BeginPaint function, but only after painting is complete. + { + assert(::IsWindow(m_hWnd)); + return ::EndPaint(m_hWnd, &ps); + } + + inline CWnd* CWnd::GetActiveWindow() const + // The GetActiveWindow function retrieves a pointer to the active window attached to the calling + // thread's message queue. + { + return FromHandle( ::GetActiveWindow() ); + } + + inline CWnd* CWnd::GetCapture() const + // The GetCapture function retrieves a pointer to the window (if any) that has captured the mouse. + { + return FromHandle( ::GetCapture() ); + } + + inline ULONG_PTR CWnd::GetClassLongPtr(int nIndex) const + // The GetClassLongPtr function retrieves the specified value from the + // WNDCLASSEX structure associated with the window. + { + assert(::IsWindow(m_hWnd)); + return ::GetClassLongPtr(m_hWnd, nIndex); + } + + inline CRect CWnd::GetClientRect() const + // The GetClientRect function retrieves the coordinates of a window's client area. + // The client coordinates specify the upper-left and lower-right corners of the + // client area. Because client coordinates are relative to the upper-left corner + // of a window's client area, the coordinates of the upper-left corner are (0,0). + { + assert(::IsWindow(m_hWnd)); + CRect rc; + ::GetClientRect(m_hWnd, &rc); + return rc; + } + + inline CDC* CWnd::GetDC() const + // The GetDC function retrieves a handle to a display device context (DC) for the + // client area of the window. + { + assert(::IsWindow(m_hWnd)); + return CDC::AddTempHDC(::GetDC(m_hWnd), m_hWnd); + } + + inline CDC* CWnd::GetDCEx(HRGN hrgnClip, DWORD flags) const + // The GetDCEx function retrieves a handle to a display device context (DC) for the + // client area or entire area of a window + { + assert(::IsWindow(m_hWnd)); + return CDC::AddTempHDC(::GetDCEx(m_hWnd, hrgnClip, flags), m_hWnd); + } + + inline CWnd* CWnd::GetDesktopWindow() const + // The GetDesktopWindow function retrieves a pointer to the desktop window. + { + return FromHandle( ::GetDesktopWindow() ); + } + + inline CWnd* CWnd::GetDlgItem(int nIDDlgItem) const + // The GetDlgItem function retrieves a handle to a control in the dialog box. + { + assert(::IsWindow(m_hWnd)); + return FromHandle( ::GetDlgItem(m_hWnd, nIDDlgItem) ); + } + + inline UINT CWnd::GetDlgItemInt(int nIDDlgItem, BOOL* lpTranslated, BOOL bSigned) const + // The GetDlgItemInt function translates the text of a specified control in a dialog box into an integer value. + { + assert(::IsWindow(m_hWnd)); + return ::GetDlgItemInt(m_hWnd, nIDDlgItem, lpTranslated, bSigned); + } + + inline CWnd* CWnd::GetFocus() const + // The GetFocus function retrieves a pointer to the window that has the keyboard focus, if the window + // is attached to the calling thread's message queue. + { + return FromHandle( ::GetFocus() ); + } + + inline CFont* CWnd::GetFont() const + // Retrieves the font with which the window is currently drawing its text. + { + assert(::IsWindow(m_hWnd)); + return FromHandle((HFONT)SendMessage(WM_GETFONT, 0, 0)); + } + + inline HICON CWnd::GetIcon(BOOL bBigIcon) const + // Retrieves a handle to the large or small icon associated with a window. + { + assert(::IsWindow(m_hWnd)); + return (HICON)SendMessage(WM_GETICON, (WPARAM)bBigIcon, 0); + } + + inline CWnd* CWnd::GetNextDlgGroupItem(CWnd* pCtl, BOOL bPrevious) const + // The GetNextDlgGroupItem function retrieves a pointer to the first control in a group of controls that + // precedes (or follows) the specified control in a dialog box. + { + assert(::IsWindow(m_hWnd)); + assert(pCtl); + return FromHandle(::GetNextDlgGroupItem(m_hWnd, pCtl->GetHwnd(), bPrevious)); + } + + inline CWnd* CWnd::GetNextDlgTabItem(CWnd* pCtl, BOOL bPrevious) const + // The GetNextDlgTabItem function retrieves a pointer to the first control that has the WS_TABSTOP style + // that precedes (or follows) the specified control. + { + assert(::IsWindow(m_hWnd)); + assert(pCtl); + return FromHandle(::GetNextDlgTabItem(m_hWnd, pCtl->GetHwnd(), bPrevious)); + } + + inline CWnd* CWnd::GetParent() const + // The GetParent function retrieves a pointer to the specified window's parent or owner. + { + assert(::IsWindow(m_hWnd)); + return FromHandle( ::GetParent(m_hWnd) ); + } + + inline LONG_PTR CWnd::GetWindowLongPtr(int nIndex) const + // The GetWindowLongPtr function retrieves information about the window. + { + assert(::IsWindow(m_hWnd)); + return ::GetWindowLongPtr(m_hWnd, nIndex); + } + + inline BOOL CWnd::GetScrollInfo(int fnBar, SCROLLINFO& si) const + // The GetScrollInfo function retrieves the parameters of a scroll bar, including + // the minimum and maximum scrolling positions, the page size, and the position + // of the scroll box (thumb). + { + assert(::IsWindow(m_hWnd)); + return ::GetScrollInfo(m_hWnd, fnBar, &si); + } + + inline CRect CWnd::GetUpdateRect(BOOL bErase) const + // The GetUpdateRect function retrieves the coordinates of the smallest rectangle that completely + // encloses the update region of the specified window. + { + assert(::IsWindow(m_hWnd)); + CRect rc; + ::GetUpdateRect(m_hWnd, &rc, bErase); + return rc; + } + + inline int CWnd::GetUpdateRgn(CRgn* pRgn, BOOL bErase) const + // The GetUpdateRgn function retrieves the update region of a window by copying it into the specified region. + { + assert(::IsWindow(m_hWnd)); + assert(pRgn); + HRGN hRgn = (HRGN)pRgn->GetHandle(); + return ::GetUpdateRgn(m_hWnd, hRgn, bErase); + } + + inline CWnd* CWnd::GetWindow(UINT uCmd) const + // The GetWindow function retrieves a pointer to a window that has the specified + // relationship (Z-Order or owner) to the specified window. + // Possible uCmd values: GW_CHILD, GW_ENABLEDPOPUP, GW_HWNDFIRST, GW_HWNDLAST, + // GW_HWNDNEXT, GW_HWNDPREV, GW_OWNER + { + assert(::IsWindow(m_hWnd)); + return FromHandle( ::GetWindow(m_hWnd, uCmd) ); + } + + inline CDC* CWnd::GetWindowDC() const + // The GetWindowDC function retrieves the device context (DC) for the entire + // window, including title bar, menus, and scroll bars. + { + assert(::IsWindow(m_hWnd)); + return CDC::AddTempHDC(::GetWindowDC(m_hWnd), m_hWnd); + } + + inline CRect CWnd::GetWindowRect() const + // retrieves the dimensions of the bounding rectangle of the window. + // The dimensions are given in screen coordinates that are relative to the + // upper-left corner of the screen. + { + assert(::IsWindow(m_hWnd)); + CRect rc; + ::GetWindowRect(m_hWnd, &rc); + return rc; + } + + inline int CWnd::GetWindowTextLength() const + // The GetWindowTextLength function retrieves the length, in characters, of the specified window's + // title bar text (if the window has a title bar). + { + assert(::IsWindow(m_hWnd)); + return ::GetWindowTextLength(m_hWnd); + } + + inline void CWnd::Invalidate(BOOL bErase /*= TRUE*/) const + // The Invalidate function adds the entire client area the window's update region. + // The update region represents the portion of the window's client area that must be redrawn. + { + assert(::IsWindow(m_hWnd)); + ::InvalidateRect(m_hWnd, NULL, bErase); + } + + inline BOOL CWnd::InvalidateRect(LPCRECT lpRect, BOOL bErase /*= TRUE*/) const + // The InvalidateRect function adds a rectangle to the window's update region. + // The update region represents the portion of the window's client area that must be redrawn. + { + assert(::IsWindow(m_hWnd)); + return ::InvalidateRect(m_hWnd, lpRect, bErase); + } + + inline BOOL CWnd::InvalidateRgn(CRgn* pRgn, BOOL bErase /*= TRUE*/) const + // The InvalidateRgn function invalidates the client area within the specified region + // by adding it to the current update region of a window. The invalidated region, + // along with all other areas in the update region, is marked for painting when the + // next WM_PAINT message occurs. + { + assert(::IsWindow(m_hWnd)); + HRGN hRgn = pRgn? (HRGN)pRgn->GetHandle() : NULL; + return ::InvalidateRgn(m_hWnd, hRgn, bErase); + } + + inline BOOL CWnd::IsChild(CWnd* pChild) const + // The IsChild function tests whether a window is a child window or descendant window + // of a parent window's CWnd. + { + assert(::IsWindow(m_hWnd)); + return ::IsChild(m_hWnd, pChild->GetHwnd()); + } + + inline BOOL CWnd::IsDialogMessage(LPMSG lpMsg) const + // The IsDialogMessage function determines whether a message is intended for the specified dialog box and, + // if it is, processes the message. + { + assert(::IsWindow(m_hWnd)); + return ::IsDialogMessage(m_hWnd, lpMsg); + } + + inline UINT CWnd::IsDlgButtonChecked(int nIDButton) const + // The IsDlgButtonChecked function determines whether a button control has a check mark next to it + // or whether a three-state button control is grayed, checked, or neither. + { + assert(::IsWindow(m_hWnd)); + return ::IsDlgButtonChecked(m_hWnd, nIDButton); + } + + inline BOOL CWnd::IsWindowEnabled() const + // The IsWindowEnabled function determines whether the window is enabled + // for mouse and keyboard input. + { + assert(::IsWindow(m_hWnd)); + return ::IsWindowEnabled(m_hWnd); + } + + inline BOOL CWnd::IsWindowVisible() const + // The IsWindowVisible function retrieves the visibility state of the window. + { + assert(::IsWindow(m_hWnd)); + return ::IsWindowVisible(m_hWnd); + } + + inline BOOL CWnd::IsWindow() const + // The IsWindow function determines whether the window exists. + { + return ::IsWindow(m_hWnd); + } + + inline void CWnd::MapWindowPoints(CWnd* pWndTo, POINT& pt) const + // The MapWindowPoints function converts (maps) a set of points from a coordinate space relative to one + // window to a coordinate space relative to another window. + { + assert (m_hWnd); + if(pWndTo) + { + assert (pWndTo->GetHwnd()); + ::MapWindowPoints(m_hWnd, pWndTo->GetHwnd(), &pt, 1); + } + else + ::MapWindowPoints(m_hWnd, NULL, &pt, 1); + } + + inline void CWnd::MapWindowPoints(CWnd* pWndTo, RECT& rc) const + // The MapWindowPoints function converts (maps) a set of points from a coordinate space relative to one + // window to a coordinate space relative to another window. + { + assert (m_hWnd); + if(pWndTo) + { + assert (pWndTo->GetHwnd()); + ::MapWindowPoints(m_hWnd, pWndTo->GetHwnd(), (LPPOINT)&rc, 2); + } + else + ::MapWindowPoints(m_hWnd, NULL, (LPPOINT)&rc, 2); + } + + inline void CWnd::MapWindowPoints(CWnd* pWndTo, LPPOINT ptArray, UINT nCount) const + // The MapWindowPoints function converts (maps) a set of points from a coordinate space relative to one + // window to a coordinate space relative to another window. + { + assert (m_hWnd); + if (pWndTo) + { + assert (pWndTo->GetHwnd()); + ::MapWindowPoints(m_hWnd, pWndTo->GetHwnd(), (LPPOINT)ptArray, nCount); + } + else + ::MapWindowPoints(m_hWnd, NULL, (LPPOINT)ptArray, nCount); + } + + inline int CWnd::MessageBox(LPCTSTR lpText, LPCTSTR lpCaption, UINT uType) const + // The MessageBox function creates, displays, and operates a message box. + // Possible combinations of uType values include: MB_OK, MB_HELP, MB_OKCANCEL, MB_RETRYCANCEL, + // MB_YESNO, MB_YESNOCANCEL, MB_ICONEXCLAMATION, MB_ICONWARNING, MB_ICONERROR (+ many others). + { + assert(::IsWindow(m_hWnd)); + return ::MessageBox(m_hWnd, lpText, lpCaption, uType); + } + + inline BOOL CWnd::MoveWindow(int x, int y, int nWidth, int nHeight, BOOL bRepaint /* = TRUE*/) const + // The MoveWindow function changes the position and dimensions of the window. + { + assert(::IsWindow(m_hWnd)); + return ::MoveWindow(m_hWnd, x, y, nWidth, nHeight, bRepaint = TRUE); + } + + inline BOOL CWnd::MoveWindow(const RECT& rc, BOOL bRepaint /* = TRUE*/) const + // The MoveWindow function changes the position and dimensions of the window. + { + assert(::IsWindow(m_hWnd)); + return ::MoveWindow(m_hWnd, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, bRepaint); + } + + inline BOOL CWnd::PostMessage(UINT uMsg, WPARAM wParam /*= 0L*/, LPARAM lParam /*= 0L*/) const + // The PostMessage function places (posts) a message in the message queue + // associated with the thread that created the window and returns without + // waiting for the thread to process the message. + { + assert(::IsWindow(m_hWnd)); + return ::PostMessage(m_hWnd, uMsg, wParam, lParam); + } + + inline BOOL CWnd::PostMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) const + // Required by by some macros + { + assert(::IsWindow(m_hWnd)); + return ::PostMessage(hWnd, uMsg, wParam, lParam); + } + + inline BOOL CWnd::RedrawWindow(LPCRECT lpRectUpdate, CRgn* pRgn, UINT flags) const + // The RedrawWindow function updates the specified rectangle or region in a window's client area. + { + assert(::IsWindow(m_hWnd)); + HRGN hRgn = pRgn? (HRGN)pRgn->GetHandle() : NULL; + return ::RedrawWindow(m_hWnd, lpRectUpdate, hRgn, flags); + } + + inline int CWnd::ReleaseDC(CDC* pDC) const + // The ReleaseDC function releases a device context (DC), freeing it for use + // by other applications. + { + assert(::IsWindow(m_hWnd)); + assert(pDC); + return ::ReleaseDC(m_hWnd, pDC->GetHDC()); + } + + inline BOOL CWnd::ScreenToClient(POINT& Point) const + // The ScreenToClient function converts the screen coordinates of a specified point on the screen to client-area coordinates. + { + assert(::IsWindow(m_hWnd)); + return ::ScreenToClient(m_hWnd, &Point); + } + + inline BOOL CWnd::ScreenToClient(RECT& rc) const + // The ScreenToClient function converts the screen coordinates of a specified RECT on the screen to client-area coordinates. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)::MapWindowPoints(NULL, m_hWnd, (LPPOINT)&rc, 2); + } + + inline LRESULT CWnd::SendDlgItemMessage(int nIDDlgItem, UINT Msg, WPARAM wParam, LPARAM lParam) const + // The SendDlgItemMessage function sends a message to the specified control in a dialog box. + { + assert(::IsWindow(m_hWnd)); + return ::SendDlgItemMessage(m_hWnd, nIDDlgItem, Msg, wParam, lParam); + } + + inline LRESULT CWnd::SendMessage(UINT uMsg, WPARAM wParam /*= 0L*/, LPARAM lParam /*= 0L*/) const + // The SendMessage function sends the specified message to a window or windows. + // It calls the window procedure for the window and does not return until the + // window procedure has processed the message. + { + assert(::IsWindow(m_hWnd)); + return ::SendMessage(m_hWnd, uMsg, wParam, lParam); + } + + inline LRESULT CWnd::SendMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) const + // Required by by some macros + { + assert(::IsWindow(m_hWnd)); + return ::SendMessage(hWnd, uMsg, wParam, lParam); + } + + inline BOOL CWnd::SendNotifyMessage(UINT Msg, WPARAM wParam, LPARAM lParam) const + // The SendNotifyMessage function sends the specified message to a window or windows. If the window was created by the + // calling thread, SendNotifyMessage calls the window procedure for the window and does not return until the window procedure + // has processed the message. If the window was created by a different thread, SendNotifyMessage passes the message to the + // window procedure and returns immediately; it does not wait for the window procedure to finish processing the message. + { + assert(::IsWindow(m_hWnd)); + return ::SendNotifyMessage(m_hWnd, Msg, wParam, lParam); + } + + inline CWnd* CWnd::SetActiveWindow() const + // The SetActiveWindow function activates the window, but + // not if the application is in the background. + { + assert(::IsWindow(m_hWnd)); + return FromHandle( ::SetActiveWindow(m_hWnd) ); + } + + inline CWnd* CWnd::SetCapture() const + // The SetCapture function sets the mouse capture to the window. + // SetCapture captures mouse input either when the mouse is over the capturing + // window, or when the mouse button was pressed while the mouse was over the + // capturing window and the button is still down. + { + assert(::IsWindow(m_hWnd)); + return FromHandle( ::SetCapture(m_hWnd) ); + } + + inline ULONG_PTR CWnd::SetClassLongPtr(int nIndex, LONG_PTR dwNewLong) const + // The SetClassLongPtr function replaces the specified value at the specified offset in the + // extra class memory or the WNDCLASSEX structure for the class to which the window belongs. + { + assert(::IsWindow(m_hWnd)); + return ::SetClassLongPtr(m_hWnd, nIndex, dwNewLong); + } + + inline CWnd* CWnd::SetFocus() const + // The SetFocus function sets the keyboard focus to the window. + { + assert(::IsWindow(m_hWnd)); + return FromHandle( ::SetFocus(m_hWnd) ); + } + + inline void CWnd::SetFont(CFont* pFont, BOOL bRedraw /* = TRUE*/) const + // Specifies the font that the window will use when drawing text. + { + assert(::IsWindow(m_hWnd)); + assert(pFont); + SendMessage(WM_SETFONT, (WPARAM)pFont->GetHandle(), (LPARAM)bRedraw); + } + + inline HICON CWnd::SetIcon(HICON hIcon, BOOL bBigIcon) const + // Associates a new large or small icon with a window. + { + assert(::IsWindow(m_hWnd)); + return (HICON)SendMessage(WM_SETICON, (WPARAM)bBigIcon, (LPARAM)hIcon); + } + + inline BOOL CWnd::SetForegroundWindow() const + // The SetForegroundWindow function puts the thread that created the window into the + // foreground and activates the window. + { + assert(::IsWindow(m_hWnd)); + return ::SetForegroundWindow(m_hWnd); + } + + inline CWnd* CWnd::SetParent(CWnd* pWndParent) const + // The SetParent function changes the parent window of the child window. + { + assert(::IsWindow(m_hWnd)); + if (pWndParent) + { + HWND hParent = pWndParent->GetHwnd(); + return FromHandle(::SetParent(m_hWnd, hParent)); + } + else + return FromHandle(::SetParent(m_hWnd, 0)); + } + + inline BOOL CWnd::SetRedraw(BOOL bRedraw /*= TRUE*/) const + // This function allows changes in that window to be redrawn or prevents changes + // in that window from being redrawn. + { + assert(::IsWindow(m_hWnd)); + return (BOOL)::SendMessage(m_hWnd, WM_SETREDRAW, (WPARAM)bRedraw, 0L); + } + + inline LONG_PTR CWnd::SetWindowLongPtr(int nIndex, LONG_PTR dwNewLong) const + // The SetWindowLongPtr function changes an attribute of the window. + { + assert(::IsWindow(m_hWnd)); + return ::SetWindowLongPtr(m_hWnd, nIndex, dwNewLong); + } + + inline BOOL CWnd::SetWindowPos(HWND hWndInsertAfter, int x, int y, int cx, int cy, UINT uFlags) const + // The SetWindowPos function changes the size, position, and Z order of a child, pop-up, + // or top-level window. The hWndInsertAfter can be a HWND or one of: + // HWND_BOTTOM, HWND_NOTOPMOST, HWND_TOP, HWND_TOPMOST + { + assert(::IsWindow(m_hWnd)); + return ::SetWindowPos(m_hWnd, hWndInsertAfter, x, y, cx, cy, uFlags); + } + + inline BOOL CWnd::SetWindowPos(HWND hWndInsertAfter, const RECT& rc, UINT uFlags) const + // The SetWindowPos function changes the size, position, and Z order of a child, pop-up, + // or top-level window. The hWndInsertAfter can be a HWND or one of: + // HWND_BOTTOM, HWND_NOTOPMOST, HWND_TOP, HWND_TOPMOST + { + assert(::IsWindow(m_hWnd)); + return ::SetWindowPos(m_hWnd, hWndInsertAfter, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, uFlags); + } + + inline int CWnd::SetWindowRgn(CRgn* pRgn, BOOL bRedraw /*= TRUE*/) const + // The SetWindowRgn function sets the window region of the window. + // The window region determines the area within the window where the system permits drawing. + { + assert(::IsWindow(m_hWnd)); + HRGN hRgn = pRgn? (HRGN)pRgn->GetHandle() : NULL; + int iResult = ::SetWindowRgn(m_hWnd, hRgn, bRedraw); + if (iResult && pRgn) + pRgn->Detach(); // The system owns the region now + return iResult; + } + + inline BOOL CWnd::SetDlgItemInt(int nIDDlgItem, UINT uValue, BOOL bSigned) const + // The SetDlgItemInt function sets the text of a control in a dialog box to the string representation of a specified integer value. + { + assert(::IsWindow(m_hWnd)); + return ::SetDlgItemInt(m_hWnd, nIDDlgItem, uValue, bSigned); + } + + inline BOOL CWnd::SetDlgItemText(int nIDDlgItem, LPCTSTR lpString) const + // The SetDlgItemText function sets the title or text of a control in a dialog box. + { + assert(::IsWindow(m_hWnd)); + return ::SetDlgItemText(m_hWnd, nIDDlgItem, lpString); + } + + inline UINT_PTR CWnd::SetTimer(UINT_PTR nIDEvent, UINT uElapse, TIMERPROC lpTimerFunc) const + // Creates a timer with the specified time-out value. + { + assert(::IsWindow(m_hWnd)); + return ::SetTimer(m_hWnd, nIDEvent, uElapse, lpTimerFunc); + } + + inline BOOL CWnd::SetWindowText(LPCTSTR lpString) const + // The SetWindowText function changes the text of the window's title bar (if it has one). + { + assert(::IsWindow(m_hWnd)); + return ::SetWindowText(m_hWnd, lpString); + } + + inline HRESULT CWnd::SetWindowTheme(LPCWSTR pszSubAppName, LPCWSTR pszSubIdList) const + // Set the XP Theme for a window. + // Exampes: + // SetWindowTheme(NULL, NULL); // Reverts the window's XP theme back to default + // SetWindowTheme(L" ", L" "); // Disables XP theme for the window + { + HRESULT hr = E_NOTIMPL; + +#ifndef _WIN32_WCE + + HMODULE hMod = ::LoadLibrary(_T("uxtheme.dll")); + if(hMod) + { + typedef HRESULT (__stdcall *PFNSETWINDOWTHEME)(HWND hWnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList); + PFNSETWINDOWTHEME pfn = (PFNSETWINDOWTHEME)GetProcAddress(hMod, "SetWindowTheme"); + + hr = (*pfn)(m_hWnd, pszSubAppName, pszSubIdList); + + ::FreeLibrary(hMod); + } + +#endif + + return hr; + } + + inline BOOL CWnd::ShowWindow(int nCmdShow /*= SW_SHOWNORMAL*/) const + // The ShowWindow function sets the window's show state. + { + assert(::IsWindow(m_hWnd)); + return ::ShowWindow(m_hWnd, nCmdShow); + } + + inline BOOL CWnd::UpdateWindow() const + // The UpdateWindow function updates the client area of the window by sending a + // WM_PAINT message to the window if the window's update region is not empty. + // If the update region is empty, no message is sent. + { + assert(::IsWindow(m_hWnd)); + return ::UpdateWindow(m_hWnd); + } + + inline BOOL CWnd::ValidateRect(LPCRECT prc) const + // The ValidateRect function validates the client area within a rectangle by + // removing the rectangle from the update region of the window. + { + assert(::IsWindow(m_hWnd)); + return ::ValidateRect(m_hWnd, prc); + } + + inline BOOL CWnd::ValidateRgn(CRgn* pRgn) const + // The ValidateRgn function validates the client area within a region by + // removing the region from the current update region of the window. + { + assert(::IsWindow(m_hWnd)); + HRGN hRgn = pRgn? (HRGN)pRgn->GetHandle() : NULL; + return ::ValidateRgn(m_hWnd, hRgn); + } + + inline CWnd* CWnd::WindowFromPoint(POINT pt) + // Retrieves the window that contains the specified point (in screen coodinates). + { + return FromHandle(::WindowFromPoint(pt)); + } + + // + // These functions aren't supported on WinCE + // + #ifndef _WIN32_WCE + inline BOOL CWnd::CloseWindow() const + // The CloseWindow function minimizes (but does not destroy) the window. + // To destroy a window, an application can use the Destroy function. + { + assert(::IsWindow(m_hWnd)); + return ::CloseWindow(m_hWnd); + } + + inline int CWnd::DlgDirList(LPTSTR lpPathSpec, int nIDListBox, int nIDStaticPath, UINT uFileType) const + // The DlgDirList function replaces the contents of a list box with the names of the subdirectories and files + // in a specified directory. You can filter the list of names by specifying a set of file attributes. + { + assert(::IsWindow(m_hWnd)); + return ::DlgDirList(m_hWnd, lpPathSpec, nIDListBox, nIDStaticPath, uFileType); + } + + inline int CWnd::DlgDirListComboBox(LPTSTR lpPathSpec, int nIDComboBox, int nIDStaticPath, UINT uFiletype) const + // The DlgDirListComboBox function replaces the contents of a combo box with the names of the subdirectories + // and files in a specified directory. You can filter the list of names by specifying a set of file attributes. + { + assert(::IsWindow(m_hWnd)); + return ::DlgDirListComboBox(m_hWnd, lpPathSpec, nIDComboBox, nIDStaticPath, uFiletype); + } + + inline BOOL CWnd::DlgDirSelectEx(LPTSTR lpString, int nCount, int nIDListBox) const + // The DlgDirSelectEx function retrieves the current selection from a single-selection list box. It assumes that the list box + // has been filled by the DlgDirList function and that the selection is a drive letter, filename, or directory name. + { + assert(::IsWindow(m_hWnd)); + return ::DlgDirSelectEx(m_hWnd, lpString, nCount, nIDListBox); + } + + inline BOOL CWnd::DlgDirSelectComboBoxEx(LPTSTR lpString, int nCount, int nIDComboBox) const + // The DlgDirSelectComboBoxEx function retrieves the current selection from a combo box filled by using the + // DlgDirListComboBox function. The selection is interpreted as a drive letter, a file, or a directory name. + { + assert(::IsWindow(m_hWnd)); + return ::DlgDirSelectComboBoxEx(m_hWnd, lpString, nCount, nIDComboBox); + } + + #ifndef WIN32_LEAN_AND_MEAN + inline void CWnd::DragAcceptFiles(BOOL fAccept) const + // Registers whether a window accepts dropped files. + { + assert(::IsWindow(m_hWnd)); + ::DragAcceptFiles(m_hWnd, fAccept); + } + #endif + + inline BOOL CWnd::DrawAnimatedRects(int idAni, RECT& rcFrom, RECT& rcTo) const + // The DrawAnimatedRects function draws a wire-frame rectangle and animates it to indicate the opening of + // an icon or the minimizing or maximizing of a window. + { + assert(::IsWindow(m_hWnd)); + return ::DrawAnimatedRects(m_hWnd, idAni, &rcFrom, &rcTo); + } + + inline BOOL CWnd::DrawCaption(CDC* pDC, RECT& rc, UINT uFlags) const + // The DrawCaption function draws a window caption. + { + assert(::IsWindow(m_hWnd)); + assert(pDC); + return ::DrawCaption(m_hWnd, pDC->GetHDC(), &rc, uFlags); + } + + inline BOOL CWnd::EnableScrollBar(UINT uSBflags, UINT uArrows) const + // The EnableScrollBar function enables or disables one or both scroll bar arrows. + { + assert(::IsWindow(m_hWnd)); + return ::EnableScrollBar(m_hWnd, uSBflags, uArrows); + } + + inline CWnd* CWnd::GetLastActivePopup() const + // The GetLastActivePopup function determines which pop-up window owned by the specified window was most recently active. + { + assert(::IsWindow(m_hWnd)); + return FromHandle( ::GetLastActivePopup(m_hWnd) ); + } + + inline CMenu* CWnd::GetMenu() const + // The GetMenu function retrieves a handle to the menu assigned to the window. + { + assert(::IsWindow(m_hWnd)); + return FromHandle(::GetMenu(m_hWnd)); + } + + inline int CWnd::GetScrollPos(int nBar) const + // The GetScrollPos function retrieves the current position of the scroll box + // (thumb) in the specified scroll bar. + { + assert(::IsWindow(m_hWnd)); + return ::GetScrollPos(m_hWnd, nBar); + } + + inline BOOL CWnd::GetScrollRange(int nBar, int& MinPos, int& MaxPos) const + // The GetScrollRange function retrieves the current minimum and maximum scroll box + // (thumb) positions for the specified scroll bar. + { + assert(::IsWindow(m_hWnd)); + return ::GetScrollRange(m_hWnd, nBar, &MinPos, &MaxPos ); + } + + inline CMenu* CWnd::GetSystemMenu(BOOL bRevert) const + // The GetSystemMenu function allows the application to access the window menu (also known as the system menu + // or the control menu) for copying and modifying. + { + assert(::IsWindow(m_hWnd)); + return FromHandle(::GetSystemMenu(m_hWnd, bRevert)); + } + + inline CWnd* CWnd::GetTopWindow() const + // The GetTopWindow function examines the Z order of the child windows associated with the parent window and + // retrieves a handle to the child window at the top of the Z order. + { + assert(::IsWindow(m_hWnd)); + return FromHandle( ::GetTopWindow(m_hWnd) ); + } + + inline BOOL CWnd::GetWindowPlacement(WINDOWPLACEMENT& wndpl) const + // The GetWindowPlacement function retrieves the show state and the restored, + // minimized, and maximized positions of the window. + { + assert(::IsWindow(m_hWnd)); + return ::GetWindowPlacement(m_hWnd, &wndpl); + } + + inline BOOL CWnd::HiliteMenuItem(CMenu* pMenu, UINT uItemHilite, UINT uHilite) const + // The HiliteMenuItem function highlights or removes the highlighting from an item in a menu bar. + { + assert(::IsWindow(m_hWnd)); + assert(pMenu); + return ::HiliteMenuItem(m_hWnd, pMenu->GetHandle(), uItemHilite, uHilite); + } + + inline BOOL CWnd::IsIconic() const + // The IsIconic function determines whether the window is minimized (iconic). + { + assert(::IsWindow(m_hWnd)); + return ::IsIconic(m_hWnd); + } + + inline BOOL CWnd::IsZoomed() const + // The IsZoomed function determines whether the window is maximized. + { + assert(::IsWindow(m_hWnd)); + return ::IsZoomed(m_hWnd); + } + + inline BOOL CWnd::KillTimer(UINT_PTR uIDEvent) const + // Destroys the specified timer. + { + assert(::IsWindow(m_hWnd)); + return ::KillTimer(m_hWnd, uIDEvent); + } + + inline BOOL CWnd::LockWindowUpdate() const + // Disables drawing in the window. Only one window can be locked at a time. + // Use UnLockWindowUpdate to re-enable drawing in the window + { + assert(::IsWindow(m_hWnd)); + return ::LockWindowUpdate(m_hWnd); + } + + inline BOOL CWnd::OpenIcon() const + // The OpenIcon function restores a minimized (iconic) window to its previous size and position. + { + assert(::IsWindow(m_hWnd)); + return ::OpenIcon(m_hWnd); + } + + inline void CWnd::Print(CDC* pDC, DWORD dwFlags) const + // Requests that the window draw itself in the specified device context, most commonly in a printer device context. + { + assert(::IsWindow(m_hWnd)); + assert(pDC); + SendMessage(m_hWnd, WM_PRINT, (WPARAM)pDC, (LPARAM)dwFlags); + } + + inline BOOL CWnd::ScrollWindow(int XAmount, int YAmount, LPCRECT lprcScroll, LPCRECT lprcClip) const + // The ScrollWindow function scrolls the contents of the specified window's client area. + { + assert(::IsWindow(m_hWnd)); + return ::ScrollWindow(m_hWnd, XAmount, YAmount, lprcScroll, lprcClip); + } + + inline int CWnd::ScrollWindowEx(int dx, int dy, LPCRECT lprcScroll, LPCRECT lprcClip, CRgn* prgnUpdate, LPRECT lprcUpdate, UINT flags) const + // The ScrollWindow function scrolls the contents of the window's client area. + { + assert(::IsWindow(m_hWnd)); + HRGN hrgnUpdate = prgnUpdate? (HRGN)prgnUpdate->GetHandle() : NULL; + return ::ScrollWindowEx(m_hWnd, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate, flags); + } + + inline BOOL CWnd::SetMenu(CMenu* pMenu) const + // The SetMenu function assigns a menu to the specified window. + // A hMenu of NULL removes the menu. + { + assert(::IsWindow(m_hWnd)); + return ::SetMenu(m_hWnd, pMenu? pMenu->GetHandle() : NULL); + } + + inline int CWnd::SetScrollInfo(int fnBar, const SCROLLINFO& si, BOOL fRedraw) const + // The SetScrollInfo function sets the parameters of a scroll bar, including + // the minimum and maximum scrolling positions, the page size, and the + // position of the scroll box (thumb). + { + assert(::IsWindow(m_hWnd)); + return ::SetScrollInfo(m_hWnd, fnBar, &si, fRedraw); + } + + inline int CWnd::SetScrollPos(int nBar, int nPos, BOOL bRedraw) const + // The SetScrollPos function sets the position of the scroll box (thumb) in + // the specified scroll bar. + { + assert(::IsWindow(m_hWnd)); + return ::SetScrollPos(m_hWnd, nBar, nPos, bRedraw); + } + + inline BOOL CWnd::SetScrollRange(int nBar, int nMinPos, int nMaxPos, BOOL bRedraw) const + // The SetScrollRange function sets the minimum and maximum scroll box positions for the scroll bar. + { + assert(::IsWindow(m_hWnd)); + return ::SetScrollRange(m_hWnd, nBar, nMinPos, nMaxPos, bRedraw); + } + + inline BOOL CWnd::SetWindowPlacement(const WINDOWPLACEMENT& wndpl) const + // The SetWindowPlacement function sets the show state and the restored, minimized, + // and maximized positions of the window. + { + assert(::IsWindow(m_hWnd)); + return ::SetWindowPlacement(m_hWnd, &wndpl); + } + + inline BOOL CWnd::ShowOwnedPopups(BOOL fShow) const + // The ShowOwnedPopups function shows or hides all pop-up windows owned by the specified window. + { + assert(::IsWindow(m_hWnd)); + return ::ShowOwnedPopups(m_hWnd, fShow); + } + + inline BOOL CWnd::ShowScrollBar(int nBar, BOOL bShow) const + // The ShowScrollBar function shows or hides the specified scroll bar. + { + assert(::IsWindow(m_hWnd)); + return ::ShowScrollBar(m_hWnd, nBar, bShow); + } + + inline BOOL CWnd::ShowWindowAsync(int nCmdShow) const + // The ShowWindowAsync function sets the show state of a window created by a different thread. + { + assert(::IsWindow(m_hWnd)); + return ::ShowWindowAsync(m_hWnd, nCmdShow); + } + + inline BOOL CWnd::UnLockWindowUpdate() const + // Enables drawing in the window. Only one window can be locked at a time. + // Use LockWindowUpdate to disable drawing in the window + { + assert(::IsWindow(m_hWnd)); + return ::LockWindowUpdate(0); + } + + inline CWnd* CWnd::WindowFromDC(CDC* pDC) const + // The WindowFromDC function returns a handle to the window associated with the specified display device context (DC). + { + assert(pDC); + return FromHandle( ::WindowFromDC(pDC->GetHDC()) ); + } + + #endif + +}; // namespace Win32xx + + +#endif // _WIN32XX_WINCORE_H_ + diff --git a/mmc_updater/depends/win32cpp/winutils.h b/mmc_updater/depends/win32cpp/winutils.h new file mode 100644 index 00000000..94ba2d80 --- /dev/null +++ b/mmc_updater/depends/win32cpp/winutils.h @@ -0,0 +1,649 @@ +// Win32++ Version 7.2 +// Released: 5th AUgust 2011 +// +// David Nash +// email: dnash@bigpond.net.au +// url: https://sourceforge.net/projects/win32-framework +// +// +// Copyright (c) 2005-2011 David Nash +// +// Permission is hereby granted, free of charge, to +// any person obtaining a copy of this software and +// associated documentation files (the "Software"), +// to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, +// merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom +// the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice +// shall be included in all copies or substantial portions +// of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE +// OR OTHER DEALINGS IN THE SOFTWARE. +// +//////////////////////////////////////////////////////// + +#ifndef _WIN32XX_WINUTILS_H_ +#define _WIN32XX_WINUTILS_H_ + + +// define useful macros from WindowsX.h +#ifndef GET_X_LPARAM + #define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) +#endif +#ifndef GET_Y_LPARAM + #define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) +#endif + +// Define our own MIN and MAX macros +// this avoids inconsistencies with Dev-C++ and other compilers, and +// avoids conflicts between typical min/max macros and std::min/std::max +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) + + +namespace Win32xx +{ + // Forward declarations + class CPoint; + class CRect; + CWinApp* GetApp(); + void TRACE(LPCTSTR str); + + + ///////////////////////////////////////// + // Definition of the CSize class + // This class can be used to replace the SIZE structure + class CSize : public SIZE + { + public: + CSize() { cx = 0; cy = 0; } + CSize(int CX, int CY) { cx = CX; cy = CY; } + CSize(SIZE sz) { cx = sz.cx; cy = sz.cy; } + CSize(POINT pt) { cx = pt.x; cy = pt.y; } + CSize(DWORD dw) { cx = (short)LOWORD(dw); cy = (short)HIWORD(dw); } + void SetSize(int CX, int CY) { cx = CX; cy = CY; } + + // Operators + operator LPSIZE() { return this; } + BOOL operator == (SIZE sz) const { return (cx == sz.cx && cy == sz.cy); } + BOOL operator != (SIZE sz) const { return (cx != sz.cx || cy != sz.cy); } + void operator += (SIZE sz) { cx += sz.cx; cy += sz.cy; } + void operator -= (SIZE sz) { cx -= sz.cx; cy -= sz.cy; } + + // Operators returning CSize + CSize operator - () const { return CSize (-cx, -cy); } + CSize operator + (SIZE sz) const { return CSize (cx + sz.cx, cy + sz.cy); } + CSize operator - (SIZE sz) const { return CSize (cx - sz.cx, cy - sz.cy); } + + // Operators returning CPoint + CPoint operator + (POINT point) const; + CPoint operator - (POINT point) const; + + // Operators returning CRect + CRect operator + (RECT rc) const; + CRect operator - (RECT rc) const; + }; + + + ///////////////////////////////////////// + // Definition of the CPoint class + // This class can be used to replace the POINT structure + class CPoint : public POINT + { + public: + CPoint() { x = 0; y = 0; } + CPoint(int X, int Y) { x = X; y = Y; } + CPoint(POINT pt) { x = pt.x ; y = pt.y; } + CPoint(POINTS pts) { x = pts.x; y = pts.y; } + CPoint(SIZE sz) { x = sz.cx; y = sz.cy; } + CPoint(DWORD dw) { x = (short) LOWORD(dw); y = (short) HIWORD(dw); } + + void Offset(int dx, int dy) { x += dx; y += dy; } + void Offset(POINT pt) { x += pt.x; y += pt.y; } + void Offset(SIZE sz) { x += sz.cx; y += sz.cy; } + void SetPoint(int X, int Y) { x = X; y = Y; } + + // Operators + operator LPPOINT() { return this; } + BOOL operator == (POINT pt) const { return ((x == pt.x) && (y == pt.y)); } + BOOL operator != (POINT pt) const { return ((x != pt.x) || (y != pt.y)); } + void operator += (SIZE sz) { x += sz.cx; y += sz.cy; } + void operator -= (SIZE sz) { x -= sz.cx; y -= sz.cy; } + void operator += (POINT pt) { x += pt.x; y += pt.y; } + void operator -= (POINT pt) { x -= pt.x; y -= pt.y; } + + // Operators returning CPoint + CPoint operator - () const { return CPoint(-x, -y); } + CPoint operator + (SIZE sz) const { return CPoint(x + sz.cx, y + sz.cy); } + CPoint operator - (SIZE sz) const { return CPoint(x - sz.cx, y - sz.cy); } + CPoint operator + (POINT pt) const { return CPoint(x + pt.x, y + pt.y); } + CPoint operator - (POINT pt) const { return CPoint(x - pt.x, y - pt.y); } + + // Operators returning CRect + CRect operator + (RECT rc) const; + CRect operator - (RECT rc) const; + }; + + + ///////////////////////////////////////// + // Definition of the CRect class + // This class can be used to replace the RECT structure. + class CRect : public RECT + { + public: + CRect() { left = top = right = bottom = 0; } + CRect(int l, int t, int r, int b) { left = l; top = t; right = r; bottom = b; } + CRect(RECT rc) { left = rc.left; top = rc.top; right = rc.right; bottom = rc.bottom; } + CRect(POINT pt, SIZE sz) { right = (left = pt.x) + sz.cx; bottom = (top = pt.y) + sz.cy; } + CRect(POINT topLeft, POINT bottomRight) { left = topLeft.x; top = topLeft.y; right = bottomRight.x; bottom = bottomRight.y; } + + BOOL CopyRect(RECT rc) { return ::CopyRect(this, &rc); } + BOOL DeflateRect(int x, int y) { return ::InflateRect(this, -x, -y); } + BOOL DeflateRect(SIZE size) { return ::InflateRect(this, -size.cx, -size.cy); } + BOOL DeflateRect(RECT rc) { return ::InflateRect(this, rc.left - rc.right, rc.top - rc.bottom); } + BOOL DeflateRect(int l, int t, int r, int b){ return ::InflateRect(this, l - r, t - b); } + BOOL EqualRect(RECT rc) const { return ::EqualRect(&rc, this); } + BOOL InflateRect(int dx, int dy) { return ::InflateRect(this, dx, dy); } + BOOL InflateRect(SIZE sz) { return ::InflateRect(this, sz.cx, sz.cy); } + BOOL InflateRect(RECT rc) { return ::InflateRect(this, rc.right - rc.left, rc.bottom - rc.top); } + BOOL InflateRect(int l, int t, int r, int b){ return ::InflateRect(this, r - l, b - t); } + BOOL IntersectRect(RECT rc1, RECT rc2) { return ::IntersectRect(this, &rc1, &rc2); } + BOOL IsRectEmpty() const { return ::IsRectEmpty(this);} + BOOL IsRectNull() const { return (left == 0 && right == 0 && top == 0 && bottom == 0); } + CRect MulDiv(int nMult, int nDiv) const { return CRect ((left * nMult) / nDiv, (top * nMult) / nDiv, + (right * nMult) / nDiv, (bottom * nMult) / nDiv); } + void NormalizeRect() { int nTemp; if (left > right) { nTemp = left; left = right; right = nTemp; } + if (top > bottom) { nTemp = top; top = bottom; bottom = nTemp; } } + BOOL OffsetRect(int dx, int dy) { return ::OffsetRect(this, dx, dy); } + BOOL OffsetRect(POINT pt) { return ::OffsetRect(this, pt.x, pt.y); } + BOOL OffsetRect(SIZE size) { return ::OffsetRect(this, size.cx, size.cy); } + BOOL PtInRect(POINT pt) const { return ::PtInRect(this, pt); } + BOOL SetRect(int l, int t, int r, int b) { return ::SetRect(this, l, t, r, b); } + BOOL SetRect(POINT TopLeft, POINT BtmRight) { return ::SetRect(this, TopLeft.x, TopLeft.y, BtmRight.x, BtmRight.y); } + BOOL SetRectEmpty() { return ::SetRectEmpty(this); } + BOOL SubtractRect(RECT rc1, RECT rc2) { return ::SubtractRect(this, &rc1, &rc2); } + BOOL UnionRect(RECT rc1, RECT rc2) { return ::UnionRect(this, &rc1, &rc2); } + + // Reposition rectangle + void MoveToX (int x) { right = Width() + x; left = x; } + void MoveToY (int y) { bottom = Height() + y; top = y; } + void MoveToXY (int x, int y) { MoveToX(x); MoveToY(y); } + void MoveToXY (POINT pt) { MoveToX (pt.x); MoveToY (pt.y); } + + // Attributes + int Height() const { return bottom - top; } + int Width() const { return right - left; } + CSize Size() const { return CSize(Width(), Height()); } + CPoint CenterPoint() const { return CPoint((left + right) / 2, (top + bottom) / 2); } + CPoint TopLeft() const { return CPoint(left, top); } + CPoint BottomRight() const { return CPoint(right, bottom); } + + // operators + operator LPRECT() { return this; } + BOOL operator == (RECT rc) const { return ::EqualRect(this, &rc); } + BOOL operator != (RECT rc) const { return !::EqualRect(this, &rc); } + void operator += (POINT pt) { ::OffsetRect(this, pt.x, pt.y); } + void operator += (SIZE size) { ::OffsetRect(this, size.cx, size.cy); } + void operator += (RECT rc) { ::InflateRect(this, rc.right - rc.left, rc.bottom - rc.top); } + void operator -= (RECT rc) { ::InflateRect(this, rc.left - rc.right, rc.top - rc.bottom); } + void operator -= (POINT pt) { ::OffsetRect(this, -pt.x, -pt.y); } + void operator -= (SIZE sz) { ::OffsetRect(this, -sz.cx, -sz.cy); } + void operator &= (RECT rc) { ::IntersectRect(this, this, &rc); } + void operator |= (RECT rc) { ::UnionRect(this, this, &rc); } + + // Operators returning CRect + CRect operator + (POINT pt) const { CRect rc(*this); ::OffsetRect(&rc, pt.x, pt.y); return rc; } + CRect operator - (POINT pt) const { CRect rc(*this); ::OffsetRect(&rc, -pt.x, -pt.y); return rc; } + CRect operator + (SIZE sz) const { CRect rc(*this); ::OffsetRect(&rc, sz.cx, sz.cy); return rc; } + CRect operator - (SIZE sz) const { CRect rc(*this); ::OffsetRect(&rc, -sz.cx, -sz.cy); return rc; } + CRect operator + (RECT rc) const { CRect rc1(*this); rc1.InflateRect(rc); return rc1; } + CRect operator - (RECT rc) const { CRect rc1(*this); rc1.DeflateRect(rc); return rc1; } + CRect operator & (RECT rc) const { CRect rc1; ::IntersectRect(&rc1, this, &rc); return rc1; } + CRect operator | (RECT rc) const { CRect rc1; ::UnionRect(&rc1, this, &rc); return rc1; } + }; + + // CSize member function definitions + inline CPoint CSize::operator + (POINT pt) const { return CPoint(pt) + *this; } + inline CPoint CSize::operator - (POINT pt) const { return CPoint(pt) - *this; } + inline CRect CSize::operator + (RECT rc) const { return CRect(rc) + *this; } + inline CRect CSize::operator - (RECT rc) const { return CRect(rc) - *this; } + + // CPoint member function definitions + inline CRect CPoint::operator + (RECT rc) const { return CRect(rc) + *this; } + inline CRect CPoint::operator - (RECT rc) const { return CRect(rc) - *this; } + + + //////////////////////////////////////////////////////// + // Classes and functions (typedefs) for text conversions + // + // This section defines the following text conversions: + // A2BSTR ANSI to BSTR + // A2OLE ANSI to OLE + // A2T ANSI to TCHAR + // A2W ANSI to WCHAR + // OLE2A OLE to ANSI + // OLE2T OLE to TCHAR + // OLE2W OLE to WCHAR + // T2A TCHAR to ANSI + // T2BSTR TCHAR to BSTR + // T2OLE TCHAR to OLE + // T2W TCHAR to WCHAR + // W2A WCHAR to ANSI + // W2BSTR WCHAR to BSTR + // W2OLE WCHAR to OLE + // W2T WCHAR to TCHAR + + // About different character and string types: + // ------------------------------------------ + // char (or CHAR) character types are ANSI (8 bits). + // wchar_t (or WCHAR) character types are Unicode (16 bits). + // TCHAR characters are Unicode if the _UNICODE macro is defined, otherwise they are ANSI. + // BSTR (Basic String) is a type of string used in Visual Basic and COM programming. + // OLE is the same as WCHAR. It is used in Visual Basic and COM programming. + + + // Forward declarations of our classes. They are defined later. + class CA2A; + class CA2W; + class CW2A; + class CW2W; + class CA2BSTR; + class CW2BSTR; + + // typedefs for the well known text conversions + typedef CA2W A2W; + typedef CW2A W2A; + typedef CW2BSTR W2BSTR; + typedef CA2BSTR A2BSTR; + typedef CW2A BSTR2A; + typedef CW2W BSTR2W; + +#ifdef _UNICODE + typedef CA2W A2T; + typedef CW2A T2A; + typedef CW2W T2W; + typedef CW2W W2T; + typedef CW2BSTR T2BSTR; + typedef BSTR2W BSTR2T; +#else + typedef CA2A A2T; + typedef CA2A T2A; + typedef CA2W T2W; + typedef CW2A W2T; + typedef CA2BSTR T2BSTR; + typedef BSTR2A BSTR2T; +#endif + + typedef A2W A2OLE; + typedef T2W T2OLE; + typedef CW2W W2OLE; + typedef W2A OLE2A; + typedef W2T OLE2T; + typedef CW2W OLE2W; + + class CA2W + { + public: + CA2W(LPCSTR pStr) : m_pStr(pStr) + { + if (pStr) + { + // Resize the vector and assign null WCHAR to each element + int length = (int)strlen(pStr)+1; + m_vWideArray.assign(length, L'\0'); + + // Fill our vector with the converted WCHAR array + MultiByteToWideChar(CP_ACP, 0, pStr, -1, &m_vWideArray[0], length); + } + } + ~CA2W() {} + operator LPCWSTR() { return m_pStr? &m_vWideArray[0] : NULL; } + operator LPOLESTR() { return m_pStr? (LPOLESTR)&m_vWideArray[0] : (LPOLESTR)NULL; } + operator LPBSTR() { return m_pStr? (LPBSTR)&m_vWideArray[0] : (LPBSTR)NULL; } + + private: + CA2W(const CA2W&); + CA2W& operator= (const CA2W&); + std::vector<wchar_t> m_vWideArray; + LPCSTR m_pStr; + }; + + class CW2A + { + public: + CW2A(LPCWSTR pWStr) : m_pWStr(pWStr) + { + // Resize the vector and assign null char to each element + int length = (int)wcslen(pWStr)+1; + m_vAnsiArray.assign(length, '\0'); + + // Fill our vector with the converted char array + WideCharToMultiByte(CP_ACP, 0, pWStr, -1, &m_vAnsiArray[0], length, NULL,NULL); + } + + ~CW2A() {} + operator LPCSTR() { return m_pWStr? &m_vAnsiArray[0] : NULL; } + + private: + CW2A(const CW2A&); + CW2A& operator= (const CW2A&); + std::vector<char> m_vAnsiArray; + LPCWSTR m_pWStr; + }; + + class CW2W + { + public: + CW2W(LPCWSTR pWStr) : m_pWStr(pWStr) {} + operator LPCWSTR() { return (LPWSTR)m_pWStr; } + operator LPOLESTR() { return (LPOLESTR)m_pWStr; } + + private: + CW2W(const CW2W&); + CW2W& operator= (const CW2W&); + + LPCWSTR m_pWStr; + }; + + class CA2A + { + public: + CA2A(LPCSTR pStr) : m_pStr(pStr) {} + operator LPCSTR() { return (LPSTR)m_pStr; } + + private: + CA2A(const CA2A&); + CA2A& operator= (const CA2A&); + + LPCSTR m_pStr; + }; + + class CW2BSTR + { + public: + CW2BSTR(LPCWSTR pWStr) { m_bstrString = ::SysAllocString(pWStr); } + ~CW2BSTR() { ::SysFreeString(m_bstrString); } + operator BSTR() { return m_bstrString;} + + private: + CW2BSTR(const CW2BSTR&); + CW2BSTR& operator= (const CW2BSTR&); + BSTR m_bstrString; + }; + + class CA2BSTR + { + public: + CA2BSTR(LPCSTR pStr) { m_bstrString = ::SysAllocString(A2W(pStr)); } + ~CA2BSTR() { ::SysFreeString(m_bstrString); } + operator BSTR() { return m_bstrString;} + + private: + CA2BSTR(const CA2BSTR&); + CA2BSTR& operator= (const CA2BSTR&); + BSTR m_bstrString; + }; + + + //////////////////////////////////////// + // Global Functions + // + + inline CWnd* FromHandle(HWND hWnd) + // Returns the CWnd object associated with the window handle + { + assert( GetApp() ); + CWnd* pWnd = GetApp()->GetCWndFromMap(hWnd); + if (::IsWindow(hWnd) && pWnd == 0) + { + GetApp()->AddTmpWnd(hWnd); + pWnd = GetApp()->GetCWndFromMap(hWnd); + ::PostMessage(hWnd, UWM_CLEANUPTEMPS, 0, 0); + } + + return pWnd; + } + + + inline CWinApp* GetApp() + // Returns a pointer to the CWinApp derrived class + { + return CWinApp::SetnGetThis(); + } + + inline CPoint GetCursorPos() + { + CPoint pt; + ::GetCursorPos(&pt); + return pt; + } + + inline HBITMAP LoadBitmap (LPCTSTR lpszName) + { + assert(GetApp()); + + HBITMAP hBitmap = (HBITMAP)::LoadImage (GetApp()->GetResourceHandle(), lpszName, IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR); + return hBitmap; + } + + inline HBITMAP LoadBitmap (int nID) + { + return LoadBitmap(MAKEINTRESOURCE(nID)); + } + + + inline void TRACE(LPCTSTR str) + // TRACE sends a string to the debug/output pane, or an external debugger + { + #ifdef _DEBUG + OutputDebugString(str); + #else + UNREFERENCED_PARAMETER(str); // no-op + #endif + } + + #ifndef _WIN32_WCE // for Win32/64 operating systems, not WinCE + + inline int GetWinVersion() + { + DWORD dwVersion = GetVersion(); + int Platform = (dwVersion < 0x80000000)? 2:1; + int MajorVer = LOBYTE(LOWORD(dwVersion)); + int MinorVer = HIBYTE(LOWORD(dwVersion)); + + int nVersion = 1000*Platform + 100*MajorVer + MinorVer; + + // Return values and window versions: + // 1400 Windows 95 + // 1410 Windows 98 + // 1490 Windows ME + // 2400 Windows NT + // 2500 Windows 2000 + // 2501 Windows XP + // 2502 Windows Server 2003 + // 2600 Windows Vista and Windows Server 2008 + // 2601 Windows 7 + + return nVersion; + } + + inline int GetComCtlVersion() + { + // Load the Common Controls DLL + HMODULE hComCtl = ::LoadLibraryA("COMCTL32.DLL"); + if (!hComCtl) + return 0; + + int ComCtlVer = 400; + + if (::GetProcAddress(hComCtl, "InitCommonControlsEx")) + { + // InitCommonControlsEx is unique to 4.7 and later + ComCtlVer = 470; + + if (::GetProcAddress(hComCtl, "DllGetVersion")) + { + typedef HRESULT CALLBACK DLLGETVERSION(DLLVERSIONINFO*); + DLLGETVERSION* pfnDLLGetVersion = NULL; + + pfnDLLGetVersion = (DLLGETVERSION*)::GetProcAddress(hComCtl, "DllGetVersion"); + if(pfnDLLGetVersion) + { + DLLVERSIONINFO dvi; + dvi.cbSize = sizeof dvi; + if(NOERROR == pfnDLLGetVersion(&dvi)) + { + DWORD dwVerMajor = dvi.dwMajorVersion; + DWORD dwVerMinor = dvi.dwMinorVersion; + ComCtlVer = 100 * dwVerMajor + dwVerMinor; + } + } + } + else if (::GetProcAddress(hComCtl, "InitializeFlatSB")) + ComCtlVer = 471; // InitializeFlatSB is unique to version 4.71 + } + + ::FreeLibrary(hComCtl); + + // return values and DLL versions + // 400 dll ver 4.00 Windows 95/Windows NT 4.0 + // 470 dll ver 4.70 Internet Explorer 3.x + // 471 dll ver 4.71 Internet Explorer 4.0 + // 472 dll ver 4.72 Internet Explorer 4.01 and Windows 98 + // 580 dll ver 5.80 Internet Explorer 5 + // 581 dll ver 5.81 Windows 2000 and Windows ME + // 582 dll ver 5.82 Windows XP or Vista without XP themes + // 600 dll ver 6.00 Windows XP with XP themes + // 610 dll ver 6.10 Windows Vista with XP themes + // 616 dll ver 6.16 Windows Vista SP1 or Windows 7 with XP themes + + return ComCtlVer; + } + + inline UINT GetSizeofMenuItemInfo() + { + UINT uSize = sizeof(MENUITEMINFO); + // For Win95 and NT, cbSize needs to be 44 + if (1400 == (GetWinVersion()) || (2400 == GetWinVersion())) + uSize = 44; + + return uSize; + } + + inline UINT GetSizeofNonClientMetrics() + { + // This function correctly determines the sizeof NONCLIENTMETRICS + UINT uSize = sizeof (NONCLIENTMETRICS); + + #if (WINVER >= 0x0600) + if (GetWinVersion() < 2600 && (uSize > 500)) // Is OS version less than Vista + uSize -= sizeof(int); // Adjust size back to correct value + #endif + + return uSize; + } + + + + // A global function to report the state of the left mouse button + inline BOOL IsLeftButtonDown() + { + SHORT state; + if (GetSystemMetrics(SM_SWAPBUTTON)) + // Mouse buttons are swapped + state = GetAsyncKeyState(VK_RBUTTON); + else + // Mouse buttons are not swapped + state = GetAsyncKeyState(VK_LBUTTON); + + // returns true if the left mouse button is down + return (state & 0x8000); + } + + inline BOOL IsAeroThemed() + { + BOOL bIsAeroThemed = FALSE; + + // Test if Windows version is XP or greater + if (GetWinVersion() >= 2501) + { + HMODULE hMod = ::LoadLibrary(_T("uxtheme.dll")); + if(hMod) + { + // Declare pointers to IsCompositionActive function + FARPROC pIsCompositionActive = ::GetProcAddress(hMod, "IsCompositionActive"); + + if(pIsCompositionActive) + { + if(pIsCompositionActive()) + { + bIsAeroThemed = TRUE; + } + } + ::FreeLibrary(hMod); + } + } + + return bIsAeroThemed; + } + + inline BOOL IsXPThemed() + { + BOOL bIsXPThemed = FALSE; + + // Test if Windows version is XP or greater + if (GetWinVersion() >= 2501) + { + HMODULE hMod = ::LoadLibrary(_T("uxtheme.dll")); + if(hMod) + { + // Declare pointers to functions + FARPROC pIsAppThemed = ::GetProcAddress(hMod, "IsAppThemed"); + FARPROC pIsThemeActive = ::GetProcAddress(hMod, "IsThemeActive"); + + if(pIsAppThemed && pIsThemeActive) + { + if(pIsAppThemed() && pIsThemeActive()) + { + // Test if ComCtl32 dll used is version 6 or later + bIsXPThemed = (GetComCtlVersion() >= 600); + } + } + ::FreeLibrary(hMod); + } + } + + return bIsXPThemed; + } + + #endif // #ifndef _WIN32_WCE + + // Required for WinCE + #ifndef lstrcpyn + inline LPTSTR lstrcpyn(LPTSTR lpstrDest, LPCTSTR lpstrSrc, int nLength) + { + if(NULL == lpstrDest || NULL == lpstrSrc || nLength <= 0) + return NULL; + int nLen = MIN((int)lstrlen(lpstrSrc), nLength - 1); + LPTSTR lpstrRet = (LPTSTR)memcpy(lpstrDest, lpstrSrc, nLen * sizeof(TCHAR)); + lpstrDest[nLen] = _T('\0'); + return lpstrRet; + } + #endif // !lstrcpyn + +} + + +#endif // _WIN32XX_WINUTILS_H_ diff --git a/mmc_updater/src/AppInfo.cpp b/mmc_updater/src/AppInfo.cpp new file mode 100644 index 00000000..a5a9bb63 --- /dev/null +++ b/mmc_updater/src/AppInfo.cpp @@ -0,0 +1,23 @@ +#include "AppInfo.h" + +#include "FileUtils.h" +#include "Platform.h" +#include "StringUtils.h" +#include "StandardDirs.h" + +#include <iostream> + +std::string AppInfo::logFilePath() +{ + return StandardDirs::appDataPath(organizationName(),appName()) + '/' + "update-log.txt"; +} + +std::string AppInfo::updateErrorMessage(const std::string& details) +{ + std::string result = "There was a problem installing the update:\n\n"; + result += details; + result += "\n\nYou can try downloading and installing the latest version of " + "MultiMC from http://multimc.org/"; + return result; +} + diff --git a/mmc_updater/src/AppInfo.h b/mmc_updater/src/AppInfo.h new file mode 100644 index 00000000..51d95886 --- /dev/null +++ b/mmc_updater/src/AppInfo.h @@ -0,0 +1,39 @@ +#pragma once + +#include <string> + +/** This class provides project-specific updater properties, + * such as the name of the application being updated and + * the path to log details of the update install to. + */ +class AppInfo +{ + public: + // Basic application information + static std::string name(); + static std::string appName(); + static std::string organizationName(); + + static std::string logFilePath(); + + /** Returns a message to display to the user in the event + * of a problem installing the update. + */ + static std::string updateErrorMessage(const std::string& details); +}; + +inline std::string AppInfo::name() +{ + return "MultiMC Updater"; +} + +inline std::string AppInfo::appName() +{ + return "MultiMC"; +} + +inline std::string AppInfo::organizationName() +{ + return "MultiMC Contributors"; +} + diff --git a/mmc_updater/src/CMakeLists.txt b/mmc_updater/src/CMakeLists.txt new file mode 100644 index 00000000..d1a32755 --- /dev/null +++ b/mmc_updater/src/CMakeLists.txt @@ -0,0 +1,121 @@ + +add_subdirectory(tests) + +find_package(Threads REQUIRED) +include(GenerateCppResourceFile) + +set (UPDATER_SOURCES + AppInfo.cpp + AppInfo.h + DirIterator.cpp + DirIterator.h + FileUtils.cpp + FileUtils.h + Log.cpp + Log.h + ProcessUtils.cpp + ProcessUtils.h + StandardDirs.cpp + StandardDirs.h + UpdateDialog.cpp + UpdateInstaller.cpp + UpdateInstaller.h + UpdateScript.cpp + UpdateScript.h + UpdaterOptions.cpp + UpdaterOptions.h +) + +add_definitions(-DTIXML_USE_STL) + +if (WIN32) + set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogWin32.cpp UpdateDialogWin32.h) +endif() + +if (UNIX) + set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogAscii.cpp UpdateDialogAscii.h) + add_definitions(-Wall -Wconversion) +if (APPLE) + set(MAC_DOCK_ICON_CPP_FILE ${CMAKE_CURRENT_BINARY_DIR}/mac_dock_icon.cpp) + set(MAC_INFO_PLIST_FILE ${CMAKE_CURRENT_BINARY_DIR}/mac_info_plist.cpp) + generate_cpp_resource_file(resource_macdockicon ${CMAKE_CURRENT_SOURCE_DIR}/resources/mac.icns ${MAC_DOCK_ICON_CPP_FILE}) + generate_cpp_resource_file(resource_macplist ${CMAKE_CURRENT_SOURCE_DIR}/resources/Info.plist ${MAC_INFO_PLIST_FILE}) + set(UPDATER_SOURCES ${UPDATER_SOURCES} + MacBundle.h + MacBundle.cpp + StandardDirs.mm + StlSymbolsLeopard.cpp + UpdateDialogCocoa.mm + UpdateDialogCocoa.h + mac_dock_icon.cpp + mac_info_plist.cpp + ) +else() # linuxes and other similar systems + find_package(GTK2 REQUIRED gtk) + include_directories(${GTK2_INCLUDE_DIRS}) + add_library(updatergtk SHARED UpdateDialogGtk.cpp UpdateDialogGtk.h) + target_link_libraries(updatergtk ${GTK2_LIBRARIES}) + + # embed the GTK helper library into the updater binary. + # At runtime it will be extracted and loaded if the + # GTK libraries are available + get_property(GTK_UPDATER_LIB TARGET updatergtk PROPERTY LOCATION) + set(GTK_BIN_CPP_FILE ${CMAKE_CURRENT_BINARY_DIR}/libupdatergtk.cpp) + generate_cpp_resource_file(resource_updatergtk ${GTK_UPDATER_LIB} ${GTK_BIN_CPP_FILE}) + add_dependencies(resource_updatergtk updatergtk) + + set(UPDATER_SOURCES ${UPDATER_SOURCES} UpdateDialogGtkFactory.cpp UpdateDialogGtkFactory.h ${GTK_BIN_CPP_FILE}) +endif() +endif() + +add_library(updatershared STATIC ${UPDATER_SOURCES}) + +target_link_libraries(updatershared + anyoption + tinyxml +) + +if (UNIX) + if (APPLE) + find_library(COCOA_LIBRARY Cocoa) + find_library(SECURITY_LIBRARY Security) + target_link_libraries(updatershared ${SECURITY_LIBRARY} ${COCOA_LIBRARY}) + else() + add_dependencies(updatershared resource_updatergtk) + endif() + target_link_libraries(updatershared pthread dl) +endif() + +if (WIN32) + set(EXE_FLAGS WIN32 resources/updater.rc) +endif() + +add_executable(updater ${EXE_FLAGS} main.cpp) + +target_link_libraries(updater + updatershared +) + + +#### Updater Executable #### +IF(WIN32) +INSTALL(TARGETS updater + BUNDLE DESTINATION . COMPONENT Runtime + LIBRARY DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION . COMPONENT Runtime +) +ENDIF() +IF(UNIX) +IF(APPLE) +INSTALL(TARGETS updater + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION MultiMC.app/Contents/MacOS COMPONENT Runtime +) +ELSE() +INSTALL(TARGETS updater + BUNDLE DESTINATION . COMPONENT Runtime + RUNTIME DESTINATION bin COMPONENT Runtime +) +ENDIF() +ENDIF() + diff --git a/mmc_updater/src/DirIterator.cpp b/mmc_updater/src/DirIterator.cpp new file mode 100644 index 00000000..a4604f05 --- /dev/null +++ b/mmc_updater/src/DirIterator.cpp @@ -0,0 +1,85 @@ +#include "DirIterator.h" + +#include "Log.h" +#include "StringUtils.h" + +#ifdef PLATFORM_UNIX + #include <dirent.h> +#endif + +#include <string.h> + +DirIterator::DirIterator(const char* path) +{ + m_path = path; + +#ifdef PLATFORM_UNIX + m_dir = opendir(path); + m_entry = 0; +#else + // to list the contents of a directory, the first + // argument to FindFirstFile needs to be a wildcard + // of the form: C:\path\to\dir\* + std::string searchPath = m_path; + if (!endsWith(searchPath,"/")) + { + searchPath.append("/"); + } + searchPath.append("*"); + m_findHandle = FindFirstFile(searchPath.c_str(),&m_findData); + m_firstEntry = true; +#endif +} + +DirIterator::~DirIterator() +{ +#ifdef PLATFORM_UNIX + closedir(m_dir); +#else + FindClose(m_findHandle); +#endif +} + +bool DirIterator::next() +{ +#ifdef PLATFORM_UNIX + m_entry = readdir(m_dir); + return m_entry != 0; +#else + bool result; + if (m_firstEntry) + { + m_firstEntry = false; + return m_findHandle != INVALID_HANDLE_VALUE; + } + else + { + result = FindNextFile(m_findHandle,&m_findData); + } + return result; +#endif +} + +std::string DirIterator::fileName() const +{ +#ifdef PLATFORM_UNIX + return m_entry->d_name; +#else + return m_findData.cFileName; +#endif +} + +std::string DirIterator::filePath() const +{ + return m_path + '/' + fileName(); +} + +bool DirIterator::isDir() const +{ +#ifdef PLATFORM_UNIX + return m_entry->d_type == DT_DIR; +#else + return (m_findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; +#endif +} + diff --git a/mmc_updater/src/DirIterator.h b/mmc_updater/src/DirIterator.h new file mode 100644 index 00000000..f3fbb955 --- /dev/null +++ b/mmc_updater/src/DirIterator.h @@ -0,0 +1,43 @@ +#pragma once + +#include "Platform.h" + +#include <string> + +#ifdef PLATFORM_UNIX +#include <dirent.h> +#endif + +/** Simple class for iterating over the files in a directory + * and reporting their names and types. + */ +class DirIterator +{ + public: + DirIterator(const char* path); + ~DirIterator(); + + // iterate to the next entry in the directory + bool next(); + + // methods to return information about + // the current entry + std::string fileName() const; + std::string filePath() const; + bool isDir() const; + + private: + std::string m_path; + +#ifdef PLATFORM_UNIX + DIR* m_dir; + dirent* m_entry; +#endif + +#ifdef PLATFORM_WINDOWS + HANDLE m_findHandle; + WIN32_FIND_DATA m_findData; + bool m_firstEntry; +#endif +}; + diff --git a/mmc_updater/src/FileUtils.cpp b/mmc_updater/src/FileUtils.cpp new file mode 100644 index 00000000..10435e49 --- /dev/null +++ b/mmc_updater/src/FileUtils.cpp @@ -0,0 +1,557 @@ +#include "FileUtils.h" + +#include "DirIterator.h" +#include "Log.h" +#include "Platform.h" +#include "StringUtils.h" + +#include <algorithm> +#include <assert.h> +#include <string.h> +#include <fstream> +#include <iostream> + +#ifdef PLATFORM_UNIX +#include <unistd.h> +#include <stdio.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <errno.h> +#include <libgen.h> +#endif + +FileUtils::IOException::IOException(const std::string& error) +{ + init(errno,error); +} + +FileUtils::IOException::IOException(int errorCode, const std::string& error) +{ + init(errorCode,error); +} + +void FileUtils::IOException::init(int errorCode, const std::string& error) +{ + m_error = error; + +#ifdef PLATFORM_UNIX + m_errorCode = errorCode; + + if (m_errorCode > 0) + { + m_error += " details: " + std::string(strerror(m_errorCode)); + } +#endif + +#ifdef PLATFORM_WINDOWS + m_errorCode = 0; + m_error += " GetLastError returned: " + intToStr(GetLastError()); +#endif +} + +FileUtils::IOException::~IOException() throw () +{ +} + +FileUtils::IOException::Type FileUtils::IOException::type() const +{ +#ifdef PLATFORM_UNIX + switch (m_errorCode) + { + case 0: + return NoError; + case EROFS: + return ReadOnlyFileSystem; + case ENOSPC: + return DiskFull; + default: + return Unknown; + } +#else + return Unknown; +#endif +} + +bool FileUtils::fileExists(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + struct stat fileInfo; + if (lstat(path,&fileInfo) != 0) + { + if (errno == ENOENT) + { + return false; + } + else + { + throw IOException("Error checking for file " + std::string(path)); + } + } + return true; +#else + DWORD result = GetFileAttributes(path); + if (result == INVALID_FILE_ATTRIBUTES) + { + return false; + } + return true; +#endif +} + +int FileUtils::fileMode(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + struct stat fileInfo; + if (stat(path,&fileInfo) != 0) + { + throw IOException("Error reading file permissions for " + std::string(path)); + } + return fileInfo.st_mode; +#else + // not implemented for Windows + return 0; +#endif +} + +void FileUtils::chmod(const char* path, int mode) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::chmod(path,static_cast<mode_t>(mode)) != 0) + { + throw IOException("Failed to set permissions on " + std::string(path) + " to " + intToStr(mode)); + } +#else + // TODO - Not implemented under Windows - all files + // get default permissions +#endif +} + +void FileUtils::moveFile(const char* src, const char* dest) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (rename(src,dest) != 0) + { + throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest)); + } +#else + if (!MoveFile(src,dest)) + { + throw IOException("Unable to rename " + std::string(src) + " to " + std::string(dest)); + } +#endif +} + +void FileUtils::mkpath(const char* dir) throw (IOException) +{ + std::string currentPath; + std::istringstream stream(dir); + while (!stream.eof()) + { + std::string segment; + std::getline(stream,segment,'/'); + currentPath += segment; + if (!currentPath.empty() && !fileExists(currentPath.c_str())) + { + mkdir(currentPath.c_str()); + } + currentPath += '/'; + } +} + +void FileUtils::mkdir(const char* dir) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::mkdir(dir,S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) + { + throw IOException("Unable to create directory " + std::string(dir)); + } +#else + if (!CreateDirectory(dir,0 /* default security attributes */)) + { + throw IOException("Unable to create directory " + std::string(dir)); + } +#endif +} + +void FileUtils::rmdir(const char* dir) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::rmdir(dir) != 0) + { + throw IOException("Unable to remove directory " + std::string(dir)); + } +#else + if (!RemoveDirectory(dir)) + { + throw IOException("Unable to remove directory " + std::string(dir)); + } +#endif +} + +void FileUtils::createSymLink(const char* link, const char* target) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (symlink(target,link) != 0) + { + throw IOException("Unable to create symlink " + std::string(link) + " to " + std::string(target)); + } +#else + // symlinks are not supported under Windows (at least, not universally. + // Windows Vista and later do actually support symlinks) + LOG(Warn,"Skipping symlink creation - not implemented in Windows"); +#endif +} + +void FileUtils::removeFile(const char* src) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (unlink(src) != 0) + { + if (errno != ENOENT) + { + throw IOException("Unable to remove file " + std::string(src)); + } + } +#else + if (!DeleteFile(src)) + { + if (GetLastError() == ERROR_ACCESS_DENIED) + { + // if another process is using the file, try moving it to + // a temporary directory and then + // scheduling it for deletion on reboot + std::string tempDeletePathBase = tempPath(); + tempDeletePathBase += '/'; + tempDeletePathBase += fileName(src); + + int suffix = 0; + std::string tempDeletePath = tempDeletePathBase; + while (fileExists(tempDeletePath.c_str())) + { + ++suffix; + tempDeletePath = tempDeletePathBase + '_' + intToStr(suffix); + } + + LOG(Warn,"Unable to remove file " + std::string(src) + " - it may be in use. Moving to " + + tempDeletePath + " and scheduling delete on reboot."); + moveFile(src,tempDeletePath.c_str()); + MoveFileEx(tempDeletePath.c_str(),0,MOVEFILE_DELAY_UNTIL_REBOOT); + } + else if (GetLastError() != ERROR_FILE_NOT_FOUND) + { + throw IOException("Unable to remove file " + std::string(src)); + } + } +#endif +} + +std::string FileUtils::fileName(const char* path) +{ +#ifdef PLATFORM_UNIX + char* pathCopy = strdup(path); + std::string basename = ::basename(pathCopy); + free(pathCopy); + return basename; +#else + char baseName[MAX_PATH]; + char extension[MAX_PATH]; + _splitpath_s(path, + 0, /* drive */ + 0, /* drive length */ + 0, /* dir */ + 0, /* dir length */ + baseName, + MAX_PATH, /* baseName length */ + extension, + MAX_PATH /* extension length */ + ); + return std::string(baseName) + std::string(extension); +#endif +} + +std::string FileUtils::dirname(const char* path) +{ +#ifdef PLATFORM_UNIX + char* pathCopy = strdup(path); + std::string dirname = ::dirname(pathCopy); + free(pathCopy); + return dirname; +#else + char drive[3]; + char dir[MAX_PATH]; + + _splitpath_s(path, + drive, /* drive */ + 3, /* drive length */ + dir, + MAX_PATH, /* dir length */ + 0, /* filename */ + 0, /* filename length */ + 0, /* extension */ + 0 /* extension length */ + ); + + std::string result; + if (drive[0]) + { + result += std::string(drive); + } + result += dir; + + return result; +#endif +} + +void FileUtils::touch(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + // see http://pubs.opengroup.org/onlinepubs/9699919799/utilities/touch.html + // + // we use utimes/futimes instead of utimensat/futimens for compatibility + // with older Linux and Mac + + if (fileExists(path)) + { + utimes(path,0 /* use current date/time */); + } + else + { + int fd = creat(path,S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd != -1) + { + futimes(fd,0 /* use current date/time */); + close(fd); + } + else + { + throw IOException("Unable to touch file " + std::string(path)); + } + } +#else + HANDLE result = CreateFile(path,GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + 0, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + 0); + if (result == INVALID_HANDLE_VALUE) + { + throw IOException("Unable to touch file " + std::string(path)); + } + else + { + CloseHandle(result); + } +#endif +} + +void FileUtils::rmdirRecursive(const char* path) throw (IOException) +{ + // remove dir contents + DirIterator dir(path); + while (dir.next()) + { + std::string name = dir.fileName(); + if (name != "." && name != "..") + { + if (dir.isDir()) + { + rmdir(dir.filePath().c_str()); + } + else + { + removeFile(dir.filePath().c_str()); + } + } + } + + // remove the directory itself + rmdir(path); +} + +std::string FileUtils::canonicalPath(const char* path) +{ +#ifdef PLATFORM_UNIX + // on Linux and Mac OS 10.6, realpath() can allocate the required + // amount of memory automatically, however Mac OS 10.5 does not support + // this, so we used a fixed-sized buffer on all platforms + char canonicalPathBuffer[PATH_MAX+1]; + if (realpath(path,canonicalPathBuffer) != 0) + { + return std::string(canonicalPathBuffer); + } + else + { + throw IOException("Error reading canonical path for " + std::string(path)); + } +#else + throw IOException("canonicalPath() not implemented"); +#endif +} + +std::string FileUtils::toWindowsPathSeparators(const std::string& str) +{ + std::string result = str; + std::replace(result.begin(),result.end(),'/','\\'); + return result; +} + +std::string FileUtils::toUnixPathSeparators(const std::string& str) +{ + std::string result = str; + std::replace(result.begin(),result.end(),'\\','/'); + return result; +} + +std::string FileUtils::tempPath() +{ +#ifdef PLATFORM_UNIX + std::string tmpDir(notNullString(getenv("TMPDIR"))); + if (tmpDir.empty()) + { + tmpDir = "/tmp"; + } + return tmpDir; +#else + char buffer[MAX_PATH+1]; + GetTempPath(MAX_PATH+1,buffer); + return toUnixPathSeparators(buffer); +#endif +} + +bool startsWithDriveLetter(const char* path) +{ + return strlen(path) >= 2 && + (isalpha(path[0])) && + path[1] == ':'; +} + +bool FileUtils::isRelative(const char* path) +{ +#ifdef PLATFORM_UNIX + return strlen(path) == 0 || path[0] != '/'; +#else + // on Windows, a path is relative if it does not start with: + // - '\\' (a UNC name) + // - '[Drive Letter]:\' + // - A single backslash + // + // the input path is assumed to have already been converted to use + // Unix-style path separators + + std::string pathStr(path); + + if ((!pathStr.empty() && pathStr.at(0) == '/') || + (startsWith(pathStr,"//")) || + (startsWithDriveLetter(pathStr.c_str()))) + { + return false; + } + else + { + return true; + } +#endif +} + +void FileUtils::writeFile(const char* path, const char* data, int length) throw (IOException) +{ + std::ofstream stream(path,std::ios::binary | std::ios::trunc); + stream.write(data,length); +} + +std::string FileUtils::readFile(const char* path) throw (IOException) +{ + std::ifstream inputFile(path, std::ios::in | std::ios::binary); + std::string content; + inputFile.seekg(0, std::ios::end); + content.resize(static_cast<unsigned int>(inputFile.tellg())); + inputFile.seekg(0, std::ios::beg); + inputFile.read(&content[0], static_cast<int>(content.size())); + return content; +} + +void FileUtils::copyFile(const char* src, const char* dest) throw (IOException) +{ +#ifdef PLATFORM_UNIX + std::ifstream inputFile(src,std::ios::binary); + std::ofstream outputFile(dest,std::ios::binary | std::ios::trunc); + + if (!inputFile.good()) + { + throw IOException("Failed to read file " + std::string(src)); + } + if (!outputFile.good()) + { + throw IOException("Failed to write file " + std::string(dest)); + } + + outputFile << inputFile.rdbuf(); + + if (inputFile.bad()) + { + throw IOException("Error reading file " + std::string(src)); + } + if (outputFile.bad()) + { + throw IOException("Error writing file " + std::string(dest)); + } + + chmod(dest,fileMode(src)); +#else + if (!CopyFile(src,dest,FALSE)) + { + throw IOException("Failed to copy " + std::string(src) + " to " + std::string(dest)); + } +#endif +} + +std::string FileUtils::makeAbsolute(const char* path, const char* basePath) +{ + if (isRelative(path)) + { + assert(!isRelative(basePath)); + return std::string(basePath) + '/' + std::string(path); + } + else + { + return path; + } +} + +void FileUtils::chdir(const char* path) throw (IOException) +{ +#ifdef PLATFORM_UNIX + if (::chdir(path) != 0) + { + throw FileUtils::IOException("Unable to change directory"); + } +#else + if (!SetCurrentDirectory(path)) + { + throw FileUtils::IOException("Unable to change directory"); + } +#endif +} + +std::string FileUtils::getcwd() throw (IOException) +{ +#ifdef PLATFORM_UNIX + char path[PATH_MAX]; + if (!::getcwd(path,PATH_MAX)) + { + throw FileUtils::IOException("Failed to get current directory"); + } + return std::string(path); +#else + char path[MAX_PATH]; + if (GetCurrentDirectory(MAX_PATH,path) == 0) + { + throw FileUtils::IOException("Failed to get current directory"); + } + return toUnixPathSeparators(std::string(path)); +#endif +} + diff --git a/mmc_updater/src/FileUtils.h b/mmc_updater/src/FileUtils.h new file mode 100644 index 00000000..cb5830ae --- /dev/null +++ b/mmc_updater/src/FileUtils.h @@ -0,0 +1,141 @@ +#pragma once + +#include <exception> +#include <string> + +#include "Platform.h" +#include "StringUtils.h" + + +/** A set of functions for performing common operations + * on files, throwing exceptions if an operation fails. + * + * Path arguments to FileUtils functions should use Unix-style path + * separators. + */ +class FileUtils +{ + public: + /** Base class for exceptions reported by + * FileUtils methods if an operation fails. + */ + class IOException : public std::exception + { + public: + IOException(const std::string& error); + IOException(int errorCode, const std::string& error); + + virtual ~IOException() throw (); + + enum Type + { + NoError, + /** Unknown error type. Call what() to get the description + * provided by the OS. + */ + Unknown, + ReadOnlyFileSystem, + DiskFull + }; + + virtual const char* what() const throw () + { + return m_error.c_str(); + } + + Type type() const; + + private: + void init(int errorCode, const std::string& error); + + std::string m_error; + int m_errorCode; + }; + + /** Remove a file. Throws an exception if the file + * could not be removed. + * + * On Unix, a file can be removed even if it is in use if the user + * has the necessary permissions. removeFile() tries to simulate + * this behavior on Windows. If a file cannot be removed on Windows + * because it is in use it will be moved to a temporary directory and + * scheduled for deletion on the next restart. + */ + static void removeFile(const char* src) throw (IOException); + + /** Set the permissions of a file. @p permissions uses the standard + * Unix mode_t values. + */ + static void chmod(const char* path, int permissions) throw (IOException); + + /** Returns true if the file at @p path exists. If @p path is a symlink, + * returns true if the symlink itself exists, not the target. + */ + static bool fileExists(const char* path) throw (IOException); + + /** Returns the Unix mode flags of @p path. If @p path is a symlink, + * returns the mode flags of the target. + */ + static int fileMode(const char* path) throw (IOException); + + static void moveFile(const char* src, const char* dest) throw (IOException); + static void mkdir(const char* dir) throw (IOException); + static void rmdir(const char* dir) throw (IOException); + static void createSymLink(const char* link, const char* target) throw (IOException); + static void touch(const char* path) throw (IOException); + static void copyFile(const char* src, const char* dest) throw (IOException); + + /** Create all the directories in @p path which do not yet exist. + * @p path may be relative or absolute. + */ + static void mkpath(const char* path) throw (IOException); + + /** Returns the file name part of a file path, including the extension. */ + static std::string fileName(const char* path); + + /** Returns the directory part of a file path. + * On Windows this includes the drive letter, if present in @p path. + */ + static std::string dirname(const char* path); + + /** Remove a directory and all of its contents. */ + static void rmdirRecursive(const char* dir) throw (IOException); + + /** Return the full, absolute path to a file, resolving any + * symlinks and removing redundant sections. + */ + static std::string canonicalPath(const char* path); + + /** Returns the path to a directory for storing temporary files. */ + static std::string tempPath(); + + /** Returns a copy of the path 'str' with Windows-style '\' + * dir separators converted to Unix-style '/' separators + */ + static std::string toUnixPathSeparators(const std::string& str); + + static std::string toWindowsPathSeparators(const std::string& str); + + /** Returns true if the provided path is relative. + * Or false if absolute. + */ + static bool isRelative(const char* path); + + /** Converts @p path to an absolute path. If @p path is already absolute, + * just returns @p path, otherwise prefixes it with @p basePath to make it absolute. + * + * @p basePath should be absolute. + */ + static std::string makeAbsolute(const char* path, const char* basePath); + + static void writeFile(const char* path, const char* data, int length) throw (IOException); + + static std::string readFile(const char* path) throw (IOException); + + /** Changes the current working directory to @p path */ + static void chdir(const char* path) throw (IOException); + + /** Returns the current working directory of the application. */ + static std::string getcwd() throw (IOException); +}; + diff --git a/mmc_updater/src/Log.cpp b/mmc_updater/src/Log.cpp new file mode 100644 index 00000000..d4e5a214 --- /dev/null +++ b/mmc_updater/src/Log.cpp @@ -0,0 +1,65 @@ +#include "Log.h" + +#include "Platform.h" +#include "StringUtils.h" +#include "ProcessUtils.h" + +#include <string.h> +#include <iostream> + +Log m_globalLog; + +Log* Log::instance() +{ + return &m_globalLog; +} + +Log::Log() +{ +} + +Log::~Log() +{ +} + +void Log::open(const std::string& path) +{ + m_mutex.lock(); + m_output.open(path.c_str(),std::ios_base::out | std::ios_base::app); + m_mutex.unlock(); +} + +void Log::writeToStream(std::ostream& stream, Type type, const char* text) +{ + // Multiple processes may be writing to the same log file during + // an update. No attempt is made to synchronize access to the file. + // + // Under Unix, appends to a single file on a local FS by multiple writers should be atomic + // provided that the length of 'text' is less than PIPE_BUF + // + switch (type) + { + case Info: + stream << "INFO "; + break; + case Warn: + stream << "WARN "; + break; + case Error: + stream << "ERROR "; + break; + } + stream << '(' << intToStr(ProcessUtils::currentProcessId()) << ") " << text << std::endl; +} + +void Log::write(Type type, const char* text) +{ + m_mutex.lock(); + writeToStream(std::cerr,type,text); + if (m_output.is_open()) + { + writeToStream(m_output,type,text); + } + m_mutex.unlock(); +} + diff --git a/mmc_updater/src/Log.h b/mmc_updater/src/Log.h new file mode 100644 index 00000000..cf6be832 --- /dev/null +++ b/mmc_updater/src/Log.h @@ -0,0 +1,46 @@ +#pragma once + +#include <string> +#include <fstream> + +#include <thread> +#include <mutex> + +class Log +{ + public: + enum Type + { + Info, + Warn, + Error + }; + + Log(); + ~Log(); + + void open(const std::string& path); + + /** Write @p text to the log. This method is thread-safe. */ + void write(Type type, const std::string& text); + /** Write @p text to the log. This method is thread-safe. */ + void write(Type type, const char* text); + + static Log* instance(); + + private: + static void writeToStream(std::ostream& stream, Type type, const char* text); + + std::mutex m_mutex; + std::ofstream m_output; +}; + +inline void Log::write(Type type, const std::string& text) +{ + write(type,text.c_str()); +} + +#define LOG(type,text) \ + Log::instance()->write(Log::type,text) + + diff --git a/mmc_updater/src/MacBundle.cpp b/mmc_updater/src/MacBundle.cpp new file mode 100644 index 00000000..205869eb --- /dev/null +++ b/mmc_updater/src/MacBundle.cpp @@ -0,0 +1,53 @@ +#include "MacBundle.h" + +#include "FileUtils.h" +#include "Log.h" + +MacBundle::MacBundle(const std::string& path, const std::string& appName) +: m_appName(appName) +{ + m_path = path + '/' + appName + ".app"; +} + +std::string MacBundle::bundlePath() const +{ + return m_path; +} + +void MacBundle::create(const std::string& infoPlist, + const std::string& icon, + const std::string& exePath) +{ + try + { + // create the bundle directories + FileUtils::mkpath(m_path.c_str()); + + std::string contentDir = m_path + "/Contents"; + std::string resourceDir = contentDir + "/Resources"; + std::string binDir = contentDir + "/MacOS"; + + FileUtils::mkpath(resourceDir.c_str()); + FileUtils::mkpath(binDir.c_str()); + + // create the Contents/Info.plist file + FileUtils::writeFile((contentDir + "/Info.plist").c_str(),infoPlist.c_str(),static_cast<int>(infoPlist.size())); + + // save the icon to Contents/Resources/<appname>.icns + FileUtils::writeFile((resourceDir + '/' + m_appName + ".icns").c_str(),icon.c_str(),static_cast<int>(icon.size())); + + // copy the app binary to Contents/MacOS/<appname> + m_exePath = binDir + '/' + m_appName; + FileUtils::copyFile(exePath.c_str(),m_exePath.c_str()); + } + catch (const FileUtils::IOException& exception) + { + LOG(Error,"Unable to create app bundle. " + std::string(exception.what())); + } +} + +std::string MacBundle::executablePath() const +{ + return m_exePath; +} + diff --git a/mmc_updater/src/MacBundle.h b/mmc_updater/src/MacBundle.h new file mode 100644 index 00000000..2b119d8f --- /dev/null +++ b/mmc_updater/src/MacBundle.h @@ -0,0 +1,35 @@ +#pragma once + +#include <string> + +/** Class for creating minimal Mac app bundles. */ +class MacBundle +{ + public: + /** Create a MacBundle instance representing the bundle + * in <path>/<appName>.app + */ + MacBundle(const std::string& path, const std::string& appName); + + /** Create a simple Mac bundle. + * + * @param infoPlist The content of the Info.plist file + * @param icon The content of the app icon + * @param exePath The path of the file to use for the main app in the bundle. + */ + void create(const std::string& infoPlist, + const std::string& icon, + const std::string& exePath); + + /** Returns the path of the main executable within the Mac bundle. */ + std::string executablePath() const; + + /** Returns the path of the bundle */ + std::string bundlePath() const; + + private: + std::string m_path; + std::string m_appName; + std::string m_exePath; +}; + diff --git a/mmc_updater/src/Platform.h b/mmc_updater/src/Platform.h new file mode 100644 index 00000000..6d9afdfb --- /dev/null +++ b/mmc_updater/src/Platform.h @@ -0,0 +1,32 @@ +#pragma once + +// basic platform defines +#ifdef __linux__ + #define PLATFORM_LINUX +#endif + +#ifdef WIN32 + #define PLATFORM_WINDOWS + #define WIN32_LEAN_AND_MEAN + #include <windows.h> + #include <shellapi.h> + + // disable warnings about exception specifications, + // which are not implemented in Visual C++ + #pragma warning(disable:4290) +#endif + +#ifdef __APPLE__ + #define PLATFORM_MAC +#endif + +#if defined(PLATFORM_LINUX) || defined(PLATFORM_MAC) + #define PLATFORM_UNIX +#endif + +// platform-specific type aliases +#if defined(PLATFORM_UNIX) + #define PLATFORM_PID pid_t +#else + #define PLATFORM_PID DWORD +#endif diff --git a/mmc_updater/src/ProcessUtils.cpp b/mmc_updater/src/ProcessUtils.cpp new file mode 100644 index 00000000..3b9ffac2 --- /dev/null +++ b/mmc_updater/src/ProcessUtils.cpp @@ -0,0 +1,536 @@ +#include "ProcessUtils.h" + +#include "FileUtils.h" +#include "Platform.h" +#include "StringUtils.h" +#include "Log.h" + +#include <string.h> +#include <vector> +#include <iostream> + +#ifdef PLATFORM_WINDOWS +#include <windows.h> +#else +#include <unistd.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <errno.h> +#endif + +#ifdef PLATFORM_MAC +#include <Security/Security.h> +#include <mach-o/dyld.h> +#endif + +PLATFORM_PID ProcessUtils::currentProcessId() +{ +#ifdef PLATFORM_UNIX + return getpid(); +#else + return GetCurrentProcessId(); +#endif +} + +int ProcessUtils::runSync(const std::string& executable, + const std::list<std::string>& args) +{ +#ifdef PLATFORM_UNIX + return runSyncUnix(executable,args); +#else + return runWindows(executable,args,RunSync); +#endif +} + +#ifdef PLATFORM_UNIX +int ProcessUtils::runSyncUnix(const std::string& executable, + const std::list<std::string>& args) +{ + PLATFORM_PID pid = runAsyncUnix(executable,args); + int status = 0; + if (waitpid(pid,&status,0) != -1) + { + if (WIFEXITED(status)) + { + return static_cast<char>(WEXITSTATUS(status)); + } + else + { + LOG(Warn,"Child exited abnormally"); + return -1; + } + } + else + { + LOG(Warn,"Failed to get exit status of child " + intToStr(pid)); + return WaitFailed; + } +} +#endif + +void ProcessUtils::runAsync(const std::string& executable, + const std::list<std::string>& args) +{ +#ifdef PLATFORM_WINDOWS + runWindows(executable,args,RunAsync); +#elif defined(PLATFORM_UNIX) + runAsyncUnix(executable,args); +#endif +} + +int ProcessUtils::runElevated(const std::string& executable, + const std::list<std::string>& args, + const std::string& task) +{ +#ifdef PLATFORM_WINDOWS + (void)task; + return runElevatedWindows(executable,args); +#elif defined(PLATFORM_MAC) + (void)task; + return runElevatedMac(executable,args); +#elif defined(PLATFORM_LINUX) + return runElevatedLinux(executable,args,task); +#endif +} + +bool ProcessUtils::waitForProcess(PLATFORM_PID pid) +{ +#ifdef PLATFORM_UNIX + pid_t result = ::waitpid(pid, 0, 0); + if (result < 0) + { + LOG(Error,"waitpid() failed with error: " + std::string(strerror(errno))); + } + return result > 0; +#elif defined(PLATFORM_WINDOWS) + HANDLE hProc; + + if (!(hProc = OpenProcess(SYNCHRONIZE, FALSE, pid))) + { + LOG(Error,"Unable to get process handle for pid " + intToStr(pid) + " last error " + intToStr(GetLastError())); + return false; + } + + DWORD dwRet = WaitForSingleObject(hProc, INFINITE); + CloseHandle(hProc); + + if (dwRet == WAIT_FAILED) + { + LOG(Error,"WaitForSingleObject failed with error " + intToStr(GetLastError())); + } + + return (dwRet == WAIT_OBJECT_0); +#endif +} + +#ifdef PLATFORM_LINUX +int ProcessUtils::runElevatedLinux(const std::string& executable, + const std::list<std::string>& args, + const std::string& _task) +{ + std::string task(_task); + if (task.empty()) + { + task = FileUtils::fileName(executable.c_str()); + } + + // try available graphical sudo instances until we find one that works. + // The different sudo front-ends have different behaviors with respect to error codes: + // + // - 'kdesudo': return 1 if the user enters the wrong password 3 times or if + // they cancel elevation + // + // - recent 'gksudo' versions: return 1 if the user enters the wrong password + // : return -1 if the user cancels elevation + // + // - older 'gksudo' versions : return 0 if the user cancels elevation + + std::vector<std::string> sudos; + + if (getenv("KDE_SESSION_VERSION")) + { + sudos.push_back("kdesudo"); + } + sudos.push_back("gksudo"); + + for (unsigned int i=0; i < sudos.size(); i++) + { + const std::string& sudoBinary = sudos.at(i); + + std::list<std::string> sudoArgs; + sudoArgs.push_back("-u"); + sudoArgs.push_back("root"); + + if (sudoBinary == "kdesudo") + { + sudoArgs.push_back("-d"); + sudoArgs.push_back("--comment"); + std::string sudoMessage = task + " needs administrative privileges. Please enter your password."; + sudoArgs.push_back(sudoMessage); + } + else if (sudoBinary == "gksudo") + { + sudoArgs.push_back("--description"); + sudoArgs.push_back(task); + } + else + { + sudoArgs.push_back(task); + } + + sudoArgs.push_back("--"); + sudoArgs.push_back(executable); + std::copy(args.begin(),args.end(),std::back_inserter(sudoArgs)); + + int result = ProcessUtils::runSync(sudoBinary,sudoArgs); + + LOG(Info,"Tried to use sudo " + sudoBinary + " with response " + intToStr(result)); + + if (result != RunFailed) + { + return result; + break; + } + } + return RunElevatedFailed; +} +#endif + +#ifdef PLATFORM_MAC +int ProcessUtils::runElevatedMac(const std::string& executable, + const std::list<std::string>& args) +{ + // request elevation using the Security Service. + // + // This only works when the application is being run directly + // from the Mac. Attempting to run the app via a remote SSH session + // (for example) will fail with an interaction-not-allowed error + + OSStatus status; + AuthorizationRef authorizationRef; + + status = AuthorizationCreate( + NULL, + kAuthorizationEmptyEnvironment, + kAuthorizationFlagDefaults, + &authorizationRef); + + AuthorizationItem right = { kAuthorizationRightExecute, 0, NULL, 0 }; + AuthorizationRights rights = { 1, &right }; + + AuthorizationFlags flags = kAuthorizationFlagDefaults | + kAuthorizationFlagInteractionAllowed | + kAuthorizationFlagPreAuthorize | + kAuthorizationFlagExtendRights; + + if (status == errAuthorizationSuccess) + { + status = AuthorizationCopyRights(authorizationRef, &rights, NULL, + flags, NULL); + + if (status == errAuthorizationSuccess) + { + char** argv; + argv = (char**) malloc(sizeof(char*) * args.size() + 1); + + unsigned int i = 0; + for (std::list<std::string>::const_iterator iter = args.begin(); iter != args.end(); iter++) + { + argv[i] = strdup(iter->c_str()); + ++i; + } + argv[i] = NULL; + + FILE* pipe = NULL; + + char* tool = strdup(executable.c_str()); + + status = AuthorizationExecuteWithPrivileges(authorizationRef, tool, + kAuthorizationFlagDefaults, argv, &pipe); + + if (status == errAuthorizationSuccess) + { + // AuthorizationExecuteWithPrivileges does not provide a way to get the process ID + // of the child process. + // + // Discussions on Apple development forums suggest two approaches for working around this, + // + // - Modify the child process to sent its process ID back to the parent via + // the pipe passed to AuthorizationExecuteWithPrivileges. + // + // - Use the generic Unix wait() call. + // + // This code uses wait(), which is simpler, but suffers from the problem that wait() waits + // for any child process, not necessarily the specific process launched + // by AuthorizationExecuteWithPrivileges. + // + // Apple's documentation (see 'Authorization Services Programming Guide') suggests + // installing files in an installer as a legitimate use for + // AuthorizationExecuteWithPrivileges but in general strongly recommends + // not using this call and discusses a number of other alternatives + // for performing privileged operations, + // which we could consider in future. + + int childStatus; + pid_t childPid = wait(&childStatus); + + if (childStatus != 0) + { + LOG(Error,"elevated process failed with status " + intToStr(childStatus) + " pid " + + intToStr(childPid)); + } + else + { + LOG(Info,"elevated process succeded with pid " + intToStr(childPid)); + } + + return childStatus; + } + else + { + LOG(Error,"failed to launch elevated process " + intToStr(status)); + return RunElevatedFailed; + } + + // If we want to know more information about what has happened: + // http://developer.apple.com/mac/library/documentation/Security/Reference/authorization_ref/Reference/reference.html#//apple_ref/doc/uid/TP30000826-CH4g-CJBEABHG + free(tool); + for (i = 0; i < args.size(); i++) + { + free(argv[i]); + } + } + else + { + LOG(Error,"failed to get rights to launch elevated process. status: " + intToStr(status)); + return RunElevatedFailed; + } + } + else + { + return RunElevatedFailed; + } +} +#endif + +// convert a list of arguments in a space-separated string. +// Arguments containing spaces are enclosed in quotes +std::string quoteArgs(const std::list<std::string>& arguments) +{ + std::string quotedArgs; + for (std::list<std::string>::const_iterator iter = arguments.begin(); + iter != arguments.end(); + iter++) + { + std::string arg = *iter; + + bool isQuoted = !arg.empty() && + arg.at(0) == '"' && + arg.at(arg.size()-1) == '"'; + + if (!isQuoted && arg.find(' ') != std::string::npos) + { + arg.insert(0,"\""); + arg.append("\""); + } + quotedArgs += arg; + quotedArgs += " "; + } + return quotedArgs; +} + +#ifdef PLATFORM_WINDOWS +int ProcessUtils::runElevatedWindows(const std::string& executable, + const std::list<std::string>& arguments) +{ + std::string args = quoteArgs(arguments); + + SHELLEXECUTEINFO executeInfo; + ZeroMemory(&executeInfo,sizeof(executeInfo)); + executeInfo.cbSize = sizeof(SHELLEXECUTEINFO); + executeInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + // request UAC elevation + executeInfo.lpVerb = "runas"; + executeInfo.lpFile = executable.c_str(); + executeInfo.lpParameters = args.c_str(); + executeInfo.nShow = SW_SHOWNORMAL; + + LOG(Info,"Attempting to execute " + executable + " with administrator priviledges"); + if (!ShellExecuteEx(&executeInfo)) + { + LOG(Error,"Failed to start with admin priviledges using ShellExecuteEx()"); + return RunElevatedFailed; + } + + WaitForSingleObject(executeInfo.hProcess, INFINITE); + + // this assumes the process succeeded - we need to check whether + // this is actually the case. + return 0; +} +#endif + +#ifdef PLATFORM_UNIX +PLATFORM_PID ProcessUtils::runAsyncUnix(const std::string& executable, + const std::list<std::string>& args) +{ + pid_t child = fork(); + if (child == 0) + { + // in child process + char** argBuffer = new char*[args.size() + 2]; + argBuffer[0] = strdup(executable.c_str()); + int i = 1; + for (std::list<std::string>::const_iterator iter = args.begin(); iter != args.end(); iter++) + { + argBuffer[i] = strdup(iter->c_str()); + ++i; + } + argBuffer[i] = 0; + + if (execvp(executable.c_str(),argBuffer) == -1) + { + LOG(Error,"error starting child: " + std::string(strerror(errno))); + exit(RunFailed); + } + } + else + { + LOG(Info,"Started child process " + intToStr(child)); + } + return child; +} +#endif + +#ifdef PLATFORM_WINDOWS +int ProcessUtils::runWindows(const std::string& _executable, + const std::list<std::string>& _args, + RunMode runMode) +{ + // most Windows API functions allow back and forward slashes to be + // used interchangeably. However, an application started with + // CreateProcess() may fail to find Side-by-Side library dependencies + // in the same directory as the executable if forward slashes are + // used as path separators, so convert the path to use back slashes here. + // + // This may be related to LoadLibrary() requiring backslashes instead + // of forward slashes. + std::string executable = FileUtils::toWindowsPathSeparators(_executable); + + std::list<std::string> args(_args); + args.push_front(executable); + std::string commandLine = quoteArgs(args); + + STARTUPINFO startupInfo; + ZeroMemory(&startupInfo,sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + + PROCESS_INFORMATION processInfo; + ZeroMemory(&processInfo,sizeof(processInfo)); + + char* commandLineStr = strdup(commandLine.c_str()); + bool result = CreateProcess( + executable.c_str(), + commandLineStr, + 0 /* process attributes */, + 0 /* thread attributes */, + false /* inherit handles */, + NORMAL_PRIORITY_CLASS /* creation flags */, + 0 /* environment */, + 0 /* current directory */, + &startupInfo /* startup info */, + &processInfo /* process information */ + ); + + if (!result) + { + LOG(Error,"Failed to start child process. " + executable + " Last error: " + intToStr(GetLastError())); + return RunFailed; + } + else + { + if (runMode == RunSync) + { + if (WaitForSingleObject(processInfo.hProcess,INFINITE) == WAIT_OBJECT_0) + { + DWORD status = WaitFailed; + if (GetExitCodeProcess(processInfo.hProcess,&status) != 0) + { + LOG(Error,"Failed to get exit code for process"); + } + return status; + } + else + { + LOG(Error,"Failed to wait for process to finish"); + return WaitFailed; + } + } + else + { + // process is being run asynchronously - return zero as if it had + // succeeded + return 0; + } + } +} +#endif + +std::string ProcessUtils::currentProcessPath() +{ +#ifdef PLATFORM_LINUX + std::string path = FileUtils::canonicalPath("/proc/self/exe"); + LOG(Info,"Current process path " + path); + return path; +#elif defined(PLATFORM_MAC) + uint32_t bufferSize = PATH_MAX; + char buffer[bufferSize]; + _NSGetExecutablePath(buffer,&bufferSize); + return buffer; +#else + char fileName[MAX_PATH]; + GetModuleFileName(0 /* get path of current process */,fileName,MAX_PATH); + return fileName; +#endif +} + +#ifdef PLATFORM_WINDOWS +void ProcessUtils::convertWindowsCommandLine(LPCWSTR commandLine, int& argc, char**& argv) +{ + argc = 0; + LPWSTR* argvUnicode = CommandLineToArgvW(commandLine,&argc); + + argv = new char*[argc]; + for (int i=0; i < argc; i++) + { + const int BUFFER_SIZE = 4096; + char buffer[BUFFER_SIZE]; + + int length = WideCharToMultiByte(CP_ACP, + 0 /* flags */, + argvUnicode[i], + -1, /* argvUnicode is null terminated */ + buffer, + BUFFER_SIZE, + 0, + false); + + // note: if WideCharToMultiByte() fails it will return zero, + // in which case we store a zero-length argument in argv + if (length == 0) + { + argv[i] = new char[1]; + argv[i][0] = '\0'; + } + else + { + // if the input string to WideCharToMultiByte is null-terminated, + // the output is also null-terminated + argv[i] = new char[length]; + strncpy(argv[i],buffer,length); + } + } + LocalFree(argvUnicode); +} +#endif + diff --git a/mmc_updater/src/ProcessUtils.h b/mmc_updater/src/ProcessUtils.h new file mode 100644 index 00000000..e0cc3dc0 --- /dev/null +++ b/mmc_updater/src/ProcessUtils.h @@ -0,0 +1,101 @@ +#pragma once + +#include "Platform.h" + +#include <list> +#include <string> + +#ifndef PLATFORM_WINDOWS +#include <unistd.h> +#endif + +/** A set of functions to get information about the current + * process and launch new processes. + */ +class ProcessUtils +{ + public: + enum Errors + { + /** Status code returned by runElevated() if launching + * the elevated process fails. + */ + RunElevatedFailed = 255, + /** Status code returned by runSync() if the application + * cannot be started. + */ + RunFailed = -8, + /** Status code returned by runSync() if waiting for + * the application to exit and reading its status code fails. + */ + WaitFailed = -1 + }; + + static PLATFORM_PID currentProcessId(); + + /** Returns the absolute path to the main binary for + * the current process. + */ + static std::string currentProcessPath(); + + /** Start a process and wait for it to finish before + * returning its exit code. + * + * Returns -1 if the process cannot be started. + */ + static int runSync(const std::string& executable, + const std::list<std::string>& args); + + /** Start a process and return without waiting for + * it to finish. + */ + static void runAsync(const std::string& executable, + const std::list<std::string>& args); + + /** Run a process with administrative privileges and return the + * status code of the process, or 0 on Windows. + * + * Returns RunElevatedFailed if the elevated process could + * not be started. + */ + static int runElevated(const std::string& executable, + const std::list<std::string>& args, + const std::string& task); + + /** Wait for a process to exit. + * Returns true if the process was found and has exited or false + * otherwise. + */ + static bool waitForProcess(PLATFORM_PID pid); + +#ifdef PLATFORM_WINDOWS + /** Convert a unicode command line returned by GetCommandLineW() + * to a standard (argc,argv) pair. The resulting argv array and each + * element of argv must be freed using free() + */ + static void convertWindowsCommandLine(LPCWSTR commandLine, int& argc, char**& argv); +#endif + + private: + enum RunMode + { + RunSync, + RunAsync + }; + static int runElevatedLinux(const std::string& executable, + const std::list<std::string>& args, + const std::string& task); + static int runElevatedMac(const std::string& executable, + const std::list<std::string>& args); + static int runElevatedWindows(const std::string& executable, + const std::list<std::string>& args); + + static PLATFORM_PID runAsyncUnix(const std::string& executable, + const std::list<std::string>& args); + static int runWindows(const std::string& executable, + const std::list<std::string>& args, + RunMode runMode); + static int runSyncUnix(const std::string& executable, + const std::list<std::string>& args); +}; + diff --git a/mmc_updater/src/StandardDirs.cpp b/mmc_updater/src/StandardDirs.cpp new file mode 100644 index 00000000..72743d5e --- /dev/null +++ b/mmc_updater/src/StandardDirs.cpp @@ -0,0 +1,63 @@ +#include "StandardDirs.h" + +#include "FileUtils.h" +#include "StringUtils.h" + +#ifdef PLATFORM_UNIX + #include <stdlib.h> + #include <pwd.h> + #include <unistd.h> +#endif + +#ifdef PLATFORM_WINDOWS +#include <shlobj.h> +#endif + +#ifdef PLATFORM_UNIX +std::string StandardDirs::homeDir() +{ + std::string dir = notNullString(getenv("HOME")); + if (!dir.empty()) + { + return dir; + } + else + { + // note: if this process has been elevated with sudo, + // this will return the home directory of the root user + struct passwd* userData = getpwuid(getuid()); + return notNullString(userData->pw_dir); + } +} +#endif + +std::string StandardDirs::appDataPath(const std::string& organizationName, + const std::string& appName) +{ +#ifdef PLATFORM_LINUX + std::string xdgDataHome = notNullString(getenv("XDG_DATA_HOME")); + if (xdgDataHome.empty()) + { + xdgDataHome = homeDir() + "/.local/share"; + } + xdgDataHome += "/data/" + organizationName + '/' + appName; + return xdgDataHome; + +#elif defined(PLATFORM_MAC) + std::string path = applicationSupportFolderPath(); + path += '/' + appName; + return path; +#elif defined(PLATFORM_WINDOWS) + char buffer[MAX_PATH + 1]; + if (SHGetFolderPath(0, CSIDL_LOCAL_APPDATA, 0 /* hToken */, SHGFP_TYPE_CURRENT, buffer) == S_OK) + { + std::string path = FileUtils::toUnixPathSeparators(notNullString(buffer)); + path += '/' + organizationName + '/' + appName; + return path; + } + else + { + return std::string(); + } +#endif +} diff --git a/mmc_updater/src/StandardDirs.h b/mmc_updater/src/StandardDirs.h new file mode 100644 index 00000000..18526173 --- /dev/null +++ b/mmc_updater/src/StandardDirs.h @@ -0,0 +1,22 @@ +#pragma once + +#include "Platform.h" + +#include <string> + +class StandardDirs +{ + public: + static std::string appDataPath(const std::string& organizationName, + const std::string& appName); + + private: +#ifdef PLATFORM_UNIX + static std::string homeDir(); +#endif + +#ifdef PLATFORM_MAC + static std::string applicationSupportFolderPath(); +#endif +}; + diff --git a/mmc_updater/src/StandardDirs.mm b/mmc_updater/src/StandardDirs.mm new file mode 100644 index 00000000..53eecd47 --- /dev/null +++ b/mmc_updater/src/StandardDirs.mm @@ -0,0 +1,18 @@ +#include <Foundation/Foundation.h> + +#include "StandardDirs.h" + +std::string StandardDirs::applicationSupportFolderPath() +{ + NSArray* paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, + NSUserDomainMask, + true /* expand tildes */); + + for (unsigned int i=0; i < [paths count]; i++) + { + NSString* path = [paths objectAtIndex:i]; + return std::string([path UTF8String]); + } + return std::string(); +} + diff --git a/mmc_updater/src/StlSymbolsLeopard.cpp b/mmc_updater/src/StlSymbolsLeopard.cpp new file mode 100644 index 00000000..932c78f0 --- /dev/null +++ b/mmc_updater/src/StlSymbolsLeopard.cpp @@ -0,0 +1,81 @@ +// Workarounds for iostream symbols that are referenced when building on OS X 10.7 but missing from +// OS X 10.5's stdlibc++.dylib. +// +// In the <iostream> headers these are declared as extern templates but the symbols are not present under 10.5. +// This file forces the compiler to instantiate the templates. +// +// see http://stackoverflow.com/questions/3484043/os-x-program-runs-on-dev-machine-crashing-horribly-on-others + +// Only do this on GCC. It fails builds on clang. +#if defined(__GCC__) + +#include <iostream> + +_GLIBCXX_BEGIN_NAMESPACE(std) +// From ostream_insert.h +template ostream& __ostream_insert(ostream&, const char*, streamsize); + +#ifdef _GLIBCXX_USE_WCHAR_T +template wostream& __ostream_insert(wostream&, const wchar_t*, streamsize); +#endif + +// From ostream.tcc +template ostream& ostream::_M_insert(long); +template ostream& ostream::_M_insert(unsigned long); +template ostream& ostream::_M_insert(bool); +#ifdef _GLIBCXX_USE_LONG_LONG +template ostream& ostream::_M_insert(long long); +template ostream& ostream::_M_insert(unsigned long long); +#endif +template ostream& ostream::_M_insert(double); +template ostream& ostream::_M_insert(long double); +template ostream& ostream::_M_insert(const void*); + +#ifdef _GLIBCXX_USE_WCHAR_T +template wostream& wostream::_M_insert(long); +template wostream& wostream::_M_insert(unsigned long); +template wostream& wostream::_M_insert(bool); +#ifdef _GLIBCXX_USE_LONG_LONG +template wostream& wostream::_M_insert(long long); +template wostream& wostream::_M_insert(unsigned long long); +#endif +template wostream& wostream::_M_insert(double); +template wostream& wostream::_M_insert(long double); +template wostream& wostream::_M_insert(const void*); +#endif + +// From istream.tcc +template istream& istream::_M_extract(unsigned short&); +template istream& istream::_M_extract(unsigned int&); +template istream& istream::_M_extract(long&); +template istream& istream::_M_extract(unsigned long&); +template istream& istream::_M_extract(bool&); +#ifdef _GLIBCXX_USE_LONG_LONG +template istream& istream::_M_extract(long long&); +template istream& istream::_M_extract(unsigned long long&); +#endif +template istream& istream::_M_extract(float&); +template istream& istream::_M_extract(double&); +template istream& istream::_M_extract(long double&); +template istream& istream::_M_extract(void*&); + +#ifdef _GLIBCXX_USE_WCHAR_T +template wistream& wistream::_M_extract(unsigned short&); +template wistream& wistream::_M_extract(unsigned int&); +template wistream& wistream::_M_extract(long&); +template wistream& wistream::_M_extract(unsigned long&); +template wistream& wistream::_M_extract(bool&); +#ifdef _GLIBCXX_USE_LONG_LONG +template wistream& wistream::_M_extract(long long&); +template wistream& wistream::_M_extract(unsigned long long&); +#endif +template wistream& wistream::_M_extract(float&); +template wistream& wistream::_M_extract(double&); +template wistream& wistream::_M_extract(long double&); +template wistream& wistream::_M_extract(void*&); +#endif + +_GLIBCXX_END_NAMESPACE + +#endif + diff --git a/mmc_updater/src/StringUtils.h b/mmc_updater/src/StringUtils.h new file mode 100644 index 00000000..745b71c9 --- /dev/null +++ b/mmc_updater/src/StringUtils.h @@ -0,0 +1,46 @@ +#pragma once + +#include <string.h> +#include <string> +#include <sstream> +#include <stdlib.h> + +template <class T> +inline std::string intToStr(T i) +{ + std::stringstream stream; + stream << i; + return stream.str(); +} + +inline bool strToBool(const std::string& str) +{ + return str == "true" || atoi(str.c_str()) != 0; +} + +/** Returns @p text if non-null or a pointer + * to an empty null-terminated string otherwise. + */ +inline const char* notNullString(const char* text) +{ + if (text) + { + return text; + } + else + { + return ""; + } +} + +inline bool endsWith(const std::string& str, const char* text) +{ + size_t length = strlen(text); + return str.find(text,str.size() - length) != std::string::npos; +} + +inline bool startsWith(const std::string& str, const char* text) +{ + return str.find(text,0) == 0; +} + diff --git a/mmc_updater/src/UpdateDialog.cpp b/mmc_updater/src/UpdateDialog.cpp new file mode 100644 index 00000000..5f181a1c --- /dev/null +++ b/mmc_updater/src/UpdateDialog.cpp @@ -0,0 +1,25 @@ +#include "UpdateDialog.h" + +UpdateDialog::UpdateDialog() +: m_autoClose(false) +{ +} + +void UpdateDialog::setAutoClose(bool autoClose) +{ + m_autoClose = autoClose; +} + +bool UpdateDialog::autoClose() const +{ + return m_autoClose; +} + +void UpdateDialog::updateFinished() +{ + if (m_autoClose) + { + quit(); + } +} + diff --git a/mmc_updater/src/UpdateDialog.h b/mmc_updater/src/UpdateDialog.h new file mode 100644 index 00000000..d0782f8f --- /dev/null +++ b/mmc_updater/src/UpdateDialog.h @@ -0,0 +1,29 @@ +#pragma once + +#include "UpdateObserver.h" + +/** Base class for the updater's UI, sub-classed + * by the different platform implementations. + */ +class UpdateDialog : public UpdateObserver +{ + public: + UpdateDialog(); + virtual ~UpdateDialog() {}; + + /** Sets whether the updater should automatically + * exit once the update has been installed. + */ + void setAutoClose(bool autoClose); + bool autoClose() const; + + virtual void init(int argc, char** argv) = 0; + virtual void exec() = 0; + virtual void quit() = 0; + + virtual void updateFinished(); + + private: + bool m_autoClose; +}; + diff --git a/mmc_updater/src/UpdateDialogAscii.cpp b/mmc_updater/src/UpdateDialogAscii.cpp new file mode 100644 index 00000000..78eb7433 --- /dev/null +++ b/mmc_updater/src/UpdateDialogAscii.cpp @@ -0,0 +1,70 @@ +#include "UpdateDialogAscii.h" + +#include "AppInfo.h" +#include "ProcessUtils.h" +#include "StringUtils.h" + +const char* introMessage = + "%s (ASCII-art edition)\n" + "====================================\n" + "\n" + "We have a nice graphical interface for the %s, but unfortunately\n" + "we can't show it to you :(\n" + "\n" + "You can fix this by installing the GTK 2 libraries.\n\n" + "Installing Updates...\n"; + +void UpdateDialogAscii::init(int /* argc */, char** /* argv */) +{ + const char* path = "/tmp/update-progress"; + m_output.open(path); + + char message[4096]; + sprintf(message,introMessage,AppInfo::name().c_str()); + m_output << message; + + std::string command = "xterm"; + std::list<std::string> args; + args.push_back("-hold"); + args.push_back("-T"); + args.push_back(AppInfo::name()); + args.push_back("-e"); + args.push_back("tail"); + args.push_back("-n+1"); + args.push_back("-f"); + args.push_back(path); + + ProcessUtils::runAsync(command,args); +} + +void UpdateDialogAscii::updateError(const std::string& errorMessage) +{ + m_mutex.lock(); + m_output << "\nThere was a problem installing the update: " << errorMessage << std::endl; + m_mutex.unlock(); +} + +void UpdateDialogAscii::updateProgress(int percentage) +{ + m_mutex.lock(); + m_output << "Update Progress: " << intToStr(percentage) << '%' << std::endl; + m_mutex.unlock(); +} + +void UpdateDialogAscii::updateFinished() +{ + m_mutex.lock(); + m_output << "\nUpdate Finished. You can now restart " << AppInfo::appName() << "." << std::endl; + m_mutex.unlock(); + + UpdateDialog::updateFinished(); +} + +void UpdateDialogAscii::quit() +{ +} + +void UpdateDialogAscii::exec() +{ +} + diff --git a/mmc_updater/src/UpdateDialogAscii.h b/mmc_updater/src/UpdateDialogAscii.h new file mode 100644 index 00000000..138194c5 --- /dev/null +++ b/mmc_updater/src/UpdateDialogAscii.h @@ -0,0 +1,32 @@ +#pragma once + +#include "UpdateDialog.h" + +#include <fstream> +#include <thread> +#include <mutex> + +/** A fallback auto-update progress 'dialog' for use on + * Linux when the GTK UI cannot be loaded. + * + * The 'dialog' consists of an xterm tailing the contents + * of a file, into which progress messages are written. + */ +class UpdateDialogAscii : public UpdateDialog +{ + public: + // implements UpdateDialog + virtual void init(int argc, char** argv); + virtual void exec(); + virtual void quit(); + + // implements UpdateObserver + virtual void updateError(const std::string& errorMessage); + virtual void updateProgress(int percentage); + virtual void updateFinished(); + + private: + std::mutex m_mutex; + std::ofstream m_output; +}; + diff --git a/mmc_updater/src/UpdateDialogCocoa.h b/mmc_updater/src/UpdateDialogCocoa.h new file mode 100644 index 00000000..586fdc8e --- /dev/null +++ b/mmc_updater/src/UpdateDialogCocoa.h @@ -0,0 +1,32 @@ +#pragma once + +#include "UpdateDialog.h" +#include "UpdateObserver.h" + +class UpdateDialogPrivate; + +class UpdateDialogCocoa : public UpdateDialog +{ + public: + UpdateDialogCocoa(); + ~UpdateDialogCocoa(); + + // implements UpdateDialog + virtual void init(int argc, char** argv); + virtual void exec(); + virtual void quit(); + + // implements UpdateObserver + virtual void updateError(const std::string& errorMessage); + virtual void updateProgress(int percentage); + virtual void updateFinished(); + + static void* createAutoreleasePool(); + static void releaseAutoreleasePool(void* data); + + private: + void enableDockIcon(); + + UpdateDialogPrivate* d; +}; + diff --git a/mmc_updater/src/UpdateDialogCocoa.mm b/mmc_updater/src/UpdateDialogCocoa.mm new file mode 100644 index 00000000..f24f3c4d --- /dev/null +++ b/mmc_updater/src/UpdateDialogCocoa.mm @@ -0,0 +1,194 @@ +#include "UpdateDialogCocoa.h" + +#include <Cocoa/Cocoa.h> +#include <Carbon/Carbon.h> + +#include "AppInfo.h" +#include "Log.h" +#include "StringUtils.h" + +@interface UpdateDialogDelegate : NSObject +{ + @public UpdateDialogPrivate* dialog; +} +- (void) finishClicked; +- (void) reportUpdateError:(id)arg; +- (void) reportUpdateProgress:(id)arg; +- (void) reportUpdateFinished:(id)arg; +@end + +class UpdateDialogPrivate +{ + public: + UpdateDialogPrivate() + : hadError(false) + { + } + + UpdateDialogDelegate* delegate; + NSAutoreleasePool* pool; + NSWindow* window; + NSButton* finishButton; + NSTextField* progressLabel; + NSProgressIndicator* progressBar; + bool hadError; +}; + +@implementation UpdateDialogDelegate +- (void) finishClicked +{ + [NSApp stop:self]; +} +- (void) reportUpdateError: (id)arg +{ + dialog->hadError = true; + + NSAlert* alert = [NSAlert + alertWithMessageText: @"Update Problem" + defaultButton: nil + alternateButton: nil + otherButton: nil + informativeTextWithFormat: @"There was a problem installing the update:\n\n%@", arg]; + [alert runModal]; +} +- (void) reportUpdateProgress: (id)arg +{ + int percentage = [arg intValue]; + [dialog->progressBar setDoubleValue:(percentage/100.0)]; +} +- (void) reportUpdateFinished: (id)arg +{ + NSMutableString* message = [[NSMutableString alloc] init]; + if (!dialog->hadError) + { + [message appendString:@"Updates installed."]; + } + else + { + [message appendString:@"Update failed."]; + } + + [message appendString:@" Click 'Finish' to restart the application."]; + [dialog->progressLabel setTitleWithMnemonic:message]; + [message release]; +} +@end + +UpdateDialogCocoa::UpdateDialogCocoa() +: d(new UpdateDialogPrivate) +{ + [NSApplication sharedApplication]; + d->pool = [[NSAutoreleasePool alloc] init]; +} + +UpdateDialogCocoa::~UpdateDialogCocoa() +{ + [d->pool release]; +} + +void UpdateDialogCocoa::enableDockIcon() +{ + // convert the application to a foreground application and in + // the process, enable the dock icon + + // the reverse transformation is not possible, according to + // http://stackoverflow.com/questions/2832961/is-it-possible-to-hide-the-dock-icon-programmatically + ProcessSerialNumber psn; + GetCurrentProcess(&psn); + TransformProcessType(&psn,kProcessTransformToForegroundApplication); +} + +void UpdateDialogCocoa::init(int /* argc */, char** /* argv */) +{ + enableDockIcon(); + + // make the updater the active application. This does not + // happen automatically because the updater starts as a + // background application + [NSApp activateIgnoringOtherApps:YES]; + + d->delegate = [[UpdateDialogDelegate alloc] init]; + d->delegate->dialog = d; + + int width = 370; + int height = 100; + + d->window = [[NSWindow alloc] initWithContentRect:NSMakeRect(200, 200, width, height) + styleMask:NSTitledWindowMask | NSMiniaturizableWindowMask + backing:NSBackingStoreBuffered defer:NO]; + [d->window setTitle:[NSString stringWithUTF8String:AppInfo::name().c_str()]]; + + d->finishButton = [[NSButton alloc] init]; + [d->finishButton setTitle:@"Finish"]; + [d->finishButton setButtonType:NSMomentaryLightButton]; + [d->finishButton setBezelStyle:NSRoundedBezelStyle]; + [d->finishButton setTarget:d->delegate]; + [d->finishButton setAction:@selector(finishClicked)]; + + d->progressBar = [[NSProgressIndicator alloc] init]; + [d->progressBar setIndeterminate:false]; + [d->progressBar setMinValue:0.0]; + [d->progressBar setMaxValue:1.0]; + + d->progressLabel = [[NSTextField alloc] init]; + [d->progressLabel setEditable:false]; + [d->progressLabel setSelectable:false]; + [d->progressLabel setTitleWithMnemonic:@"Installing Updates"]; + [d->progressLabel setBezeled:false]; + [d->progressLabel setDrawsBackground:false]; + + NSView* windowContent = [d->window contentView]; + [windowContent addSubview:d->progressLabel]; + [windowContent addSubview:d->progressBar]; + [windowContent addSubview:d->finishButton]; + + [d->progressLabel setFrame:NSMakeRect(10,70,width - 10,20)]; + [d->progressBar setFrame:NSMakeRect(10,40,width - 20,20)]; + [d->finishButton setFrame:NSMakeRect(width - 85,5,80,30)]; +} + +void UpdateDialogCocoa::exec() +{ + [d->window makeKeyAndOrderFront:d->window]; + [d->window center]; + [NSApp run]; +} + +void UpdateDialogCocoa::updateError(const std::string& errorMessage) +{ + [d->delegate performSelectorOnMainThread:@selector(reportUpdateError:) + withObject:[NSString stringWithUTF8String:errorMessage.c_str()] + waitUntilDone:false]; +} + +void UpdateDialogCocoa::updateProgress(int percentage) +{ + [d->delegate performSelectorOnMainThread:@selector(reportUpdateProgress:) + withObject:[NSNumber numberWithInt:percentage] + waitUntilDone:false]; +} + +void UpdateDialogCocoa::updateFinished() +{ + [d->delegate performSelectorOnMainThread:@selector(reportUpdateFinished:) + withObject:nil + waitUntilDone:false]; + UpdateDialog::updateFinished(); +} + +void* UpdateDialogCocoa::createAutoreleasePool() +{ + return [[NSAutoreleasePool alloc] init]; +} + +void UpdateDialogCocoa::releaseAutoreleasePool(void* arg) +{ + [(id)arg release]; +} + +void UpdateDialogCocoa::quit() +{ + [NSApp performSelectorOnMainThread:@selector(stop:) withObject:d->delegate waitUntilDone:false]; +} + + diff --git a/mmc_updater/src/UpdateDialogGtk.cpp b/mmc_updater/src/UpdateDialogGtk.cpp new file mode 100644 index 00000000..d91144f5 --- /dev/null +++ b/mmc_updater/src/UpdateDialogGtk.cpp @@ -0,0 +1,155 @@ +#include "UpdateDialogGtk.h" + +#include "AppInfo.h" +#include "StringUtils.h" + +#include <glib.h> +#include <gtk/gtk.h> + +UpdateDialogGtk* update_dialog_gtk_new() +{ + return new UpdateDialogGtk(); +} + +UpdateDialogGtk::UpdateDialogGtk() +: m_hadError(false) +{ +} + +void UpdateDialogGtk::init(int argc, char** argv) +{ + gtk_init(&argc,&argv); + + m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(m_window),AppInfo::name().c_str()); + + m_progressLabel = gtk_label_new("Installing Updates"); + + GtkWidget* windowLayout = gtk_vbox_new(FALSE,3); + GtkWidget* buttonLayout = gtk_hbox_new(FALSE,3); + GtkWidget* labelLayout = gtk_hbox_new(FALSE,3); + + m_finishButton = gtk_button_new_with_label("Finish"); + gtk_widget_set_sensitive(m_finishButton,false); + + m_progressBar = gtk_progress_bar_new(); + + // give the dialog a sensible default size by setting a minimum + // width on the progress bar. This is used instead of setting + // a default size for the dialog since gtk_window_set_default_size() + // is ignored when a dialog is marked as non-resizable + gtk_widget_set_usize(m_progressBar,350,-1); + + gtk_signal_connect(GTK_OBJECT(m_finishButton),"clicked", + GTK_SIGNAL_FUNC(UpdateDialogGtk::finish),this); + + gtk_container_add(GTK_CONTAINER(m_window),windowLayout); + gtk_container_set_border_width(GTK_CONTAINER(m_window),12); + + gtk_box_pack_start(GTK_BOX(labelLayout),m_progressLabel,false,false,0); + gtk_box_pack_end(GTK_BOX(buttonLayout),m_finishButton,false,false,0); + + gtk_box_pack_start(GTK_BOX(windowLayout),labelLayout,false,false,0); + gtk_box_pack_start(GTK_BOX(windowLayout),m_progressBar,false,false,0); + gtk_box_pack_start(GTK_BOX(windowLayout),buttonLayout,false,false,0); + + + gtk_widget_show(m_progressLabel); + gtk_widget_show(labelLayout); + gtk_widget_show(windowLayout); + gtk_widget_show(buttonLayout); + gtk_widget_show(m_finishButton); + gtk_widget_show(m_progressBar); + + gtk_window_set_resizable(GTK_WINDOW(m_window),false); + gtk_window_set_position(GTK_WINDOW(m_window),GTK_WIN_POS_CENTER); + + gtk_widget_show(m_window); +} + +void UpdateDialogGtk::exec() +{ + gtk_main(); +} + +void UpdateDialogGtk::finish(GtkWidget* widget, gpointer _dialog) +{ + UpdateDialogGtk* dialog = static_cast<UpdateDialogGtk*>(_dialog); + dialog->quit(); +} + +void UpdateDialogGtk::quit() +{ + gtk_main_quit(); +} + +gboolean UpdateDialogGtk::notify(void* _message) +{ + UpdateMessage* message = static_cast<UpdateMessage*>(_message); + UpdateDialogGtk* dialog = static_cast<UpdateDialogGtk*>(message->receiver); + switch (message->type) + { + case UpdateMessage::UpdateProgress: + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(dialog->m_progressBar),message->progress/100.0); + break; + case UpdateMessage::UpdateFailed: + { + dialog->m_hadError = true; + std::string errorMessage = AppInfo::updateErrorMessage(message->message); + GtkWidget* errorDialog = gtk_message_dialog_new (GTK_WINDOW(dialog->m_window), + GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_MESSAGE_ERROR, + GTK_BUTTONS_CLOSE, + "%s", + errorMessage.c_str()); + gtk_dialog_run (GTK_DIALOG (errorDialog)); + gtk_widget_destroy (errorDialog); + gtk_widget_set_sensitive(dialog->m_finishButton,true); + } + break; + case UpdateMessage::UpdateFinished: + { + std::string message; + if (dialog->m_hadError) + { + message = "Update failed."; + } + else + { + message = "Update installed."; + } + message += " Click 'Finish' to restart the application."; + gtk_label_set_text(GTK_LABEL(dialog->m_progressLabel),message.c_str()); + gtk_widget_set_sensitive(dialog->m_finishButton,true); + } + break; + } + delete message; + + // do not invoke this function again + return false; +} + +// callbacks during update installation +void UpdateDialogGtk::updateError(const std::string& errorMessage) +{ + UpdateMessage* message = new UpdateMessage(this,UpdateMessage::UpdateFailed); + message->message = errorMessage; + g_idle_add(&UpdateDialogGtk::notify,message); +} + +void UpdateDialogGtk::updateProgress(int percentage) +{ + UpdateMessage* message = new UpdateMessage(this,UpdateMessage::UpdateProgress); + message->progress = percentage; + g_idle_add(&UpdateDialogGtk::notify,message); +} + +void UpdateDialogGtk::updateFinished() +{ + UpdateMessage* message = new UpdateMessage(this,UpdateMessage::UpdateFinished); + g_idle_add(&UpdateDialogGtk::notify,message); + UpdateDialog::updateFinished(); +} + + diff --git a/mmc_updater/src/UpdateDialogGtk.h b/mmc_updater/src/UpdateDialogGtk.h new file mode 100644 index 00000000..70e29c78 --- /dev/null +++ b/mmc_updater/src/UpdateDialogGtk.h @@ -0,0 +1,42 @@ +#pragma once + +#include "UpdateDialog.h" +#include "UpdateMessage.h" +#include "UpdateObserver.h" + +#include <gtk/gtk.h> + +class UpdateDialogGtk : public UpdateDialog +{ + public: + UpdateDialogGtk(); + + // implements UpdateDialog + virtual void init(int argc, char** argv); + virtual void exec(); + virtual void quit(); + + // observer callbacks - these may be called + // from a background thread + virtual void updateError(const std::string& errorMessage); + virtual void updateProgress(int percentage); + virtual void updateFinished(); + + private: + static void finish(GtkWidget* widget, gpointer dialog); + static gboolean notify(void* message); + + GtkWidget* m_window; + GtkWidget* m_progressLabel; + GtkWidget* m_finishButton; + GtkWidget* m_progressBar; + bool m_hadError; +}; + +// helper functions which allow the GTK dialog to be loaded dynamically +// at runtime and used only if the GTK libraries are actually present +extern "C" { + UpdateDialogGtk* update_dialog_gtk_new(); +} + + diff --git a/mmc_updater/src/UpdateDialogGtkFactory.cpp b/mmc_updater/src/UpdateDialogGtkFactory.cpp new file mode 100644 index 00000000..313da31a --- /dev/null +++ b/mmc_updater/src/UpdateDialogGtkFactory.cpp @@ -0,0 +1,59 @@ +#include "UpdateDialogGtkFactory.h" + +#include "Log.h" +#include "UpdateDialog.h" +#include "StringUtils.h" + +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +class UpdateDialogGtk; + +// GTK updater UI library embedded into +// the updater binary +extern unsigned char libupdatergtk_so[]; +extern unsigned int libupdatergtk_so_len; + +// pointers to helper functions in the GTK updater UI library +UpdateDialogGtk* (*update_dialog_gtk_new)() = 0; + +bool extractFileFromBinary(const char* path, const void* buffer, size_t length) +{ + int fd = open(path,O_CREAT | O_WRONLY | O_TRUNC,0755); + size_t count = write(fd,buffer,length); + if (fd < 0 || count < length) + { + if (fd >= 0) + { + close(fd); + } + return false; + } + close(fd); + return true; +} + +UpdateDialog* UpdateDialogGtkFactory::createDialog() +{ + const char* libPath = "/tmp/libupdatergtk.so"; + + if (!extractFileFromBinary(libPath,libupdatergtk_so,libupdatergtk_so_len)) + { + LOG(Warn,"Failed to load the GTK UI library - " + std::string(strerror(errno))); + return 0; + } + + void* gtkLib = dlopen(libPath,RTLD_LAZY); + if (!gtkLib) + { + LOG(Warn,"Failed to load the GTK UI - " + std::string(dlerror())); + return 0; + } + update_dialog_gtk_new = (UpdateDialogGtk* (*)()) dlsym(gtkLib,"update_dialog_gtk_new"); + return reinterpret_cast<UpdateDialog*>(update_dialog_gtk_new()); +} + diff --git a/mmc_updater/src/UpdateDialogGtkFactory.h b/mmc_updater/src/UpdateDialogGtkFactory.h new file mode 100644 index 00000000..1806c252 --- /dev/null +++ b/mmc_updater/src/UpdateDialogGtkFactory.h @@ -0,0 +1,13 @@ +#pragma once + +class UpdateDialog; + +/** Factory for loading the GTK version of the update dialog + * dynamically at runtime if the GTK libraries are available. + */ +class UpdateDialogGtkFactory +{ + public: + static UpdateDialog* createDialog(); +}; + diff --git a/mmc_updater/src/UpdateDialogWin32.cpp b/mmc_updater/src/UpdateDialogWin32.cpp new file mode 100644 index 00000000..bdc25437 --- /dev/null +++ b/mmc_updater/src/UpdateDialogWin32.cpp @@ -0,0 +1,215 @@ +#include "UpdateDialogWin32.h" + +#include "AppInfo.h" +#include "Log.h" + +// enable themed controls +// see http://msdn.microsoft.com/en-us/library/bb773175%28v=vs.85%29.aspx +// for details +#pragma comment(linker,"\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +static const char* updateDialogClassName = "UPDATEDIALOG"; + +static std::map<HWND,UpdateDialogWin32*> windowDialogMap; + +// enable the standard Windows font for a widget +// (typically Tahoma or Segoe UI) +void setDefaultFont(HWND window) +{ + SendMessage(window, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(TRUE, 0)); +} + +LRESULT WINAPI updateDialogWindowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) +{ + std::map<HWND,UpdateDialogWin32*>::const_iterator iter = windowDialogMap.find(window); + if (iter != windowDialogMap.end()) + { + return iter->second->windowProc(window,message,wParam,lParam); + } + else + { + return DefWindowProc(window,message,wParam,lParam); + } +}; + +void registerWindowClass() +{ + WNDCLASSEX wcex; + ZeroMemory(&wcex,sizeof(WNDCLASSEX)); + + wcex.cbSize = sizeof(WNDCLASSEX); + + HBRUSH background = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = updateDialogWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hIcon = LoadIcon(GetModuleHandle(0),"IDI_APPICON"); + wcex.hCursor = LoadCursor(0,IDC_ARROW); + wcex.hbrBackground = (HBRUSH)background; + wcex.lpszMenuName = (LPCTSTR)0; + wcex.lpszClassName = updateDialogClassName; + wcex.hIconSm = 0; + wcex.hInstance = GetModuleHandle(0); + + RegisterClassEx(&wcex); +} + +UpdateDialogWin32::UpdateDialogWin32() +: m_hadError(false) +{ + registerWindowClass(); +} + +UpdateDialogWin32::~UpdateDialogWin32() +{ + for (std::map<HWND,UpdateDialogWin32*>::iterator iter = windowDialogMap.begin(); + iter != windowDialogMap.end(); + iter++) + { + if (iter->second == this) + { + std::map<HWND,UpdateDialogWin32*>::iterator oldIter = iter; + ++iter; + windowDialogMap.erase(oldIter); + } + else + { + ++iter; + } + } +} + +void UpdateDialogWin32::init(int /* argc */, char** /* argv */) +{ + int width = 300; + int height = 130; + + DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + m_window.CreateEx(0 /* dwExStyle */, + updateDialogClassName /* class name */, + AppInfo::name().c_str(), + style, + 0, 0, width, height, + 0 /* parent */, 0 /* menu */, 0 /* reserved */); + m_progressBar.Create(&m_window); + m_finishButton.Create(&m_window); + m_progressLabel.Create(&m_window); + + installWindowProc(&m_window); + installWindowProc(&m_finishButton); + + setDefaultFont(m_progressLabel); + setDefaultFont(m_finishButton); + + m_progressBar.SetRange(0,100); + m_finishButton.SetWindowText("Finish"); + m_finishButton.EnableWindow(false); + m_progressLabel.SetWindowText("Installing Updates"); + + m_window.SetWindowPos(0,0,0,width,height,0); + m_progressBar.SetWindowPos(0,10,40,width - 30,20,0); + m_progressLabel.SetWindowPos(0,10,15,width - 30,20,0); + m_finishButton.SetWindowPos(0,width-100,70,80,25,0); + m_window.CenterWindow(); + m_window.ShowWindow(); +} + +void UpdateDialogWin32::exec() +{ + m_app.Run(); +} + +void UpdateDialogWin32::updateError(const std::string& errorMessage) +{ + UpdateMessage* message = new UpdateMessage(UpdateMessage::UpdateFailed); + message->message = errorMessage; + SendNotifyMessage(m_window.GetHwnd(),WM_USER,reinterpret_cast<WPARAM>(message),0); +} + +void UpdateDialogWin32::updateProgress(int percentage) +{ + UpdateMessage* message = new UpdateMessage(UpdateMessage::UpdateProgress); + message->progress = percentage; + SendNotifyMessage(m_window.GetHwnd(),WM_USER,reinterpret_cast<WPARAM>(message),0); +} + +void UpdateDialogWin32::updateFinished() +{ + UpdateMessage* message = new UpdateMessage(UpdateMessage::UpdateFinished); + SendNotifyMessage(m_window.GetHwnd(),WM_USER,reinterpret_cast<WPARAM>(message),0); + UpdateDialog::updateFinished(); +} + +void UpdateDialogWin32::quit() +{ + PostThreadMessage(GetWindowThreadProcessId(m_window.GetHwnd(), 0 /* process ID */), WM_QUIT, 0, 0); +} + +LRESULT WINAPI UpdateDialogWin32::windowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CLOSE: + if (window == m_window.GetHwnd()) + { + return 0; + } + break; + case WM_COMMAND: + { + if (reinterpret_cast<HWND>(lParam) == m_finishButton.GetHwnd()) + { + quit(); + } + } + break; + case WM_USER: + { + if (window == m_window.GetHwnd()) + { + UpdateMessage* message = reinterpret_cast<UpdateMessage*>(wParam); + switch (message->type) + { + case UpdateMessage::UpdateFailed: + { + m_hadError = true; + std::string text = AppInfo::updateErrorMessage(message->message); + MessageBox(m_window.GetHwnd(),text.c_str(),"Update Problem",MB_OK); + } + break; + case UpdateMessage::UpdateProgress: + m_progressBar.SetPos(message->progress); + break; + case UpdateMessage::UpdateFinished: + { + std::string message; + m_finishButton.EnableWindow(true); + if (m_hadError) + { + message = "Update failed."; + } + else + { + message = "Updates installed."; + } + message += " Click 'Finish' to restart the application."; + m_progressLabel.SetWindowText(message.c_str()); + } + break; + } + delete message; + } + } + break; + } + return DefWindowProc(window,message,wParam,lParam); +} + +void UpdateDialogWin32::installWindowProc(CWnd* window) +{ + windowDialogMap[window->GetHwnd()] = this; +} diff --git a/mmc_updater/src/UpdateDialogWin32.h b/mmc_updater/src/UpdateDialogWin32.h new file mode 100644 index 00000000..fe4208c8 --- /dev/null +++ b/mmc_updater/src/UpdateDialogWin32.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Platform.h" +#include "UpdateDialog.h" +#include "UpdateMessage.h" + +#include "wincore.h" +#include "controls.h" +#include "stdcontrols.h" + +class UpdateDialogWin32 : public UpdateDialog +{ + public: + UpdateDialogWin32(); + ~UpdateDialogWin32(); + + // implements UpdateDialog + virtual void init(int argc, char** argv); + virtual void exec(); + virtual void quit(); + + // implements UpdateObserver + virtual void updateError(const std::string& errorMessage); + virtual void updateProgress(int percentage); + virtual void updateFinished(); + + LRESULT WINAPI windowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam); + + private: + void installWindowProc(CWnd* window); + + CWinApp m_app; + CWnd m_window; + CStatic m_progressLabel; + CProgressBar m_progressBar; + CButton m_finishButton; + bool m_hadError; +}; + diff --git a/mmc_updater/src/UpdateInstaller.cpp b/mmc_updater/src/UpdateInstaller.cpp new file mode 100644 index 00000000..ced6ff39 --- /dev/null +++ b/mmc_updater/src/UpdateInstaller.cpp @@ -0,0 +1,426 @@ +#include "UpdateInstaller.h" + +#include "AppInfo.h" +#include "FileUtils.h" +#include "Log.h" +#include "ProcessUtils.h" +#include "UpdateObserver.h" + +UpdateInstaller::UpdateInstaller() +: m_mode(Setup) +, m_waitPid(0) +, m_script(0) +, m_observer(0) +, m_forceElevated(false) +, m_autoClose(false) +{ +} + +void UpdateInstaller::setWaitPid(PLATFORM_PID pid) +{ + m_waitPid = pid; +} + +void UpdateInstaller::setInstallDir(const std::string& path) +{ + m_installDir = path; +} + +void UpdateInstaller::setPackageDir(const std::string& path) +{ + m_packageDir = path; +} + +void UpdateInstaller::setBackupDir(const std::string& path) +{ + m_backupDir = path; +} + +void UpdateInstaller::setMode(Mode mode) +{ + m_mode = mode; +} + +void UpdateInstaller::setScript(UpdateScript* script) +{ + m_script = script; +} + +void UpdateInstaller::setForceElevated(bool elevated) +{ + m_forceElevated = elevated; +} + +void UpdateInstaller::setFinishCmd(const std::string& cmd) +{ + m_finishCmd = cmd; +} + +std::list<std::string> UpdateInstaller::updaterArgs() const +{ + std::list<std::string> args; + args.push_back("--install-dir"); + args.push_back(m_installDir); + args.push_back("--package-dir"); + args.push_back(m_packageDir); + args.push_back("--script"); + args.push_back(m_script->path()); + if (m_autoClose) + { + args.push_back("--auto-close"); + } + return args; +} + +void UpdateInstaller::reportError(const std::string& error) +{ + if (m_observer) + { + m_observer->updateError(error); + m_observer->updateFinished(); + } +} + +std::string UpdateInstaller::friendlyErrorForError(const FileUtils::IOException& exception) const +{ + std::string friendlyError; + + switch (exception.type()) + { + case FileUtils::IOException::ReadOnlyFileSystem: +#ifdef PLATFORM_MAC + friendlyError = AppInfo::appName() + " was started from a read-only location. " + "Copy it to the Applications folder on your Mac and run " + "it from there."; +#else + friendlyError = AppInfo::appName() + " was started from a read-only location. " + "Re-install it to a location that can be updated and run it from there."; +#endif + break; + case FileUtils::IOException::DiskFull: + friendlyError = "The disk is full. Please free up some space and try again."; + break; + default: + break; + } + + return friendlyError; +} + +void UpdateInstaller::run() throw () +{ + if (!m_script || !m_script->isValid()) + { + reportError("Unable to read update script"); + return; + } + if (m_installDir.empty()) + { + reportError("No installation directory specified"); + return; + } + + std::string updaterPath; + try + { + updaterPath = ProcessUtils::currentProcessPath(); + } + catch (const FileUtils::IOException&) + { + LOG(Error,"error reading process path with mode " + intToStr(m_mode)); + reportError("Unable to determine path of updater"); + return; + } + + if (m_mode == Setup) + { + if (m_waitPid != 0) + { + LOG(Info,"Waiting for main app process to finish"); + ProcessUtils::waitForProcess(m_waitPid); + } + + std::list<std::string> args = updaterArgs(); + args.push_back("--mode"); + args.push_back("main"); + args.push_back("--wait"); + args.push_back(intToStr(ProcessUtils::currentProcessId())); + + int installStatus = 0; + if (m_forceElevated || !checkAccess()) + { + LOG(Info,"Insufficient rights to install app to " + m_installDir + " requesting elevation"); + + // start a copy of the updater with admin rights + installStatus = ProcessUtils::runElevated(updaterPath,args,AppInfo::name()); + } + else + { + LOG(Info,"Sufficient rights to install app - restarting with same permissions"); + installStatus = ProcessUtils::runSync(updaterPath,args); + } + + if (installStatus == 0) + { + LOG(Info,"Update install completed"); + } + else + { + LOG(Error,"Update install failed with status " + intToStr(installStatus)); + } + + // restart the main application - this is currently done + // regardless of whether the installation succeeds or not + restartMainApp(); + + // clean up files created by the updater + cleanup(); + } + else if (m_mode == Main) + { + LOG(Info,"Starting update installation"); + + // the detailed error string returned by the OS + std::string error; + // the message to present to the user. This may be the same + // as 'error' or may be different if a more helpful suggestion + // can be made for a particular problem + std::string friendlyError; + + try + { + LOG(Info,"Installing new and updated files"); + installFiles(); + + LOG(Info,"Uninstalling removed files"); + uninstallFiles(); + + LOG(Info,"Removing backups"); + removeBackups(); + + postInstallUpdate(); + } + catch (const FileUtils::IOException& exception) + { + error = exception.what(); + friendlyError = friendlyErrorForError(exception); + } + catch (const std::string& genericError) + { + error = genericError; + } + + if (!error.empty()) + { + LOG(Error,std::string("Error installing update ") + error); + + try + { + revert(); + } + catch (const FileUtils::IOException& exception) + { + LOG(Error,"Error reverting partial update " + std::string(exception.what())); + } + + if (m_observer) + { + if (friendlyError.empty()) + { + friendlyError = error; + } + m_observer->updateError(friendlyError); + } + } + + if (m_observer) + { + m_observer->updateFinished(); + } + } +} + +void UpdateInstaller::cleanup() +{ + try + { + FileUtils::rmdirRecursive(m_packageDir.c_str()); + } + catch (const FileUtils::IOException& ex) + { + LOG(Error,"Error cleaning up updater " + std::string(ex.what())); + } + LOG(Info,"Updater files removed"); +} + +void UpdateInstaller::revert() +{ + std::map<std::string,std::string>::const_iterator iter = m_backups.begin(); + for (;iter != m_backups.end();iter++) + { + const std::string& installedFile = iter->first; + const std::string& backupFile = iter->second; + + if (FileUtils::fileExists(installedFile.c_str())) + { + FileUtils::removeFile(installedFile.c_str()); + } + FileUtils::moveFile(backupFile.c_str(),installedFile.c_str()); + } +} + +void UpdateInstaller::installFile(const UpdateScriptFile& file) +{ + std::string destPath = file.dest; + std::string target = file.linkTarget; + + // backup the existing file if any + backupFile(destPath); + + // create the target directory if it does not exist + std::string destDir = FileUtils::dirname(destPath.c_str()); + if (!FileUtils::fileExists(destDir.c_str())) + { + FileUtils::mkpath(destDir.c_str()); + } + + std::string sourceFile = file.source; + if (!FileUtils::fileExists(sourceFile.c_str())) + { + throw "Source file does not exist: " + sourceFile; + } + FileUtils::copyFile(sourceFile.c_str(),destPath.c_str()); + + // set the permissions on the newly extracted file + FileUtils::chmod(destPath.c_str(),file.permissions); +} + +void UpdateInstaller::installFiles() +{ + std::vector<UpdateScriptFile>::const_iterator iter = m_script->filesToInstall().begin(); + int filesInstalled = 0; + for (;iter != m_script->filesToInstall().end();iter++) + { + installFile(*iter); + ++filesInstalled; + if (m_observer) + { + int toInstallCount = static_cast<int>(m_script->filesToInstall().size()); + double percentage = ((1.0 * filesInstalled) / toInstallCount) * 100.0; + m_observer->updateProgress(static_cast<int>(percentage)); + } + } +} + +void UpdateInstaller::uninstallFiles() +{ + std::vector<std::string>::const_iterator iter = m_script->filesToUninstall().begin(); + for (;iter != m_script->filesToUninstall().end();iter++) + { + std::string path = m_installDir + '/' + iter->c_str(); + if (FileUtils::fileExists(path.c_str())) + { + FileUtils::removeFile(path.c_str()); + } + else + { + LOG(Warn,"Unable to uninstall file " + path + " because it does not exist."); + } + } +} + +void UpdateInstaller::backupFile(const std::string& path) +{ + if (!FileUtils::fileExists(path.c_str())) + { + // no existing file to backup + return; + } + + std::string backupPath = path + ".bak"; + FileUtils::removeFile(backupPath.c_str()); + FileUtils::moveFile(path.c_str(), backupPath.c_str()); + m_backups[path] = backupPath; +} + +void UpdateInstaller::removeBackups() +{ + std::map<std::string,std::string>::const_iterator iter = m_backups.begin(); + for (;iter != m_backups.end();iter++) + { + const std::string& backupFile = iter->second; + FileUtils::removeFile(backupFile.c_str()); + } +} + +bool UpdateInstaller::checkAccess() +{ + std::string testFile = m_installDir + "/update-installer-test-file"; + + try + { + FileUtils::removeFile(testFile.c_str()); + } + catch (const FileUtils::IOException& error) + { + LOG(Info,"Removing existing access check file failed " + std::string(error.what())); + } + + try + { + FileUtils::touch(testFile.c_str()); + FileUtils::removeFile(testFile.c_str()); + return true; + } + catch (const FileUtils::IOException& error) + { + LOG(Info,"checkAccess() failed " + std::string(error.what())); + return false; + } +} + +void UpdateInstaller::setObserver(UpdateObserver* observer) +{ + m_observer = observer; +} + +void UpdateInstaller::restartMainApp() +{ + try + { + std::string command = m_finishCmd; + std::list<std::string> args; + + if (!command.empty()) + { + LOG(Info,"Starting main application " + command); + ProcessUtils::runAsync(command,args); + } + else + { + LOG(Error,"No main binary specified"); + } + } + catch (const std::exception& ex) + { + LOG(Error,"Unable to restart main app " + std::string(ex.what())); + } +} + +void UpdateInstaller::postInstallUpdate() +{ + // perform post-install actions + +#ifdef PLATFORM_MAC + // touch the application's bundle directory so that + // OS X' Launch Services notices any changes in the application's + // Info.plist file. + FileUtils::touch(m_installDir.c_str()); +#endif +} + +void UpdateInstaller::setAutoClose(bool autoClose) +{ + m_autoClose = autoClose; +} + diff --git a/mmc_updater/src/UpdateInstaller.h b/mmc_updater/src/UpdateInstaller.h new file mode 100644 index 00000000..1eca0bc7 --- /dev/null +++ b/mmc_updater/src/UpdateInstaller.h @@ -0,0 +1,72 @@ +#pragma once + +#include "Platform.h" +#include "FileUtils.h" +#include "UpdateScript.h" + +#include <list> +#include <string> +#include <map> + +class UpdateObserver; + +/** Central class responsible for installing updates, + * launching an elevated copy of the updater if required + * and restarting the main application once the update + * is installed. + */ +class UpdateInstaller +{ + public: + enum Mode + { + Setup, + Main + }; + + UpdateInstaller(); + void setInstallDir(const std::string& path); + void setPackageDir(const std::string& path); + void setBackupDir(const std::string& path); + void setMode(Mode mode); + void setScript(UpdateScript* script); + void setWaitPid(PLATFORM_PID pid); + void setForceElevated(bool elevated); + void setAutoClose(bool autoClose); + void setFinishCmd(const std::string& cmd); + + void setObserver(UpdateObserver* observer); + + void run() throw (); + + void restartMainApp(); + + private: + void cleanup(); + void revert(); + void removeBackups(); + bool checkAccess(); + + void installFiles(); + void uninstallFiles(); + void installFile(const UpdateScriptFile& file); + void backupFile(const std::string& path); + void reportError(const std::string& error); + void postInstallUpdate(); + + std::list<std::string> updaterArgs() const; + std::string friendlyErrorForError(const FileUtils::IOException& ex) const; + + Mode m_mode; + std::string m_installDir; + std::string m_packageDir; + std::string m_backupDir; + std::string m_finishCmd; + PLATFORM_PID m_waitPid; + UpdateScript* m_script; + UpdateObserver* m_observer; + std::map<std::string,std::string> m_backups; + bool m_forceElevated; + bool m_autoClose; +}; + diff --git a/mmc_updater/src/UpdateMessage.h b/mmc_updater/src/UpdateMessage.h new file mode 100644 index 00000000..fee51ab8 --- /dev/null +++ b/mmc_updater/src/UpdateMessage.h @@ -0,0 +1,42 @@ +#pragma once + +#include <string> + +/** UpdateMessage stores information for a message + * about the status of update installation sent + * between threads. + */ +class UpdateMessage +{ + public: + enum Type + { + UpdateFailed, + UpdateProgress, + UpdateFinished + }; + + UpdateMessage(void* receiver, Type type) + { + init(receiver,type); + } + + UpdateMessage(Type type) + { + init(0,type); + } + + void* receiver; + Type type; + std::string message; + int progress; + + private: + void init(void* receiver, Type type) + { + this->progress = 0; + this->receiver = receiver; + this->type = type; + } +}; + diff --git a/mmc_updater/src/UpdateObserver.h b/mmc_updater/src/UpdateObserver.h new file mode 100644 index 00000000..84d47325 --- /dev/null +++ b/mmc_updater/src/UpdateObserver.h @@ -0,0 +1,15 @@ +#pragma once + +#include <string> + +/** Base class for observers of update installation status. + * See UpdateInstaller::setObserver() + */ +class UpdateObserver +{ + public: + virtual void updateError(const std::string& errorMessage) = 0; + virtual void updateProgress(int percentage) = 0; + virtual void updateFinished() = 0; +}; + diff --git a/mmc_updater/src/UpdateScript.cpp b/mmc_updater/src/UpdateScript.cpp new file mode 100644 index 00000000..849a2217 --- /dev/null +++ b/mmc_updater/src/UpdateScript.cpp @@ -0,0 +1,99 @@ +#include "UpdateScript.h" + +#include "Log.h" +#include "StringUtils.h" + +#include "tinyxml/tinyxml.h" + +std::string elementText(const TiXmlElement* element) +{ + if (!element) + { + return std::string(); + } + return element->GetText(); +} + +UpdateScript::UpdateScript() +{ +} + +void UpdateScript::parse(const std::string& path) +{ + m_path.clear(); + + TiXmlDocument document(path); + if (document.LoadFile()) + { + m_path = path; + + LOG(Info,"Loaded script from " + path); + + const TiXmlElement* updateNode = document.RootElement(); + parseUpdate(updateNode); + } + else + { + LOG(Error,"Unable to load script " + path); + } +} + +bool UpdateScript::isValid() const +{ + return !m_path.empty(); +} + +void UpdateScript::parseUpdate(const TiXmlElement* updateNode) +{ + const TiXmlElement* installNode = updateNode->FirstChildElement("install"); + if (installNode) + { + const TiXmlElement* installFileNode = installNode->FirstChildElement("file"); + while (installFileNode) + { + m_filesToInstall.push_back(parseFile(installFileNode)); + installFileNode = installFileNode->NextSiblingElement("file"); + } + } + + const TiXmlElement* uninstallNode = updateNode->FirstChildElement("uninstall"); + if (uninstallNode) + { + const TiXmlElement* uninstallFileNode = uninstallNode->FirstChildElement("file"); + while (uninstallFileNode) + { + m_filesToUninstall.push_back(uninstallFileNode->GetText()); + uninstallFileNode = uninstallFileNode->NextSiblingElement("file"); + } + } +} + +UpdateScriptFile UpdateScript::parseFile(const TiXmlElement* element) +{ + UpdateScriptFile file; + // The name within the update files folder. + file.source = elementText(element->FirstChildElement("source")); + // The path to install to. + file.dest = elementText(element->FirstChildElement("dest")); + + std::string modeString = elementText(element->FirstChildElement("mode")); + sscanf(modeString.c_str(),"%i",&file.permissions); + + return file; +} + +const std::vector<UpdateScriptFile>& UpdateScript::filesToInstall() const +{ + return m_filesToInstall; +} + +const std::vector<std::string>& UpdateScript::filesToUninstall() const +{ + return m_filesToUninstall; +} + +const std::string UpdateScript::path() const +{ + return m_path; +} + diff --git a/mmc_updater/src/UpdateScript.h b/mmc_updater/src/UpdateScript.h new file mode 100644 index 00000000..c825e35d --- /dev/null +++ b/mmc_updater/src/UpdateScript.h @@ -0,0 +1,85 @@ +#pragma once + +#include <string> +#include <vector> + +class TiXmlElement; + +/** Represents a package containing one or more + * files for an update. + */ +class UpdateScriptPackage +{ + public: + UpdateScriptPackage() + : size(0) + {} + + std::string name; + std::string sha1; + std::string source; + int size; + + bool operator==(const UpdateScriptPackage& other) const + { + return name == other.name && + sha1 == other.sha1 && + source == other.source && + size == other.size; + } +}; + +/** Represents a file to be installed as part of an update. */ +class UpdateScriptFile +{ + public: + UpdateScriptFile() + : permissions(0) + {} + + /// Path to copy from. + std::string source; + /// The path to copy to. + std::string dest; + std::string linkTarget; + + /** The permissions for this file, specified + * using the standard Unix mode_t values. + */ + int permissions; + + bool operator==(const UpdateScriptFile& other) const + { + return source == other.source && + dest == other.dest && + permissions == other.permissions; + } +}; + +/** Stores information about the packages and files included + * in an update, parsed from an XML file. + */ +class UpdateScript +{ + public: + UpdateScript(); + + /** Initialize this UpdateScript with the script stored + * in the XML file at @p path. + */ + void parse(const std::string& path); + + bool isValid() const; + const std::string path() const; + const std::vector<UpdateScriptFile>& filesToInstall() const; + const std::vector<std::string>& filesToUninstall() const; + + private: + void parseUpdate(const TiXmlElement* element); + UpdateScriptFile parseFile(const TiXmlElement* element); + + std::string m_path; + std::vector<UpdateScriptFile> m_filesToInstall; + std::vector<std::string> m_filesToUninstall; +}; + diff --git a/mmc_updater/src/UpdaterOptions.cpp b/mmc_updater/src/UpdaterOptions.cpp new file mode 100644 index 00000000..ae34562d --- /dev/null +++ b/mmc_updater/src/UpdaterOptions.cpp @@ -0,0 +1,154 @@ +#include "UpdaterOptions.h" + +#include "Log.h" +#include "AnyOption/anyoption.h" +#include "FileUtils.h" +#include "Platform.h" +#include "StringUtils.h" + +#include <cstdlib> +#include <iostream> + +UpdaterOptions::UpdaterOptions() +: mode(UpdateInstaller::Setup) +, waitPid(0) +, showVersion(false) +, forceElevated(false) +, autoClose(false) +{ +} + +UpdateInstaller::Mode stringToMode(const std::string& modeStr) +{ + if (modeStr == "main") + { + return UpdateInstaller::Main; + } + else + { + if (!modeStr.empty()) + { + LOG(Error,"Unknown mode " + modeStr); + } + return UpdateInstaller::Setup; + } +} + +void UpdaterOptions::parseOldFormatArg(const std::string& arg, std::string* key, std::string* value) +{ + size_t pos = arg.find('='); + if (pos != std::string::npos) + { + *key = arg.substr(0,pos); + *value = arg.substr(pos+1); + } +} + +// this is a compatibility function to allow the updater binary +// to be involved by legacy versions of Mendeley Desktop +// which used a different syntax for the updater's command-line +// arguments +void UpdaterOptions::parseOldFormatArgs(int argc, char** argv) +{ + for (int i=0; i < argc; i++) + { + std::string key; + std::string value; + + parseOldFormatArg(argv[i],&key,&value); + + if (key == "CurrentDir") + { + // CurrentDir is the directory containing the main application + // binary. On Mac and Linux this differs from the root of + // the installation directory + +#ifdef PLATFORM_LINUX + // the main binary is in lib/mendeleydesktop/libexec, + // go up 3 levels + installDir = FileUtils::canonicalPath((value + "/../../../").c_str()); +#elif defined(PLATFORM_MAC) + // the main binary is in Contents/MacOS, + // go up 2 levels + installDir = FileUtils::canonicalPath((value + "/../../").c_str()); +#elif defined(PLATFORM_WINDOWS) + // the main binary is in the root of the install directory + installDir = value; +#endif + } + else if (key == "TempDir") + { + packageDir = value; + } + else if (key == "UpdateScriptFileName") + { + scriptPath = value; + } + else if (key == "AppFileName") + { + // TODO - Store app file name + } + else if (key == "PID") + { + waitPid = static_cast<PLATFORM_PID>(atoll(value.c_str())); + } + else if (key == "--main") + { + mode = UpdateInstaller::Main; + } + } +} + +void UpdaterOptions::parse(int argc, char** argv) +{ + AnyOption parser; + parser.setOption("install-dir"); + parser.setOption("package-dir"); + parser.setOption("finish-cmd"); + parser.setOption("script"); + parser.setOption("wait"); + parser.setOption("mode"); + parser.setFlag("version"); + parser.setFlag("force-elevated"); + parser.setFlag("auto-close"); + + parser.processCommandArgs(argc,argv); + + if (parser.getValue("mode")) + { + mode = stringToMode(parser.getValue("mode")); + } + if (parser.getValue("install-dir")) + { + installDir = parser.getValue("install-dir"); + } + if (parser.getValue("package-dir")) + { + packageDir = parser.getValue("package-dir"); + } + if (parser.getValue("script")) + { + scriptPath = parser.getValue("script"); + } + if (parser.getValue("wait")) + { + waitPid = static_cast<PLATFORM_PID>(atoll(parser.getValue("wait"))); + } + if (parser.getValue("finish-cmd")) + { + finishCmd = parser.getValue("finish-cmd"); + } + + showVersion = parser.getFlag("version"); + forceElevated = parser.getFlag("force-elevated"); + autoClose = parser.getFlag("auto-close"); + + if (installDir.empty()) + { + // if no --install-dir argument is present, try parsing + // the command-line arguments in the old format (which uses + // a list of 'Key=Value' args) + parseOldFormatArgs(argc,argv); + } +} + diff --git a/mmc_updater/src/UpdaterOptions.h b/mmc_updater/src/UpdaterOptions.h new file mode 100644 index 00000000..b4473a82 --- /dev/null +++ b/mmc_updater/src/UpdaterOptions.h @@ -0,0 +1,28 @@ +#pragma once + +#include "UpdateInstaller.h" + +/** Parses the command-line options to the updater binary. */ +class UpdaterOptions +{ + public: + UpdaterOptions(); + + void parse(int argc, char** argv); + + UpdateInstaller::Mode mode; + std::string installDir; + std::string packageDir; + std::string scriptPath; + std::string finishCmd; + PLATFORM_PID waitPid; + std::string logFile; + bool showVersion; + bool forceElevated; + bool autoClose; + + private: + void parseOldFormatArgs(int argc, char** argv); + static void parseOldFormatArg(const std::string& arg, std::string* key, std::string* value); +}; + diff --git a/mmc_updater/src/main.cpp b/mmc_updater/src/main.cpp new file mode 100644 index 00000000..fb072ab5 --- /dev/null +++ b/mmc_updater/src/main.cpp @@ -0,0 +1,203 @@ +#include "AppInfo.h" +#include "FileUtils.h" +#include "Log.h" +#include "Platform.h" +#include "ProcessUtils.h" +#include "StringUtils.h" +#include "UpdateScript.h" +#include "UpdaterOptions.h" + +#include <thread> + +#if defined(PLATFORM_LINUX) + #include "UpdateDialogGtkFactory.h" + #include "UpdateDialogAscii.h" +#endif + +#if defined(PLATFORM_MAC) + #include "MacBundle.h" + #include "UpdateDialogCocoa.h" +#endif + +#if defined(PLATFORM_WINDOWS) + #include "UpdateDialogWin32.h" +#endif + +#include <iostream> +#include <memory> + +#define UPDATER_VERSION "0.16" + +UpdateDialog* createUpdateDialog(); + +void runUpdaterThread(void* arg) +{ +#ifdef PLATFORM_MAC + // create an autorelease pool to free any temporary objects + // created by Cocoa whilst handling notifications from the UpdateInstaller + void* pool = UpdateDialogCocoa::createAutoreleasePool(); +#endif + + try + { + UpdateInstaller* installer = static_cast<UpdateInstaller*>(arg); + installer->run(); + } + catch (const std::exception& ex) + { + LOG(Error,"Unexpected exception " + std::string(ex.what())); + } + +#ifdef PLATFORM_MAC + UpdateDialogCocoa::releaseAutoreleasePool(pool); +#endif +} + +#ifdef PLATFORM_MAC +extern unsigned char Info_plist[]; +extern unsigned int Info_plist_len; + +extern unsigned char mac_icns[]; +extern unsigned int mac_icns_len; + +bool unpackBundle(int argc, char** argv) +{ + MacBundle bundle(FileUtils::tempPath(),AppInfo::name()); + std::string currentExePath = ProcessUtils::currentProcessPath(); + + if (currentExePath.find(bundle.bundlePath()) != std::string::npos) + { + // already running from a bundle + return false; + } + LOG(Info,"Creating bundle " + bundle.bundlePath()); + + // create a Mac app bundle + std::string plistContent(reinterpret_cast<const char*>(Info_plist),Info_plist_len); + std::string iconContent(reinterpret_cast<const char*>(mac_icns),mac_icns_len); + bundle.create(plistContent,iconContent,ProcessUtils::currentProcessPath()); + + std::list<std::string> args; + for (int i = 1; i < argc; i++) + { + args.push_back(argv[i]); + } + ProcessUtils::runSync(bundle.executablePath(),args); + return true; +} +#endif + +void setupConsole() +{ +#ifdef PLATFORM_WINDOWS + // see http://stackoverflow.com/questions/587767/how-to-output-to-console-in-c-windows + // and http://www.libsdl.org/cgi/docwiki.cgi/FAQ_Console + AttachConsole(ATTACH_PARENT_PROCESS); + freopen( "CON", "w", stdout ); + freopen( "CON", "w", stderr ); +#endif +} + +int main(int argc, char** argv) +{ +#ifdef PLATFORM_MAC + void* pool = UpdateDialogCocoa::createAutoreleasePool(); +#endif + + Log::instance()->open(AppInfo::logFilePath()); + +#ifdef PLATFORM_MAC + // when the updater is run for the first time, create a Mac app bundle + // and re-launch the application from the bundle. This permits + // setting up bundle properties (such as application icon) + if (unpackBundle(argc,argv)) + { + return 0; + } +#endif + + UpdaterOptions options; + options.parse(argc,argv); + if (options.showVersion) + { + setupConsole(); + std::cout << "Update installer version " << UPDATER_VERSION << std::endl; + return 0; + } + + UpdateInstaller installer; + UpdateScript script; + + if (!options.scriptPath.empty()) + { + script.parse(FileUtils::makeAbsolute(options.scriptPath.c_str(),options.packageDir.c_str())); + } + + LOG(Info,"started updater. install-dir: " + options.installDir + + ", package-dir: " + options.packageDir + + ", wait-pid: " + intToStr(options.waitPid) + + ", script-path: " + options.scriptPath + + ", mode: " + intToStr(options.mode) + + ", finish-cmd: " + options.finishCmd); + + installer.setMode(options.mode); + installer.setInstallDir(options.installDir); + installer.setPackageDir(options.packageDir); + installer.setScript(&script); + installer.setWaitPid(options.waitPid); + installer.setForceElevated(options.forceElevated); + installer.setAutoClose(options.autoClose); + installer.setFinishCmd(options.finishCmd); + + if (options.mode == UpdateInstaller::Main) + { + LOG(Info, "Showing updater UI - auto close? " + intToStr(options.autoClose)); + std::auto_ptr<UpdateDialog> dialog(createUpdateDialog()); + dialog->setAutoClose(options.autoClose); + dialog->init(argc, argv); + installer.setObserver(dialog.get()); + std::thread updaterThread(runUpdaterThread, &installer); + dialog->exec(); + updaterThread.join(); + } + else + { + installer.run(); + } + +#ifdef PLATFORM_MAC + UpdateDialogCocoa::releaseAutoreleasePool(pool); +#endif + + return 0; +} + +UpdateDialog* createUpdateDialog() +{ +#if defined(PLATFORM_WINDOWS) + return new UpdateDialogWin32(); +#elif defined(PLATFORM_MAC) + return new UpdateDialogCocoa(); +#elif defined(PLATFORM_LINUX) + UpdateDialog* dialog = UpdateDialogGtkFactory::createDialog(); + if (!dialog) + { + dialog = new UpdateDialogAscii(); + } + return dialog; +#endif +} + +#ifdef PLATFORM_WINDOWS +// application entry point under Windows +int CALLBACK WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + int argc = 0; + char** argv; + ProcessUtils::convertWindowsCommandLine(GetCommandLineW(),argc,argv); + return main(argc,argv); +} +#endif diff --git a/mmc_updater/src/resources/Info.plist b/mmc_updater/src/resources/Info.plist new file mode 100644 index 00000000..93e97ccd --- /dev/null +++ b/mmc_updater/src/resources/Info.plist @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <!-- Note - The name of the application specified here must match the value + returned by AppInfo::name() + !--> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>MultiMC Updater</string> + <key>CFBundleIconFile</key> + <string>MultiMC Updater.icns</string> + <key>CFBundleIdentifier</key> + <string>org.multimc.MultiMCUpdater</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>1.0</string> + <key>NSMainNibFile</key> + <string>MainMenu</string> + <key>NSPrincipalClass</key> + <string>NSApplication</string> + <key>LSMinimumSystemVersion</key> + <string>10.5</string> + <key>LSMinimumSystemVersionByArchitecture</key> + <dict> + <key>i386</key> + <string>10.5.0</string> + <key>x86_64</key> + <string>10.5.0</string> + </dict> +</dict> +</plist> diff --git a/mmc_updater/src/resources/icon128.png b/mmc_updater/src/resources/icon128.png Binary files differnew file mode 100644 index 00000000..324452aa --- /dev/null +++ b/mmc_updater/src/resources/icon128.png diff --git a/mmc_updater/src/resources/icon64.png b/mmc_updater/src/resources/icon64.png Binary files differnew file mode 100644 index 00000000..5e3373e2 --- /dev/null +++ b/mmc_updater/src/resources/icon64.png diff --git a/mmc_updater/src/resources/mac.icns b/mmc_updater/src/resources/mac.icns Binary files differnew file mode 100644 index 00000000..7c8fa2ef --- /dev/null +++ b/mmc_updater/src/resources/mac.icns diff --git a/mmc_updater/src/resources/updater.ico b/mmc_updater/src/resources/updater.ico Binary files differnew file mode 100644 index 00000000..b011bac9 --- /dev/null +++ b/mmc_updater/src/resources/updater.ico diff --git a/mmc_updater/src/resources/updater.rc b/mmc_updater/src/resources/updater.rc new file mode 100644 index 00000000..550970a8 --- /dev/null +++ b/mmc_updater/src/resources/updater.rc @@ -0,0 +1,30 @@ +IDI_APPICON ICON DISCARDABLE "updater.ico" + +1 VERSIONINFO +FILEVERSION 0,0,1,0 +PRODUCTVERSION 1,0,1,0 +FILEFLAGSMASK 0X3FL +FILEFLAGS 0X8L +FILEOS 0X40004L +FILETYPE 0X1 +FILESUBTYPE 0 +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "FileVersion", "0.0.1.0" + VALUE "ProductVersion", "1.0.1.0" + VALUE "OriginalFilename", "updater.exe" + VALUE "InternalName", "updater.exe" + VALUE "FileDescription", "Software Update Tool" + VALUE "CompanyName", "MultiMC Contributors" + VALUE "ProductName", "MultiMC Software Updater" + VALUE "PrivateBuild", "Built by BuildBot" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0000, 0x04b0 + END +END
\ No newline at end of file diff --git a/mmc_updater/src/tests/CMakeLists.txt b/mmc_updater/src/tests/CMakeLists.txt new file mode 100644 index 00000000..2af9b9c0 --- /dev/null +++ b/mmc_updater/src/tests/CMakeLists.txt @@ -0,0 +1,51 @@ + +include_directories("${CMAKE_CURRENT_SOURCE_DIR}/..") + +if (APPLE) + set(HELPER_SHARED_SOURCES ../StlSymbolsLeopard.cpp) +endif() + +# Create helper binaries for unit tests +add_executable(oldapp + old_app.cpp + ${HELPER_SHARED_SOURCES} +) +add_executable(newapp + new_app.cpp + ${HELPER_SHARED_SOURCES} +) + +# Install data files required by unit tests +set(TEST_FILES + file_list.xml + v2_file_list.xml + test-update.rb +) + +foreach(TEST_FILE ${TEST_FILES}) + execute_process( + COMMAND + "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${TEST_FILE}" "${CMAKE_CURRENT_BINARY_DIR}" + ) +endforeach() + +# Add unit test binaries +macro(ADD_UPDATER_TEST CLASS) + set(TEST_TARGET updater_${CLASS}) + add_executable(${TEST_TARGET} ${CLASS}.cpp) + target_link_libraries(${TEST_TARGET} updatershared) + add_test(${TEST_TARGET} ${TEST_TARGET}) + if (APPLE) + set_target_properties(${TEST_TARGET} PROPERTIES LINK_FLAGS "-framework Security -framework Cocoa") + endif() +endmacro() + +add_updater_test(TestUpdateScript) +add_updater_test(TestUpdaterOptions) +add_updater_test(TestFileUtils) + +# Add updater that that performs a complete update install +# and checks the result +find_program(RUBY_BIN ruby) +add_test(updater_TestUpdateInstall ${RUBY_BIN} test-update.rb) + diff --git a/mmc_updater/src/tests/TestFileUtils.cpp b/mmc_updater/src/tests/TestFileUtils.cpp new file mode 100644 index 00000000..709acc5c --- /dev/null +++ b/mmc_updater/src/tests/TestFileUtils.cpp @@ -0,0 +1,50 @@ +#include "TestFileUtils.h" + +#include "FileUtils.h" +#include "TestUtils.h" + +void TestFileUtils::testDirName() +{ +#ifdef PLATFORM_WINDOWS + std::string dirName = FileUtils::dirname("E:/Some Dir/App.exe"); + TEST_COMPARE(dirName,"E:/Some Dir/"); +#endif +} + +void TestFileUtils::testIsRelative() +{ +#ifdef PLATFORM_WINDOWS + TEST_COMPARE(FileUtils::isRelative("temp"),true); + TEST_COMPARE(FileUtils::isRelative("D:/temp"),false); + TEST_COMPARE(FileUtils::isRelative("d:/temp"),false); +#else + TEST_COMPARE(FileUtils::isRelative("/tmp"),false); + TEST_COMPARE(FileUtils::isRelative("tmp"),true); +#endif +} + +void TestFileUtils::testSymlinkFileExists() +{ +#ifdef PLATFORM_UNIX + const char* linkName = "link-name"; + FileUtils::removeFile(linkName); + FileUtils::createSymLink(linkName, "target-that-does-not-exist"); + TEST_COMPARE(FileUtils::fileExists(linkName), true); +#endif +} + +void TestFileUtils::testStandardDirs() +{ + std::string tmpDir = FileUtils::tempPath(); + TEST_COMPARE(FileUtils::fileExists(tmpDir.data()), true); +} + +int main(int,char**) +{ + TestList<TestFileUtils> tests; + tests.addTest(&TestFileUtils::testDirName); + tests.addTest(&TestFileUtils::testIsRelative); + tests.addTest(&TestFileUtils::testSymlinkFileExists); + tests.addTest(&TestFileUtils::testStandardDirs); + return TestUtils::runTest(tests); +} diff --git a/mmc_updater/src/tests/TestFileUtils.h b/mmc_updater/src/tests/TestFileUtils.h new file mode 100644 index 00000000..1a45164b --- /dev/null +++ b/mmc_updater/src/tests/TestFileUtils.h @@ -0,0 +1,10 @@ +#pragma once + +class TestFileUtils +{ + public: + void testDirName(); + void testIsRelative(); + void testSymlinkFileExists(); + void testStandardDirs(); +}; diff --git a/mmc_updater/src/tests/TestUpdateScript.cpp b/mmc_updater/src/tests/TestUpdateScript.cpp new file mode 100644 index 00000000..30a7572a --- /dev/null +++ b/mmc_updater/src/tests/TestUpdateScript.cpp @@ -0,0 +1,27 @@ +#include "TestUpdateScript.h" + +#include "TestUtils.h" +#include "UpdateScript.h" + +#include <iostream> +#include <algorithm> + +void TestUpdateScript::testV2Script() +{ + UpdateScript newFormat; + UpdateScript oldFormat; + + newFormat.parse("file_list.xml"); + oldFormat.parse("v2_file_list.xml"); + + TEST_COMPARE(newFormat.filesToInstall(),oldFormat.filesToInstall()); + TEST_COMPARE(newFormat.filesToUninstall(),oldFormat.filesToUninstall()); +} + +int main(int,char**) +{ + TestList<TestUpdateScript> tests; + tests.addTest(&TestUpdateScript::testV2Script); + return TestUtils::runTest(tests); +} + diff --git a/mmc_updater/src/tests/TestUpdateScript.h b/mmc_updater/src/tests/TestUpdateScript.h new file mode 100644 index 00000000..513513d5 --- /dev/null +++ b/mmc_updater/src/tests/TestUpdateScript.h @@ -0,0 +1,8 @@ +#pragma once + +class TestUpdateScript +{ + public: + void testV2Script(); +}; + diff --git a/mmc_updater/src/tests/TestUpdaterOptions.cpp b/mmc_updater/src/tests/TestUpdaterOptions.cpp new file mode 100644 index 00000000..a4cb7d33 --- /dev/null +++ b/mmc_updater/src/tests/TestUpdaterOptions.cpp @@ -0,0 +1,68 @@ +#include "TestUpdaterOptions.h" + +#include "FileUtils.h" +#include "Platform.h" +#include "TestUtils.h" +#include "UpdaterOptions.h" + +#include <string.h> +#include <stdlib.h> + +void TestUpdaterOptions::testOldFormatArgs() +{ + const int argc = 6; + char* argv[argc]; + argv[0] = strdup("updater"); + + std::string currentDir("CurrentDir="); + const char* appDir = 0; + + // CurrentDir is the path to the directory containing the main + // Mendeley Desktop binary, on Linux and Mac this differs from + // the root of the install directory +#ifdef PLATFORM_LINUX + appDir = "/tmp/path-to-app/lib/mendeleydesktop/libexec/"; + FileUtils::mkpath(appDir); +#elif defined(PLATFORM_MAC) + appDir = "/tmp/path-to-app/Contents/MacOS/"; + FileUtils::mkpath(appDir); +#elif defined(PLATFORM_WINDOWS) + appDir = "C:/path/to/app/"; +#endif + currentDir += appDir; + + argv[1] = strdup(currentDir.c_str()); + argv[2] = strdup("TempDir=/tmp/updater"); + argv[3] = strdup("UpdateScriptFileName=/tmp/updater/file_list.xml"); + argv[4] = strdup("AppFileName=/path/to/app/theapp"); + argv[5] = strdup("PID=123456"); + + UpdaterOptions options; + options.parse(argc,argv); + + TEST_COMPARE(options.mode,UpdateInstaller::Setup); +#ifdef PLATFORM_LINUX + TEST_COMPARE(options.installDir,"/tmp/path-to-app"); +#elif defined(PLATFORM_MAC) + // /tmp is a symlink to /private/tmp on Mac + TEST_COMPARE(options.installDir,"/private/tmp/path-to-app"); +#else + TEST_COMPARE(options.installDir,"C:/path/to/app/"); +#endif + TEST_COMPARE(options.packageDir,"/tmp/updater"); + TEST_COMPARE(options.scriptPath,"/tmp/updater/file_list.xml"); + TEST_COMPARE(options.waitPid,123456); + + for (int i=0; i < argc; i++) + { + free(argv[i]); + } +} + +int main(int,char**) +{ + TestList<TestUpdaterOptions> tests; + tests.addTest(&TestUpdaterOptions::testOldFormatArgs); + return TestUtils::runTest(tests); +} + diff --git a/mmc_updater/src/tests/TestUpdaterOptions.h b/mmc_updater/src/tests/TestUpdaterOptions.h new file mode 100644 index 00000000..5ed102c1 --- /dev/null +++ b/mmc_updater/src/tests/TestUpdaterOptions.h @@ -0,0 +1,8 @@ +#pragma once + +class TestUpdaterOptions +{ + public: + void testOldFormatArgs(); +}; + diff --git a/mmc_updater/src/tests/TestUtils.h b/mmc_updater/src/tests/TestUtils.h new file mode 100644 index 00000000..68d97da5 --- /dev/null +++ b/mmc_updater/src/tests/TestUtils.h @@ -0,0 +1,108 @@ +#pragma once + +#include <iostream> +#include <functional> +#include <string> +#include <vector> + +template <class T> +class TestList +{ + public: + void addTest(void (T::*test)()) + { + m_tests.push_back(std::mem_fun(test)); + } + + int size() const + { + return m_tests.size(); + } + + void runTest(T* testInstance, int i) + { + m_tests.at(i)(testInstance); + } + + private: + std::vector<std::mem_fun_t<void,T> > m_tests; +}; + +class TestUtils +{ + public: + template <class X, class Y> + static void compare(const X& x, const Y& y, const char* xString, const char* yString) + { + if (x != y) + { + throw "Actual and expected values differ. " + "Actual: " + toString(x,xString) + + " Expected: " + toString(y,yString); + } + } + + template <typename T> + static std::string toString(T value, const char* context) + { + return "Unprintable: " + std::string(context); + } + + template <class T> + static int runTest(class TestList<T>& tests) throw () + { + std::string errorText; + try + { + T testInstance; + for (int i=0; i < tests.size(); i++) + { + tests.runTest(&testInstance,i); + } + } + catch (const std::exception& ex) + { + errorText = ex.what(); + } + catch (const std::string& error) + { + errorText = error; + } + catch (...) + { + errorText = "Unknown exception"; + } + + if (errorText.empty()) + { + std::cout << "Test passed" << std::endl; + return 0; + } + else + { + std::cout << "Test failed: " << errorText << std::endl; + return 1; + } + } +}; + +template <> +inline std::string TestUtils::toString(const std::string& value, const char*) +{ + return value; +} +template <> +inline std::string TestUtils::toString(std::string value, const char*) +{ + return value; +} +template <> +inline std::string TestUtils::toString(const char* value, const char*) +{ + return value; +} + +#define TEST_COMPARE(x,y) \ + TestUtils::compare(x,y,#x,#y); + + diff --git a/mmc_updater/src/tests/file_list.xml b/mmc_updater/src/tests/file_list.xml new file mode 100644 index 00000000..dff4b54f --- /dev/null +++ b/mmc_updater/src/tests/file_list.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<update version="3"> + <targetVersion>2.0</targetVersion> + <platform>Test</platform> + <dependencies> + <!-- The new updater is standalone and has no dependencies, + except for standard system libraries. + !--> + </dependencies> + <packages> + <package> + <name>app-pkg</name> + <hash>$APP_PACKAGE_HASH</hash> + <size>$APP_PACKAGE_SIZE</size> + <source>http://some/dummy/URL</source> + </package> + </packages> + <install> + <file> + <name>$APP_FILENAME</name> + <hash>$UPDATED_APP_HASH</hash> + <size>$UPDATED_APP_SIZE</size> + <permissions>0755</permissions> + <package>app-pkg</package> + <is-main-binary>true</is-main-binary> + </file> + <file> + <name>$UPDATER_FILENAME</name> + <hash>$UPDATER_HASH</hash> + <size>$UPDATER_SIZE</size> + <permissions>0755</permissions> + </file> + <!-- Test symlink !--> + <file> + <name>test-dir/app-symlink</name> + <target>../app</target> + </file> + <!-- Test file in new directory !--> + <file> + <name>new-dir/new-dir2/new-file.txt</name> + <hash>$TEST_FILENAME</hash> + <size>$TEST_SIZE</size> + <package>app-pkg</package> + <permissions>0644</permissions> + </file> + </install> + <uninstall> + <!-- TODO - List some files to uninstall here !--> + <file>file-to-uninstall.txt</file> + <file>symlink-to-file-to-uninstall.txt</file> + </uninstall> +</update> diff --git a/mmc_updater/src/tests/new_app.cpp b/mmc_updater/src/tests/new_app.cpp new file mode 100644 index 00000000..7fad1380 --- /dev/null +++ b/mmc_updater/src/tests/new_app.cpp @@ -0,0 +1,8 @@ +#include <iostream> + +int main(int,char**) +{ + std::cout << "new app starting" << std::endl; + return 0; +} + diff --git a/mmc_updater/src/tests/old_app.cpp b/mmc_updater/src/tests/old_app.cpp new file mode 100644 index 00000000..476a3405 --- /dev/null +++ b/mmc_updater/src/tests/old_app.cpp @@ -0,0 +1,7 @@ +#include <iostream> + +int main(int,char**) +{ + std::cout << "old app starting" << std::endl; + return 0; +} diff --git a/mmc_updater/src/tests/test-update.rb b/mmc_updater/src/tests/test-update.rb new file mode 100755 index 00000000..82965cf4 --- /dev/null +++ b/mmc_updater/src/tests/test-update.rb @@ -0,0 +1,218 @@ +#!/usr/bin/ruby + +require 'fileutils.rb' +require 'find' +require 'rbconfig' +require 'optparse' + +# Install directory - this contains a space to check +# for correct escaping of paths when passing comamnd +# line arguments under Windows +INSTALL_DIR = File.expand_path("install dir/") +PACKAGE_DIR = File.expand_path("package-dir/") +PACKAGE_SRC_DIR = File.expand_path("package-src-dir/") +IS_WINDOWS = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ + +if IS_WINDOWS + OLDAPP_NAME = "oldapp.exe" + NEWAPP_NAME = "newapp.exe" + APP_NAME = "app.exe" + UPDATER_NAME = "updater.exe" + ZIP_TOOL = File.expand_path("../zip-tool.exe") +else + OLDAPP_NAME = "oldapp" + NEWAPP_NAME = "newapp" + APP_NAME = "app" + UPDATER_NAME = "updater" + ZIP_TOOL = File.expand_path("../zip-tool") +end + +file_list_vars = { + "APP_FILENAME" => APP_NAME, + "UPDATER_FILENAME" => UPDATER_NAME +} + +def replace_vars(src_file,dest_file,vars) + content = File.read(src_file) + vars.each do |key,value| + content.gsub! "$#{key}",value + end + File.open(dest_file,'w') do |file| + file.print content + end +end + +# Returns true if |src_file| and |dest_file| have the same contents, type +# and permissions or false otherwise +def compare_files(src_file, dest_file) + if File.ftype(src_file) != File.ftype(dest_file) + $stderr.puts "Type of file #{src_file} and #{dest_file} differ" + return false + end + + if File.file?(src_file) && !FileUtils.identical?(src_file, dest_file) + $stderr.puts "Contents of file #{src_file} and #{dest_file} differ" + return false + end + + src_stat = File.stat(src_file) + dest_stat = File.stat(dest_file) + + if src_stat.mode != dest_stat.mode + $stderr.puts "Permissions of #{src_file} and #{dest_file} differ" + return false + end + + return true +end + +# Compares the contents of two directories and returns a map of (file path => change type) +# for files and directories which differ between the two +def compare_dirs(src_dir, dest_dir) + src_dir += '/' if !src_dir.end_with?('/') + dest_dir += '/' if !dest_dir.end_with?('/') + + src_file_map = {} + Find.find(src_dir) do |src_file| + src_file = src_file[src_dir.length..-1] + src_file_map[src_file] = nil + end + + change_map = {} + Find.find(dest_dir) do |dest_file| + dest_file = dest_file[dest_dir.length..-1] + + if !src_file_map.include?(dest_file) + change_map[dest_file] = :deleted + elsif !compare_files("#{src_dir}/#{dest_file}", "#{dest_dir}/#{dest_file}") + change_map[dest_file] = :updated + end + + src_file_map.delete(dest_file) + end + + src_file_map.each do |file| + change_map[file] = :added + end + + return change_map +end + +def create_test_file(name, content) + File.open(name, 'w') do |file| + file.puts content + end + return name +end + +force_elevation = false +run_in_debugger = false + +OptionParser.new do |parser| + parser.on("-f","--force-elevated","Force the updater to elevate itself") do + force_elevation = true + end + parser.on("-d","--debug","Run the updater under GDB") do + run_in_debugger = true + end +end.parse! + +# copy 'src' to 'dest', preserving the attributes +# of 'src' +def copy_file(src, dest) + FileUtils.cp src, dest, :preserve => true +end + +# Remove the install and package dirs if they +# already exist +FileUtils.rm_rf(INSTALL_DIR) +FileUtils.rm_rf(PACKAGE_DIR) +FileUtils.rm_rf(PACKAGE_SRC_DIR) + +# Create the install directory with the old app +Dir.mkdir(INSTALL_DIR) +copy_file OLDAPP_NAME, "#{INSTALL_DIR}/#{APP_NAME}" + +# Create a dummy file to uninstall +uninstall_test_file = create_test_file("#{INSTALL_DIR}/file-to-uninstall.txt", "this file should be removed after the update") +uninstall_test_symlink = if not IS_WINDOWS + FileUtils.ln_s("#{INSTALL_DIR}/file-to-uninstall.txt", "#{INSTALL_DIR}/symlink-to-file-to-uninstall.txt") +else + create_test_file("#{INSTALL_DIR}/symlink-to-file-to-uninstall.txt", "dummy file. this is a symlink on Unix") +end + +# Populate package source dir with files to install +Dir.mkdir(PACKAGE_SRC_DIR) +nested_dir_path = "#{PACKAGE_SRC_DIR}/new-dir/new-dir2" +FileUtils.mkdir_p(nested_dir_path) +FileUtils::chmod 0755, "#{PACKAGE_SRC_DIR}/new-dir" +FileUtils::chmod 0755, "#{PACKAGE_SRC_DIR}/new-dir/new-dir2" +nested_dir_test_file = "#{nested_dir_path}/new-file.txt" +File.open(nested_dir_test_file,'w') do |file| + file.puts "this is a new file in a new nested dir" +end +FileUtils::chmod 0644, nested_dir_test_file +copy_file NEWAPP_NAME, "#{PACKAGE_SRC_DIR}/#{APP_NAME}" +FileUtils::chmod 0755, "#{PACKAGE_SRC_DIR}/#{APP_NAME}" + +# Create .zip packages from source files +Dir.mkdir(PACKAGE_DIR) +Dir.chdir(PACKAGE_SRC_DIR) do + if !system("#{ZIP_TOOL} #{PACKAGE_DIR}/app-pkg.zip .") + raise "Unable to create update package" + end +end + +# Copy the install script and updater to the target +# directory +replace_vars("file_list.xml","#{PACKAGE_DIR}/file_list.xml",file_list_vars) +copy_file "../#{UPDATER_NAME}", "#{PACKAGE_DIR}/#{UPDATER_NAME}" + +# Run the updater using the new syntax +# +# Run the application from the install directory to +# make sure that it looks in the correct directory for +# the file_list.xml file and packages +# +install_path = File.expand_path(INSTALL_DIR) +Dir.chdir(INSTALL_DIR) do + flags = "--force-elevated" if force_elevation + debug_flags = "gdb --args" if run_in_debugger + cmd = "#{debug_flags} #{PACKAGE_DIR}/#{UPDATER_NAME} #{flags} --install-dir \"#{install_path}\" --package-dir \"#{PACKAGE_DIR}\" --script file_list.xml --auto-close" + puts "Running '#{cmd}'" + system(cmd) +end + +# TODO - Correctly wait until updater has finished +sleep(1) + +# Check that the app was updated +app_path = "#{INSTALL_DIR}/#{APP_NAME}" +output = `"#{app_path}"` +if (output.strip != "new app starting") + throw "Updated app produced unexpected output: #{output}" +end + +# Check that the packaged dir and install dir match +dir_diff = compare_dirs(PACKAGE_SRC_DIR, INSTALL_DIR) +ignored_files = ["test-dir", "test-dir/app-symlink", UPDATER_NAME] +have_unexpected_change = false +dir_diff.each do |path, change_type| + if !ignored_files.include?(path) + case change_type + when :added + $stderr.puts "File #{path} was not installed" + when :changed + $stderr.puts "File #{path} differs between install and package dir" + when :deleted + $stderr.puts "File #{path} was not uninstalled" + end + have_unexpected_change = true + end +end + +if have_unexpected_change + throw "Unexpected differences between packaging and update dir" +end + +puts "Test passed" diff --git a/mmc_updater/src/tests/v2_file_list.xml b/mmc_updater/src/tests/v2_file_list.xml new file mode 100644 index 00000000..202e5bbe --- /dev/null +++ b/mmc_updater/src/tests/v2_file_list.xml @@ -0,0 +1,67 @@ +<?xml version="1.0"?> + +<!-- The v2-compatible attribute lets the update script parser + know that it is dealing with a script structured for backwards + compatibility with the MD <= 1.0 updater. +!--> +<update version="3" v2-compatible="true"> + <targetVersion>2.0</targetVersion> + <platform>Test</platform> + <dependencies> + <!-- The new updater is standalone and has no dependencies, + except for standard system libraries and itself. + !--> + </dependencies> + <packages> + <package> + <name>app-pkg</name> + <hash>$APP_PACKAGE_HASH</hash> + <size>$APP_PACKAGE_SIZE</size> + <source>http://some/dummy/URL</source> + </package> + </packages> + + <!-- For compatibility with the update download in MD <= 1.0, + an <install> section lists the packages to download and + the real list of files to install is in the <install-v3> + section. !--> + <install> + <!-- A duplicate of the <packages> section should appear here, + except that each package is listed using the same structure + as files in the install-v3/files section. + !--> + </install> + <install-v3> + <file> + <name>$APP_FILENAME</name> + <hash>$UPDATED_APP_HASH</hash> + <size>$UPDATED_APP_SIZE</size> + <permissions>0755</permissions> + <package>app-pkg</package> + <is-main-binary>true</is-main-binary> + </file> + <file> + <name>$UPDATER_FILENAME</name> + <hash>$UPDATER_HASH</hash> + <size>$UPDATER_SIZE</size> + <permissions>0755</permissions> + </file> + <!-- Test symlink !--> + <file> + <name>test-dir/app-symlink</name> + <target>../app</target> + </file> + <file> + <name>new-dir/new-dir2/new-file.txt</name> + <hash>$TEST_FILENAME</hash> + <size>$TEST_SIZE</size> + <package>app-pkg</package> + <permissions>0644</permissions> + </file> + </install-v3> + <uninstall> + <!-- TODO - List some files to uninstall here !--> + <file>file-to-uninstall.txt</file> + <file>symlink-to-file-to-uninstall.txt</file> + </uninstall> +</update> diff --git a/package/linux/MultiMC b/package/linux/MultiMC index a2ef0c81..8229b24f 100755 --- a/package/linux/MultiMC +++ b/package/linux/MultiMC @@ -1,4 +1,4 @@ -#!/bin/bash +#!/bin/sh # Basic start script for running MultiMC with the libs packaged with it. MMC_DIR=`dirname "$0"` @@ -10,9 +10,24 @@ export LD_LIBRARY_PATH="${MMC_DIR}/bin":$LD_LIBRARY_PATH export QT_PLUGIN_PATH="${MMC_DIR}/plugins" export QT_FONTPATH="${MMC_DIR}/fonts" -# Just to be sure... -chmod +x "${MMC_DIR}/bin/MultiMC" +# Detect missing dependencies... +DEPS_LIST=`ldd "${MMC_DIR}"/plugins/*/*.so | grep "not found" | awk -vORS=", " '{ print $1 }'` +if [ -z $DEPS_LIST ]; then + # We have all our dependencies. Run MultiMC. + echo "No missing dependencies found." -# run MultiMC -"${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" $@ + # Just to be sure... + chmod +x "${MMC_DIR}/bin/MultiMC" + + # Run MultiMC + "${MMC_DIR}/bin/MultiMC" -d "${MMC_DIR}" $@ + + # Exit with MultiMC's exit code. + exit $? +else + echo "Error: MultiMC is missing the following libraries that it needs to work correctly:" + echo "\t${DEPS_LIST}" + echo "Please install them from your distribution's package manager." + exit 1 +fi |