diff options
Diffstat (limited to 'Source/QtDialog')
38 files changed, 6006 insertions, 0 deletions
diff --git a/Source/QtDialog/AddCacheEntry.cxx b/Source/QtDialog/AddCacheEntry.cxx new file mode 100644 index 0000000..daf4bd1 --- /dev/null +++ b/Source/QtDialog/AddCacheEntry.cxx @@ -0,0 +1,99 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "AddCacheEntry.h" + +#include <QCompleter> +#include <QMetaProperty> + +static const int NumTypes = 4; +static const int DefaultTypeIndex = 0; +static const QByteArray TypeStrings[NumTypes] = { "BOOL", "PATH", "FILEPATH", + "STRING" }; +static const QCMakeProperty::PropertyType Types[NumTypes] = { + QCMakeProperty::BOOL, QCMakeProperty::PATH, QCMakeProperty::FILEPATH, + QCMakeProperty::STRING +}; + +AddCacheEntry::AddCacheEntry(QWidget* p, const QStringList& varNames, + const QStringList& varTypes) + : QWidget(p) + , VarNames(varNames) + , VarTypes(varTypes) +{ + this->setupUi(this); + for (int i = 0; i < NumTypes; i++) { + this->Type->addItem(TypeStrings[i]); + } + QWidget* cb = new QCheckBox(); + QWidget* path = new QCMakePathEditor(); + QWidget* filepath = new QCMakeFilePathEditor(); + QWidget* string = new QLineEdit(); + this->StackedWidget->addWidget(cb); + this->StackedWidget->addWidget(path); + this->StackedWidget->addWidget(filepath); + this->StackedWidget->addWidget(string); + this->setTabOrder(this->Name, this->Type); + this->setTabOrder(this->Type, cb); + this->setTabOrder(cb, path); + this->setTabOrder(path, filepath); + this->setTabOrder(filepath, string); + this->setTabOrder(string, this->Description); + QCompleter* completer = new QCompleter(this->VarNames, this); + this->Name->setCompleter(completer); + connect(completer, SIGNAL(activated(const QString&)), this, + SLOT(onCompletionActivated(const QString&))); +} + +QString AddCacheEntry::name() const +{ + return this->Name->text(); +} + +QVariant AddCacheEntry::value() const +{ + QWidget* w = this->StackedWidget->currentWidget(); + if (qobject_cast<QLineEdit*>(w)) { + return static_cast<QLineEdit*>(w)->text(); + } + if (qobject_cast<QCheckBox*>(w)) { + return static_cast<QCheckBox*>(w)->isChecked(); + } + return QVariant(); +} + +QString AddCacheEntry::description() const +{ + return this->Description->text(); +} + +QCMakeProperty::PropertyType AddCacheEntry::type() const +{ + int idx = this->Type->currentIndex(); + if (idx >= 0 && idx < NumTypes) { + return Types[idx]; + } + return Types[DefaultTypeIndex]; +} + +QString AddCacheEntry::typeString() const +{ + int idx = this->Type->currentIndex(); + if (idx >= 0 && idx < NumTypes) { + return TypeStrings[idx]; + } + return TypeStrings[DefaultTypeIndex]; +} + +void AddCacheEntry::onCompletionActivated(const QString& text) +{ + int idx = this->VarNames.indexOf(text); + if (idx != -1) { + QString vartype = this->VarTypes[idx]; + for (int i = 0; i < NumTypes; i++) { + if (TypeStrings[i] == vartype) { + this->Type->setCurrentIndex(i); + break; + } + } + } +} diff --git a/Source/QtDialog/AddCacheEntry.h b/Source/QtDialog/AddCacheEntry.h new file mode 100644 index 0000000..cc710f5 --- /dev/null +++ b/Source/QtDialog/AddCacheEntry.h @@ -0,0 +1,35 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef AddCacheEntry_h +#define AddCacheEntry_h + +#include "QCMake.h" + +#include <QCheckBox> +#include <QStringList> +#include <QWidget> + +#include "ui_AddCacheEntry.h" + +class AddCacheEntry : public QWidget, public Ui::AddCacheEntry +{ + Q_OBJECT +public: + AddCacheEntry(QWidget* p, const QStringList& varNames, + const QStringList& varTypes); + + QString name() const; + QVariant value() const; + QString description() const; + QCMakeProperty::PropertyType type() const; + QString typeString() const; + +private slots: + void onCompletionActivated(const QString& text); + +private: + const QStringList& VarNames; + const QStringList& VarTypes; +}; + +#endif diff --git a/Source/QtDialog/AddCacheEntry.ui b/Source/QtDialog/AddCacheEntry.ui new file mode 100644 index 0000000..a815874 --- /dev/null +++ b/Source/QtDialog/AddCacheEntry.ui @@ -0,0 +1,97 @@ +<ui version="4.0" > + <class>AddCacheEntry</class> + <widget class="QWidget" name="AddCacheEntry" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>380</width> + <height>158</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" > + <property name="margin" > + <number>0</number> + </property> + <item row="0" column="0" > + <widget class="QLabel" name="label" > + <property name="text" > + <string>Name:</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="Name" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_2" > + <property name="text" > + <string>Type:</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="Type" > + </widget> + </item> + <item row="2" column="0" > + <widget class="QLabel" name="label_5" > + <property name="text" > + <string>Value:</string> + </property> + </widget> + </item> + <item row="2" column="1" > + <widget class="QStackedWidget" name="StackedWidget" > + <property name="currentIndex" > + <number>0</number> + </property> + </widget> + </item> + <item row="3" column="0" > + <widget class="QLabel" name="label_3" > + <property name="text" > + <string>Description:</string> + </property> + </widget> + </item> + <item row="3" column="1" > + <widget class="QLineEdit" name="Description" /> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QCMakePathEditor</class> + <extends>QLineEdit</extends> + <header>QCMakeWidgets.h</header> + </customwidget> + <customwidget> + <class>QCMakeFilePathEditor</class> + <extends>QLineEdit</extends> + <header>QCMakeWidgets.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections> + <connection> + <sender>Type</sender> + <signal>currentIndexChanged(int)</signal> + <receiver>StackedWidget</receiver> + <slot>setCurrentIndex(int)</slot> + <hints> + <hint type="sourcelabel" > + <x>229</x> + <y>34</y> + </hint> + <hint type="destinationlabel" > + <x>287</x> + <y>65</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/Source/QtDialog/CMake.desktop b/Source/QtDialog/CMake.desktop new file mode 100644 index 0000000..842091f --- /dev/null +++ b/Source/QtDialog/CMake.desktop @@ -0,0 +1,12 @@ +[Desktop Entry] +Version=1.0 +Name=CMake +Comment=Cross-platform buildsystem +Exec=cmake-gui %f +Icon=CMakeSetup +Terminal=false +X-MultipleArgs=false +Type=Application +Categories=Development; +StartupNotify=true +MimeType=application/x-cmakecache; diff --git a/Source/QtDialog/CMakeLists.txt b/Source/QtDialog/CMakeLists.txt new file mode 100644 index 0000000..10fd718 --- /dev/null +++ b/Source/QtDialog/CMakeLists.txt @@ -0,0 +1,232 @@ +# Distributed under the OSI-approved BSD 3-Clause License. See accompanying +# file Copyright.txt or https://cmake.org/licensing for details. + +project(QtDialog) +if(POLICY CMP0020) + cmake_policy(SET CMP0020 NEW) # Drop when CMake >= 2.8.11 required +endif() +CMake_OPTIONAL_COMPONENT(cmake-gui) +find_package(Qt5Widgets QUIET) +if (Qt5Widgets_FOUND) + include_directories(${Qt5Widgets_INCLUDE_DIRS}) + add_definitions(${Qt5Widgets_DEFINITONS}) + macro(qt4_wrap_ui) + qt5_wrap_ui(${ARGN}) + endmacro() + macro(qt4_wrap_cpp) + qt5_wrap_cpp(${ARGN}) + endmacro() + macro(qt4_add_resources) + qt5_add_resources(${ARGN}) + endmacro() + set(CMake_QT_LIBRARIES ${Qt5Widgets_LIBRARIES}) + set(QT_QTMAIN_LIBRARY ${Qt5Core_QTMAIN_LIBRARIES}) + + # Remove this when the minimum version of Qt is 4.6. + add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0) + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}") + + if(CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES) + list(APPEND CMake_QT_LIBRARIES ${CMake_QT_STATIC_QXcbIntegrationPlugin_LIBRARIES}) + set_property(SOURCE CMakeSetup.cxx + PROPERTY COMPILE_DEFINITIONS USE_QXcbIntegrationPlugin) + endif() + + # We need to install platform plugin and add qt.conf for Qt5 on Mac and Windows. + # FIXME: This should be part of Qt5 CMake scripts, but unfortunatelly + # Qt5 support is missing there. + if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) + macro(install_qt5_plugin _qt_plugin_name _qt_plugins_var) + get_target_property(_qt_plugin_path "${_qt_plugin_name}" LOCATION) + if(EXISTS "${_qt_plugin_path}") + get_filename_component(_qt_plugin_file "${_qt_plugin_path}" NAME) + get_filename_component(_qt_plugin_type "${_qt_plugin_path}" PATH) + get_filename_component(_qt_plugin_type "${_qt_plugin_type}" NAME) + if(APPLE) + set(_qt_plugin_dir "PlugIns") + elseif(WIN32) + set(_qt_plugin_dir "plugins") + endif() + set(_qt_plugin_dest "${_qt_plugin_dir}/${_qt_plugin_type}") + install(FILES "${_qt_plugin_path}" + DESTINATION "${_qt_plugin_dest}" + ${COMPONENT}) + set(${_qt_plugins_var} + "${${_qt_plugins_var}};\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/${_qt_plugin_dest}/${_qt_plugin_file}") + else() + message(FATAL_ERROR "QT plugin ${_qt_plugin_name} not found") + endif() + endmacro() + if(APPLE) + install_qt5_plugin("Qt5::QCocoaIntegrationPlugin" QT_PLUGINS) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + "[Paths]\nPlugins = ${_qt_plugin_dir}\n") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources" + ${COMPONENT}) + elseif(WIN32) + install_qt5_plugin("Qt5::QWindowsIntegrationPlugin" QT_PLUGINS) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + "[Paths]\nPlugins = ../${_qt_plugin_dir}\n") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/qt.conf" + DESTINATION bin + ${COMPONENT}) + endif() + endif() + + if(TARGET Qt5::Core) + get_property(_Qt5_Core_LOCATION TARGET Qt5::Core PROPERTY LOCATION) + get_filename_component(Qt_BIN_DIR "${_Qt5_Core_LOCATION}" PATH) + if(APPLE) + get_filename_component(Qt_BIN_DIR "${Qt_BIN_DIR}" PATH) + endif() + endif() +else() + set(QT_MIN_VERSION "4.4.0") + find_package(Qt4 REQUIRED) + if(NOT QT4_FOUND) + message(SEND_ERROR "Failed to find Qt 4.4 or greater.") + return() + endif() + + include(${QT_USE_FILE}) + + set(CMake_QT_LIBRARIES ${QT_LIBRARIES}) + +endif() + +set(SRCS + AddCacheEntry.cxx + AddCacheEntry.h + CMakeSetup.cxx + CMakeSetupDialog.cxx + CMakeSetupDialog.h + FirstConfigure.cxx + FirstConfigure.h + QCMake.cxx + QCMake.h + QCMakeCacheView.cxx + QCMakeCacheView.h + QCMakeWidgets.cxx + QCMakeWidgets.h + RegexExplorer.cxx + RegexExplorer.h + WarningMessagesDialog.cxx + WarningMessagesDialog.h + ) +QT4_WRAP_UI(UI_SRCS + CMakeSetupDialog.ui + Compilers.ui + CrossCompiler.ui + AddCacheEntry.ui + RegexExplorer.ui + WarningMessagesDialog.ui + ) +QT4_WRAP_CPP(MOC_SRCS + AddCacheEntry.h + Compilers.h + CMakeSetupDialog.h + FirstConfigure.h + QCMake.h + QCMakeCacheView.h + QCMakeWidgets.h + RegexExplorer.h + WarningMessagesDialog.h + ) +QT4_ADD_RESOURCES(RC_SRCS CMakeSetup.qrc) + +set(SRCS ${SRCS} ${UI_SRCS} ${MOC_SRCS} ${RC_SRCS}) +if(WIN32) + set(SRCS ${SRCS} CMakeSetup.rc) +endif() +if(APPLE) + set(SRCS ${SRCS} CMakeSetup.icns) + set(MACOSX_BUNDLE_ICON_FILE CMakeSetup.icns) + set_source_files_properties(CMakeSetup.icns PROPERTIES + MACOSX_PACKAGE_LOCATION Resources) +endif() + +if(USE_LGPL) + install(FILES ${CMake_SOURCE_DIR}/Licenses/LGPLv${USE_LGPL}.txt + DESTINATION ${CMAKE_DATA_DIR}/Licenses + ${COMPONENT}) + set_property(SOURCE CMakeSetupDialog.cxx + PROPERTY COMPILE_DEFINITIONS USE_LGPL="${USE_LGPL}") +endif() + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_executable(cmake-gui WIN32 MACOSX_BUNDLE ${SRCS} ${MANIFEST_FILE}) +target_link_libraries(cmake-gui CMakeLib ${QT_QTMAIN_LIBRARY} ${CMake_QT_LIBRARIES}) + +if(APPLE) + file(STRINGS "${CMake_SOURCE_DIR}/Copyright.txt" copyright_line + LIMIT_COUNT 1 REGEX "^Copyright 2000-20[0-9][0-9] Kitware") + + set_target_properties(cmake-gui PROPERTIES + OUTPUT_NAME CMake + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${CMAKE_BUNDLE_VERSION}" + # TBD: MACOSX_BUNDLE_BUNDLE_VERSION "${CMAKE_BUNDLE_VERSION}" + MACOSX_BUNDLE_COPYRIGHT "${copyright_line}" + MACOSX_BUNDLE_GUI_IDENTIFIER "org.cmake.cmake" + ) + + # Create a symlink in the build tree to provide a "cmake-gui" next + # to the "cmake" executable that refers to the application bundle. + add_custom_command(TARGET cmake-gui POST_BUILD + COMMAND ln -sf CMake.app/Contents/MacOS/CMake + $<TARGET_FILE_DIR:cmake>/cmake-gui + ) +endif() +set(CMAKE_INSTALL_DESTINATION_ARGS + BUNDLE DESTINATION "${CMAKE_BUNDLE_LOCATION}" ${COMPONENT}) + +install(TARGETS cmake-gui + RUNTIME DESTINATION bin ${COMPONENT} + ${CMAKE_INSTALL_DESTINATION_ARGS}) + +if(UNIX AND NOT APPLE) + foreach (size IN ITEMS 32 128) + install( + FILES "${CMAKE_CURRENT_SOURCE_DIR}/CMakeSetup${size}.png" + DESTINATION "${CMAKE_XDGDATA_DIR}/icons/hicolor/${size}x${size}/apps" + ${COMPONENT} + RENAME "CMakeSetup.png") + endforeach () + + # install a desktop file so CMake appears in the application start menu + # with an icon + install(FILES CMake.desktop + DESTINATION "${CMAKE_XDGDATA_DIR}/applications" + ${COMPONENT}) + install(FILES cmakecache.xml + DESTINATION "${CMAKE_XDGDATA_DIR}/mime/packages" + ${COMPONENT}) +endif() + +if(APPLE) + install(CODE " + execute_process(COMMAND ln -s \"../MacOS/CMake\" cmake-gui + WORKING_DIRECTORY \$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/bin) + " ${COMPONENT}) +endif() + +if(CMake_INSTALL_DEPENDENCIES AND (APPLE OR WIN32)) + # install rules for including 3rd party libs such as Qt + # if a system Qt is used (e.g. installed in /usr/lib/), it will not be included in the installation + set(fixup_exe "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/bin/cmake-gui${CMAKE_EXECUTABLE_SUFFIX}") + if(APPLE) + set(fixup_exe "\$ENV{DESTDIR}\${CMAKE_INSTALL_PREFIX}/MacOS/CMake") + endif() + install(CODE " + include(\"${CMake_SOURCE_DIR}/Modules/BundleUtilities.cmake\") + set(BU_CHMOD_BUNDLE_ITEMS ON) + fixup_bundle(\"${fixup_exe}\" \"${QT_PLUGINS}\" \"${Qt_BIN_DIR};${QT_LIBRARY_DIR};${QT_BINARY_DIR}\") + " ${COMPONENT}) +endif() + +set(CMAKE_PACKAGE_QTGUI TRUE) +configure_file("${QtDialog_SOURCE_DIR}/QtDialogCPack.cmake.in" + "${QtDialog_BINARY_DIR}/QtDialogCPack.cmake" @ONLY) diff --git a/Source/QtDialog/CMakeSetup.cxx b/Source/QtDialog/CMakeSetup.cxx new file mode 100644 index 0000000..5e03c39 --- /dev/null +++ b/Source/QtDialog/CMakeSetup.cxx @@ -0,0 +1,253 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMake.h" // include to disable MS warnings + +#include "CMakeSetupDialog.h" +#include "cmAlgorithms.h" +#include "cmDocumentation.h" +#include "cmDocumentationEntry.h" +#include "cmVersion.h" +#include "cmake.h" +#include <QApplication> +#include <QDir> +#include <QLocale> +#include <QString> +#include <QTextCodec> +#include <QTranslator> +#include <QtPlugin> +#include <cmsys/CommandLineArguments.hxx> +#include <cmsys/Encoding.hxx> +#include <cmsys/SystemTools.hxx> +#include <iostream> + +#include "cmSystemTools.h" // IWYU pragma: keep + +static const char* cmDocumentationName[][2] = { { CM_NULLPTR, + " cmake-gui - CMake GUI." }, + { CM_NULLPTR, CM_NULLPTR } }; + +static const char* cmDocumentationUsage[][2] = { + { CM_NULLPTR, " cmake-gui [options]\n" + " cmake-gui [options] <path-to-source>\n" + " cmake-gui [options] <path-to-existing-build>" }, + { CM_NULLPTR, CM_NULLPTR } +}; + +static const char* cmDocumentationOptions[] + [2] = { { CM_NULLPTR, CM_NULLPTR } }; + +#if defined(Q_OS_MAC) +static int cmOSXInstall(std::string dir); +static void cmAddPluginPath(); +#endif + +#if defined(USE_QXcbIntegrationPlugin) +Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); +#endif + +int main(int argc, char** argv) +{ + cmsys::Encoding::CommandLineArguments encoding_args = + cmsys::Encoding::CommandLineArguments::Main(argc, argv); + int argc2 = encoding_args.argc(); + char const* const* argv2 = encoding_args.argv(); + + cmSystemTools::FindCMakeResources(argv2[0]); + // check docs first so that X is not need to get docs + // do docs, if args were given + cmDocumentation doc; + doc.addCMakeStandardDocSections(); + if (argc2 > 1 && doc.CheckOptions(argc2, argv2)) { + // Construct and print requested documentation. + cmake hcm; + hcm.SetHomeDirectory(""); + hcm.SetHomeOutputDirectory(""); + hcm.AddCMakePaths(); + + std::vector<cmDocumentationEntry> generators; + hcm.GetGeneratorDocumentation(generators); + doc.SetName("cmake"); + doc.SetSection("Name", cmDocumentationName); + doc.SetSection("Usage", cmDocumentationUsage); + doc.AppendSection("Generators", generators); + doc.PrependSection("Options", cmDocumentationOptions); + + return (doc.PrintRequestedDocumentation(std::cout) ? 0 : 1); + } + +#if defined(Q_OS_MAC) + if (argc2 == 2 && strcmp(argv2[1], "--install") == 0) { + return cmOSXInstall("/usr/local/bin"); + } + if (argc2 == 2 && cmHasLiteralPrefix(argv2[1], "--install=")) { + return cmOSXInstall(argv2[1] + 10); + } +#endif + +// When we are on OSX and we are launching cmake-gui from a symlink, the +// application will fail to launch as it can't find the qt.conf file which +// tells it what the name of the plugin folder is. We need to add this path +// BEFORE the application is constructed as that is what triggers the +// searching for the platform plugins +#if defined(Q_OS_MAC) + cmAddPluginPath(); +#endif + + QApplication app(argc, argv); + + setlocale(LC_NUMERIC, "C"); + + QTextCodec* utf8_codec = QTextCodec::codecForName("UTF-8"); + QTextCodec::setCodecForLocale(utf8_codec); + +#if QT_VERSION < 0x050000 + // clean out standard Qt paths for plugins, which we don't use anyway + // when creating Mac bundles, it potentially causes problems + foreach (QString p, QApplication::libraryPaths()) { + QApplication::removeLibraryPath(p); + } +#endif + + // tell the cmake library where cmake is + QDir cmExecDir(QApplication::applicationDirPath()); +#if defined(Q_OS_MAC) + cmExecDir.cd("../../../"); +#endif + + // pick up translation files if they exists in the data directory + QDir translationsDir = cmExecDir; + translationsDir.cd(QString::fromLocal8Bit(".." CMAKE_DATA_DIR)); + translationsDir.cd("i18n"); + QTranslator translator; + QString transfile = QString("cmake_%1").arg(QLocale::system().name()); + translator.load(transfile, translationsDir.path()); + app.installTranslator(&translator); + + // app setup + app.setApplicationName("CMakeSetup"); + app.setOrganizationName("Kitware"); + QIcon appIcon; + appIcon.addFile(":/Icons/CMakeSetup32.png"); + appIcon.addFile(":/Icons/CMakeSetup128.png"); + app.setWindowIcon(appIcon); + + CMakeSetupDialog dialog; + dialog.show(); + + cmsys::CommandLineArguments arg; + arg.Initialize(argc2, argv2); + std::string binaryDirectory; + std::string sourceDirectory; + typedef cmsys::CommandLineArguments argT; + arg.AddArgument("-B", argT::CONCAT_ARGUMENT, &binaryDirectory, + "Binary Directory"); + arg.AddArgument("-H", argT::CONCAT_ARGUMENT, &sourceDirectory, + "Source Directory"); + // do not complain about unknown options + arg.StoreUnusedArguments(true); + arg.Parse(); + if (!sourceDirectory.empty() && !binaryDirectory.empty()) { + dialog.setSourceDirectory(QString::fromLocal8Bit(sourceDirectory.c_str())); + dialog.setBinaryDirectory(QString::fromLocal8Bit(binaryDirectory.c_str())); + } else { + QStringList args = app.arguments(); + if (args.count() == 2) { + std::string filePath = + cmSystemTools::CollapseFullPath(args[1].toLocal8Bit().data()); + + // check if argument is a directory containing CMakeCache.txt + std::string buildFilePath = + cmSystemTools::CollapseFullPath("CMakeCache.txt", filePath.c_str()); + + // check if argument is a CMakeCache.txt file + if (cmSystemTools::GetFilenameName(filePath) == "CMakeCache.txt" && + cmSystemTools::FileExists(filePath.c_str())) { + buildFilePath = filePath; + } + + // check if argument is a directory containing CMakeLists.txt + std::string srcFilePath = + cmSystemTools::CollapseFullPath("CMakeLists.txt", filePath.c_str()); + + if (cmSystemTools::FileExists(buildFilePath.c_str())) { + dialog.setBinaryDirectory(QString::fromLocal8Bit( + cmSystemTools::GetFilenamePath(buildFilePath).c_str())); + } else if (cmSystemTools::FileExists(srcFilePath.c_str())) { + dialog.setSourceDirectory(QString::fromLocal8Bit(filePath.c_str())); + dialog.setBinaryDirectory(QString::fromLocal8Bit( + cmSystemTools::CollapseFullPath(".").c_str())); + } + } + } + + return app.exec(); +} + +#if defined(Q_OS_MAC) +#include <errno.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> +static bool cmOSXInstall(std::string const& dir, std::string const& tool) +{ + if (tool.empty()) { + return true; + } + std::string link = dir + cmSystemTools::GetFilenameName(tool); + struct stat st; + if (lstat(link.c_str(), &st) == 0 && S_ISLNK(st.st_mode)) { + char buf[4096]; + ssize_t s = readlink(link.c_str(), buf, sizeof(buf) - 1); + if (s >= 0 && std::string(buf, s) == tool) { + std::cerr << "Exists: '" << link << "' -> '" << tool << "'\n"; + return true; + } + } + cmSystemTools::MakeDirectory(dir); + if (symlink(tool.c_str(), link.c_str()) == 0) { + std::cerr << "Linked: '" << link << "' -> '" << tool << "'\n"; + return true; + } else { + int err = errno; + std::cerr << "Failed: '" << link << "' -> '" << tool + << "': " << strerror(err) << "\n"; + return false; + } +} +static int cmOSXInstall(std::string dir) +{ + if (!cmHasLiteralSuffix(dir, "/")) { + dir += "/"; + } + return (cmOSXInstall(dir, cmSystemTools::GetCMakeCommand()) && + cmOSXInstall(dir, cmSystemTools::GetCTestCommand()) && + cmOSXInstall(dir, cmSystemTools::GetCPackCommand()) && + cmOSXInstall(dir, cmSystemTools::GetCMakeGUICommand()) && + cmOSXInstall(dir, cmSystemTools::GetCMakeCursesCommand())) + ? 0 + : 1; +} + +// Locate the PlugIns directory and add it to the QApplication library paths. +// We need to resolve all symlinks so we have a known relative path between +// MacOS/CMake and the PlugIns directory. +// +// Note we are using cmSystemTools since Qt can't provide the path to the +// executable before the QApplication is created, and that is when plugin +// searching occurs. +static void cmAddPluginPath() +{ + std::string const& path = cmSystemTools::GetCMakeGUICommand(); + if (path.empty()) { + return; + } + std::string const& realPath = cmSystemTools::GetRealPath(path); + QFileInfo appPath(QString::fromLocal8Bit(realPath.c_str())); + QDir pluginDir = appPath.dir(); + bool const foundPluginDir = pluginDir.cd("../PlugIns"); + if (foundPluginDir) { + QApplication::addLibraryPath(pluginDir.path()); + } +} + +#endif diff --git a/Source/QtDialog/CMakeSetup.icns b/Source/QtDialog/CMakeSetup.icns Binary files differnew file mode 100644 index 0000000..4a50c04 --- /dev/null +++ b/Source/QtDialog/CMakeSetup.icns diff --git a/Source/QtDialog/CMakeSetup.ico b/Source/QtDialog/CMakeSetup.ico Binary files differnew file mode 100644 index 0000000..e13bb15 --- /dev/null +++ b/Source/QtDialog/CMakeSetup.ico diff --git a/Source/QtDialog/CMakeSetup.qrc b/Source/QtDialog/CMakeSetup.qrc new file mode 100644 index 0000000..eaac192 --- /dev/null +++ b/Source/QtDialog/CMakeSetup.qrc @@ -0,0 +1,8 @@ +<RCC> + <qresource prefix="/Icons" > + <file>CMakeSetup128.png</file> + <file>CMakeSetup32.png</file> + <file>Delete16.png</file> + <file>Plus16.png</file> + </qresource> +</RCC> diff --git a/Source/QtDialog/CMakeSetup.rc b/Source/QtDialog/CMakeSetup.rc new file mode 100644 index 0000000..fcc887d --- /dev/null +++ b/Source/QtDialog/CMakeSetup.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "CMakeSetup.ico" diff --git a/Source/QtDialog/CMakeSetup128.png b/Source/QtDialog/CMakeSetup128.png Binary files differnew file mode 100644 index 0000000..12f1d9a --- /dev/null +++ b/Source/QtDialog/CMakeSetup128.png diff --git a/Source/QtDialog/CMakeSetup32.png b/Source/QtDialog/CMakeSetup32.png Binary files differnew file mode 100644 index 0000000..7bbcee4 --- /dev/null +++ b/Source/QtDialog/CMakeSetup32.png diff --git a/Source/QtDialog/CMakeSetup64.png b/Source/QtDialog/CMakeSetup64.png Binary files differnew file mode 100644 index 0000000..43a8cc6 --- /dev/null +++ b/Source/QtDialog/CMakeSetup64.png diff --git a/Source/QtDialog/CMakeSetupDialog.cxx b/Source/QtDialog/CMakeSetupDialog.cxx new file mode 100644 index 0000000..111b28a --- /dev/null +++ b/Source/QtDialog/CMakeSetupDialog.cxx @@ -0,0 +1,1330 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "CMakeSetupDialog.h" + +#include <QCloseEvent> +#include <QCoreApplication> +#include <QDesktopServices> +#include <QDialogButtonBox> +#include <QDragEnterEvent> +#include <QFileDialog> +#include <QInputDialog> +#include <QKeySequence> +#include <QMenu> +#include <QMenuBar> +#include <QMessageBox> +#include <QMimeData> +#include <QProgressBar> +#include <QSettings> +#include <QShortcut> +#include <QStatusBar> +#include <QToolButton> +#include <QUrl> + +#include "AddCacheEntry.h" +#include "FirstConfigure.h" +#include "QCMake.h" +#include "QCMakeCacheView.h" +#include "RegexExplorer.h" +#include "WarningMessagesDialog.h" +#include "cmSystemTools.h" +#include "cmVersion.h" + +QCMakeThread::QCMakeThread(QObject* p) + : QThread(p) + , CMakeInstance(CM_NULLPTR) +{ +} + +QCMake* QCMakeThread::cmakeInstance() const +{ + return this->CMakeInstance; +} + +void QCMakeThread::run() +{ + this->CMakeInstance = new QCMake; + // emit that this cmake thread is ready for use + emit this->cmakeInitialized(); + this->exec(); + delete this->CMakeInstance; + this->CMakeInstance = CM_NULLPTR; +} + +CMakeSetupDialog::CMakeSetupDialog() + : ExitAfterGenerate(true) + , CacheModified(false) + , ConfigureNeeded(true) + , CurrentState(Interrupting) +{ + QString title = QString(tr("CMake %1")); + title = title.arg(cmVersion::GetCMakeVersion()); + this->setWindowTitle(title); + + // create the GUI + QSettings settings; + settings.beginGroup("Settings/StartPath"); + restoreGeometry(settings.value("geometry").toByteArray()); + restoreState(settings.value("windowState").toByteArray()); + + this->AddVariableNames = + settings.value("AddVariableNames", QStringList("CMAKE_INSTALL_PREFIX")) + .toStringList(); + this->AddVariableTypes = + settings.value("AddVariableTypes", QStringList("PATH")).toStringList(); + + QWidget* cont = new QWidget(this); + this->setupUi(cont); + this->Splitter->setStretchFactor(0, 3); + this->Splitter->setStretchFactor(1, 1); + this->setCentralWidget(cont); + this->ProgressBar->reset(); + this->RemoveEntry->setEnabled(false); + this->AddEntry->setEnabled(false); + + QByteArray p = settings.value("SplitterSizes").toByteArray(); + this->Splitter->restoreState(p); + + bool groupView = settings.value("GroupView", false).toBool(); + this->setGroupedView(groupView); + this->groupedCheck->setCheckState(groupView ? Qt::Checked : Qt::Unchecked); + + bool advancedView = settings.value("AdvancedView", false).toBool(); + this->setAdvancedView(advancedView); + this->advancedCheck->setCheckState(advancedView ? Qt::Checked + : Qt::Unchecked); + + QMenu* FileMenu = this->menuBar()->addMenu(tr("&File")); + this->ReloadCacheAction = FileMenu->addAction(tr("&Reload Cache")); + QObject::connect(this->ReloadCacheAction, SIGNAL(triggered(bool)), this, + SLOT(doReloadCache())); + this->DeleteCacheAction = FileMenu->addAction(tr("&Delete Cache")); + QObject::connect(this->DeleteCacheAction, SIGNAL(triggered(bool)), this, + SLOT(doDeleteCache())); + this->ExitAction = FileMenu->addAction(tr("E&xit")); + this->ExitAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Q)); + QObject::connect(this->ExitAction, SIGNAL(triggered(bool)), this, + SLOT(close())); + + QMenu* ToolsMenu = this->menuBar()->addMenu(tr("&Tools")); + this->ConfigureAction = ToolsMenu->addAction(tr("&Configure")); + // prevent merging with Preferences menu item on Mac OS X + this->ConfigureAction->setMenuRole(QAction::NoRole); + QObject::connect(this->ConfigureAction, SIGNAL(triggered(bool)), this, + SLOT(doConfigure())); + this->GenerateAction = ToolsMenu->addAction(tr("&Generate")); + QObject::connect(this->GenerateAction, SIGNAL(triggered(bool)), this, + SLOT(doGenerate())); + QAction* showChangesAction = ToolsMenu->addAction(tr("&Show My Changes")); + QObject::connect(showChangesAction, SIGNAL(triggered(bool)), this, + SLOT(showUserChanges())); +#if defined(Q_WS_MAC) || defined(Q_OS_MAC) + this->InstallForCommandLineAction = + ToolsMenu->addAction(tr("&How to Install For Command Line Use")); + QObject::connect(this->InstallForCommandLineAction, SIGNAL(triggered(bool)), + this, SLOT(doInstallForCommandLine())); +#endif + ToolsMenu->addSeparator(); + ToolsMenu->addAction(tr("Regular Expression Explorer..."), this, + SLOT(doRegexExplorerDialog())); + ToolsMenu->addSeparator(); + ToolsMenu->addAction(tr("&Find in Output..."), this, + SLOT(doOutputFindDialog()), QKeySequence::Find); + ToolsMenu->addAction(tr("Find Next"), this, SLOT(doOutputFindNext()), + QKeySequence::FindNext); + ToolsMenu->addAction(tr("Find Previous"), this, SLOT(doOutputFindPrev()), + QKeySequence::FindPrevious); + ToolsMenu->addAction(tr("Goto Next Error"), this, SLOT(doOutputErrorNext()), + QKeySequence(Qt::Key_F8)); // in Visual Studio + new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_Period), this, + SLOT(doOutputErrorNext())); // in Eclipse + + QMenu* OptionsMenu = this->menuBar()->addMenu(tr("&Options")); + OptionsMenu->addAction(tr("Warning Messages..."), this, + SLOT(doWarningMessagesDialog())); + this->WarnUninitializedAction = + OptionsMenu->addAction(tr("&Warn Uninitialized (--warn-uninitialized)")); + this->WarnUninitializedAction->setCheckable(true); + this->WarnUnusedAction = + OptionsMenu->addAction(tr("&Warn Unused (--warn-unused-vars)")); + this->WarnUnusedAction->setCheckable(true); + + QAction* debugAction = OptionsMenu->addAction(tr("&Debug Output")); + debugAction->setCheckable(true); + QObject::connect(debugAction, SIGNAL(toggled(bool)), this, + SLOT(setDebugOutput(bool))); + + OptionsMenu->addSeparator(); + QAction* expandAction = + OptionsMenu->addAction(tr("&Expand Grouped Entries")); + QObject::connect(expandAction, SIGNAL(triggered(bool)), this->CacheValues, + SLOT(expandAll())); + QAction* collapseAction = + OptionsMenu->addAction(tr("&Collapse Grouped Entries")); + QObject::connect(collapseAction, SIGNAL(triggered(bool)), this->CacheValues, + SLOT(collapseAll())); + + QMenu* HelpMenu = this->menuBar()->addMenu(tr("&Help")); + QAction* a = HelpMenu->addAction(tr("About")); + QObject::connect(a, SIGNAL(triggered(bool)), this, SLOT(doAbout())); + a = HelpMenu->addAction(tr("Help")); + QObject::connect(a, SIGNAL(triggered(bool)), this, SLOT(doHelp())); + + this->setAcceptDrops(true); + + // get the saved binary directories + QStringList buildPaths = this->loadBuildPaths(); + this->BinaryDirectory->addItems(buildPaths); + + this->BinaryDirectory->setCompleter(new QCMakeFileCompleter(this, true)); + this->SourceDirectory->setCompleter(new QCMakeFileCompleter(this, true)); + + // fixed pitch font in output window + QFont outputFont("Courier"); + this->Output->setFont(outputFont); + this->ErrorFormat.setForeground(QBrush(Qt::red)); + + this->Output->setContextMenuPolicy(Qt::CustomContextMenu); + connect(this->Output, SIGNAL(customContextMenuRequested(const QPoint&)), + this, SLOT(doOutputContextMenu(const QPoint&))); + + // start the cmake worker thread + this->CMakeThread = new QCMakeThread(this); + QObject::connect(this->CMakeThread, SIGNAL(cmakeInitialized()), this, + SLOT(initialize()), Qt::QueuedConnection); + this->CMakeThread->start(); + + this->enterState(ReadyConfigure); + + ProgressOffset = 0.0; + ProgressFactor = 1.0; +} + +void CMakeSetupDialog::initialize() +{ + // now the cmake worker thread is running, lets make our connections to it + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(propertiesChanged(const QCMakePropertyList&)), + this->CacheValues->cacheModel(), + SLOT(setProperties(const QCMakePropertyList&))); + + QObject::connect(this->ConfigureButton, SIGNAL(clicked(bool)), this, + SLOT(doConfigure())); + + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(configureDone(int)), this, SLOT(exitLoop(int))); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(generateDone(int)), this, SLOT(exitLoop(int))); + + QObject::connect(this->GenerateButton, SIGNAL(clicked(bool)), this, + SLOT(doGenerate())); + QObject::connect(this->OpenProjectButton, SIGNAL(clicked(bool)), this, + SLOT(doOpenProject())); + + QObject::connect(this->BrowseSourceDirectoryButton, SIGNAL(clicked(bool)), + this, SLOT(doSourceBrowse())); + QObject::connect(this->BrowseBinaryDirectoryButton, SIGNAL(clicked(bool)), + this, SLOT(doBinaryBrowse())); + + QObject::connect(this->BinaryDirectory, SIGNAL(editTextChanged(QString)), + this, SLOT(onBinaryDirectoryChanged(QString))); + QObject::connect(this->SourceDirectory, SIGNAL(textChanged(QString)), this, + SLOT(onSourceDirectoryChanged(QString))); + + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(sourceDirChanged(QString)), this, + SLOT(updateSourceDirectory(QString))); + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(binaryDirChanged(QString)), this, + SLOT(updateBinaryDirectory(QString))); + + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(progressChanged(QString, float)), this, + SLOT(showProgress(QString, float))); + + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(errorMessage(QString)), this, SLOT(error(QString))); + + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(outputMessage(QString)), this, + SLOT(message(QString))); + + QObject::connect(this->groupedCheck, SIGNAL(toggled(bool)), this, + SLOT(setGroupedView(bool))); + QObject::connect(this->advancedCheck, SIGNAL(toggled(bool)), this, + SLOT(setAdvancedView(bool))); + QObject::connect(this->Search, SIGNAL(textChanged(QString)), this, + SLOT(setSearchFilter(QString))); + + QObject::connect(this->CMakeThread->cmakeInstance(), + SIGNAL(generatorChanged(QString)), this, + SLOT(updateGeneratorLabel(QString))); + this->updateGeneratorLabel(QString()); + + QObject::connect(this->CacheValues->cacheModel(), + SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, + SLOT(setCacheModified())); + + QObject::connect(this->CacheValues->selectionModel(), + SIGNAL(selectionChanged(QItemSelection, QItemSelection)), + this, SLOT(selectionChanged())); + QObject::connect(this->RemoveEntry, SIGNAL(clicked(bool)), this, + SLOT(removeSelectedCacheEntries())); + QObject::connect(this->AddEntry, SIGNAL(clicked(bool)), this, + SLOT(addCacheEntry())); + + QObject::connect(this->WarnUninitializedAction, SIGNAL(triggered(bool)), + this->CMakeThread->cmakeInstance(), + SLOT(setWarnUninitializedMode(bool))); + QObject::connect(this->WarnUnusedAction, SIGNAL(triggered(bool)), + this->CMakeThread->cmakeInstance(), + SLOT(setWarnUnusedMode(bool))); + + if (!this->SourceDirectory->text().isEmpty() || + !this->BinaryDirectory->lineEdit()->text().isEmpty()) { + this->onSourceDirectoryChanged(this->SourceDirectory->text()); + this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text()); + } else { + this->onBinaryDirectoryChanged(this->BinaryDirectory->lineEdit()->text()); + } +} + +CMakeSetupDialog::~CMakeSetupDialog() +{ + QSettings settings; + settings.beginGroup("Settings/StartPath"); + settings.setValue("windowState", QVariant(saveState())); + settings.setValue("geometry", QVariant(saveGeometry())); + settings.setValue("SplitterSizes", this->Splitter->saveState()); + + // wait for thread to stop + this->CMakeThread->quit(); + this->CMakeThread->wait(); +} + +bool CMakeSetupDialog::prepareConfigure() +{ + // make sure build directory exists + QString bindir = this->CMakeThread->cmakeInstance()->binaryDirectory(); + QDir dir(bindir); + if (!dir.exists()) { + QString msg = tr("Build directory does not exist, " + "should I create it?\n\n" + "Directory: "); + msg += bindir; + QString title = tr("Create Directory"); + QMessageBox::StandardButton btn; + btn = QMessageBox::information(this, title, msg, + QMessageBox::Yes | QMessageBox::No); + if (btn == QMessageBox::No) { + return false; + } + if (!dir.mkpath(".")) { + QMessageBox::information( + this, tr("Create Directory Failed"), + QString(tr("Failed to create directory %1")).arg(dir.path()), + QMessageBox::Ok); + + return false; + } + } + + // if no generator, prompt for it and other setup stuff + if (this->CMakeThread->cmakeInstance()->generator().isEmpty()) { + if (!this->setupFirstConfigure()) { + return false; + } + } + + // remember path + this->addBinaryPath(dir.absolutePath()); + + return true; +} + +void CMakeSetupDialog::exitLoop(int err) +{ + this->LocalLoop.exit(err); +} + +void CMakeSetupDialog::doConfigure() +{ + if (this->CurrentState == Configuring) { + // stop configure + doInterrupt(); + return; + } + + if (!prepareConfigure()) { + return; + } + + this->enterState(Configuring); + + bool ret = doConfigureInternal(); + + if (ret) { + this->ConfigureNeeded = false; + } + + if (ret && !this->CacheValues->cacheModel()->newPropertyCount()) { + this->enterState(ReadyGenerate); + } else { + this->enterState(ReadyConfigure); + this->CacheValues->scrollToTop(); + } + this->ProgressBar->reset(); +} + +bool CMakeSetupDialog::doConfigureInternal() +{ + this->Output->clear(); + this->CacheValues->selectionModel()->clear(); + + QMetaObject::invokeMethod( + this->CMakeThread->cmakeInstance(), "setProperties", Qt::QueuedConnection, + Q_ARG(QCMakePropertyList, this->CacheValues->cacheModel()->properties())); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "configure", + Qt::QueuedConnection); + + int err = this->LocalLoop.exec(); + + if (err != 0) { + QMessageBox::critical( + this, tr("Error"), + tr("Error in configuration process, project files may be invalid"), + QMessageBox::Ok); + } + + return 0 == err; +} + +void CMakeSetupDialog::doInstallForCommandLine() +{ + QString title = tr("How to Install For Command Line Use"); + QString msg = tr("One may add CMake to the PATH:\n" + "\n" + " PATH=\"%1\":\"$PATH\"\n" + "\n" + "Or, to install symlinks to '/usr/local/bin', run:\n" + "\n" + " sudo \"%2\" --install\n" + "\n" + "Or, to install symlinks to another directory, run:\n" + "\n" + " sudo \"%3\" --install=/path/to/bin\n"); + msg = msg.arg( + cmSystemTools::GetFilenamePath(cmSystemTools::GetCMakeCommand()).c_str()); + msg = msg.arg(cmSystemTools::GetCMakeGUICommand().c_str()); + msg = msg.arg(cmSystemTools::GetCMakeGUICommand().c_str()); + + QDialog dialog; + dialog.setWindowTitle(title); + QVBoxLayout* l = new QVBoxLayout(&dialog); + QLabel* lab = new QLabel(&dialog); + l->addWidget(lab); + lab->setText(msg); + lab->setWordWrap(false); + lab->setTextInteractionFlags(Qt::TextSelectableByMouse); + QDialogButtonBox* btns = + new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, &dialog); + QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept())); + l->addWidget(btns); + dialog.exec(); +} + +bool CMakeSetupDialog::doGenerateInternal() +{ + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "generate", + Qt::QueuedConnection); + + int err = this->LocalLoop.exec(); + + if (err != 0) { + QMessageBox::critical( + this, tr("Error"), + tr("Error in generation process, project files may be invalid"), + QMessageBox::Ok); + } + + return 0 == err; +} + +void CMakeSetupDialog::doGenerate() +{ + if (this->CurrentState == Generating) { + // stop generate + doInterrupt(); + return; + } + + // see if we need to configure + // we'll need to configure if: + // the configure step hasn't been done yet + // generate was the last step done + if (this->ConfigureNeeded) { + if (!prepareConfigure()) { + return; + } + } + + this->enterState(Generating); + + bool config_passed = true; + if (this->ConfigureNeeded) { + this->CacheValues->cacheModel()->setShowNewProperties(false); + this->ProgressFactor = 0.5; + config_passed = doConfigureInternal(); + this->ProgressOffset = 0.5; + } + + if (config_passed) { + doGenerateInternal(); + } + + this->ProgressOffset = 0.0; + this->ProgressFactor = 1.0; + this->CacheValues->cacheModel()->setShowNewProperties(true); + + this->enterState(ReadyConfigure); + this->ProgressBar->reset(); + + this->ConfigureNeeded = true; +} + +QString CMakeSetupDialog::getProjectFilename() +{ + QStringList nameFilter; + nameFilter << "*.sln" + << "*.xcodeproj"; + QDir directory(this->BinaryDirectory->currentText()); + QStringList nlnFile = directory.entryList(nameFilter); + + if (nlnFile.count() == 1) { + return this->BinaryDirectory->currentText() + "/" + nlnFile.at(0); + } + + return QString(); +} + +void CMakeSetupDialog::doOpenProject() +{ + QDesktopServices::openUrl(QUrl::fromLocalFile(this->getProjectFilename())); +} + +void CMakeSetupDialog::closeEvent(QCloseEvent* e) +{ + // prompt for close if there are unsaved changes, and we're not busy + if (this->CacheModified) { + QString msg = tr("You have changed options but not rebuilt, " + "are you sure you want to exit?"); + QString title = tr("Confirm Exit"); + QMessageBox::StandardButton btn; + btn = QMessageBox::critical(this, title, msg, + QMessageBox::Yes | QMessageBox::No); + if (btn == QMessageBox::No) { + e->ignore(); + } + } + + // don't close if we're busy, unless the user really wants to + if (this->CurrentState == Configuring) { + QString msg = + tr("You are in the middle of a Configure.\n" + "If you Exit now the configure information will be lost.\n" + "Are you sure you want to Exit?"); + QString title = tr("Confirm Exit"); + QMessageBox::StandardButton btn; + btn = QMessageBox::critical(this, title, msg, + QMessageBox::Yes | QMessageBox::No); + if (btn == QMessageBox::No) { + e->ignore(); + } else { + this->doInterrupt(); + } + } + + // let the generate finish + if (this->CurrentState == Generating) { + e->ignore(); + } +} + +void CMakeSetupDialog::doHelp() +{ + QString msg = tr( + "CMake is used to configure and generate build files for " + "software projects. The basic steps for configuring a project are as " + "follows:\r\n\r\n1. Select the source directory for the project. This " + "should " + "contain the CMakeLists.txt files for the project.\r\n\r\n2. Select the " + "build " + "directory for the project. This is the directory where the project " + "will be " + "built. It can be the same or a different directory than the source " + "directory. For easy clean up, a separate build directory is " + "recommended. " + "CMake will create the directory if it does not exist.\r\n\r\n3. Once the " + "source and binary directories are selected, it is time to press the " + "Configure button. This will cause CMake to read all of the input files " + "and " + "discover all the variables used by the project. The first time a " + "variable " + "is displayed it will be in Red. Users should inspect red variables " + "making " + "sure the values are correct. For some projects the Configure process " + "can " + "be iterative, so continue to press the Configure button until there are " + "no " + "longer red entries.\r\n\r\n4. Once there are no longer red entries, you " + "should click the Generate button. This will write the build files to " + "the build " + "directory."); + + QDialog dialog; + QFontMetrics met(this->font()); + int msgWidth = met.width(msg); + dialog.setMinimumSize(msgWidth / 15, 20); + dialog.setWindowTitle(tr("Help")); + QVBoxLayout* l = new QVBoxLayout(&dialog); + QLabel* lab = new QLabel(&dialog); + lab->setText(msg); + lab->setWordWrap(true); + QDialogButtonBox* btns = + new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, &dialog); + QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept())); + l->addWidget(lab); + l->addWidget(btns); + dialog.exec(); +} + +void CMakeSetupDialog::doInterrupt() +{ + this->enterState(Interrupting); + this->CMakeThread->cmakeInstance()->interrupt(); +} + +void CMakeSetupDialog::doSourceBrowse() +{ + QString dir = QFileDialog::getExistingDirectory( + this, tr("Enter Path to Source"), this->SourceDirectory->text(), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + if (!dir.isEmpty()) { + this->setSourceDirectory(dir); + } +} + +void CMakeSetupDialog::updateSourceDirectory(const QString& dir) +{ + if (this->SourceDirectory->text() != dir) { + this->SourceDirectory->blockSignals(true); + this->SourceDirectory->setText(dir); + this->SourceDirectory->blockSignals(false); + } +} + +void CMakeSetupDialog::updateBinaryDirectory(const QString& dir) +{ + if (this->BinaryDirectory->currentText() != dir) { + this->BinaryDirectory->blockSignals(true); + this->BinaryDirectory->setEditText(dir); + this->BinaryDirectory->blockSignals(false); + } + if (!this->getProjectFilename().isEmpty()) { + this->OpenProjectButton->setEnabled(true); + } else { + this->OpenProjectButton->setEnabled(false); + } +} + +void CMakeSetupDialog::doBinaryBrowse() +{ + QString dir = QFileDialog::getExistingDirectory( + this, tr("Enter Path to Build"), this->BinaryDirectory->currentText(), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); + if (!dir.isEmpty() && dir != this->BinaryDirectory->currentText()) { + this->setBinaryDirectory(dir); + } +} + +void CMakeSetupDialog::setBinaryDirectory(const QString& dir) +{ + this->BinaryDirectory->setEditText(dir); +} + +void CMakeSetupDialog::onSourceDirectoryChanged(const QString& dir) +{ + this->Output->clear(); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "setSourceDirectory", Qt::QueuedConnection, + Q_ARG(QString, dir)); +} + +void CMakeSetupDialog::onBinaryDirectoryChanged(const QString& dir) +{ + QString title = QString(tr("CMake %1 - %2")); + title = title.arg(cmVersion::GetCMakeVersion()); + title = title.arg(dir); + this->setWindowTitle(title); + + this->CacheModified = false; + this->CacheValues->cacheModel()->clear(); + qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate()) + ->clearChanges(); + this->Output->clear(); + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "setBinaryDirectory", Qt::QueuedConnection, + Q_ARG(QString, dir)); +} + +void CMakeSetupDialog::setSourceDirectory(const QString& dir) +{ + this->SourceDirectory->setText(dir); +} + +void CMakeSetupDialog::showProgress(const QString& /*msg*/, float percent) +{ + percent = (percent * ProgressFactor) + ProgressOffset; + this->ProgressBar->setValue(qRound(percent * 100)); +} + +void CMakeSetupDialog::error(const QString& msg) +{ + this->Output->setCurrentCharFormat(this->ErrorFormat); + // QTextEdit will terminate the msg with a ParagraphSeparator, but it also + // replaces + // all newlines with ParagraphSeparators. By replacing the newlines by + // ourself, one + // error msg will be one paragraph. + QString paragraph(msg); + paragraph.replace(QLatin1Char('\n'), QChar::LineSeparator); + this->Output->append(paragraph); +} + +void CMakeSetupDialog::message(const QString& msg) +{ + this->Output->setCurrentCharFormat(this->MessageFormat); + this->Output->append(msg); +} + +void CMakeSetupDialog::setEnabledState(bool enabled) +{ + // disable parts of the GUI during configure/generate + this->CacheValues->cacheModel()->setEditEnabled(enabled); + this->SourceDirectory->setEnabled(enabled); + this->BrowseSourceDirectoryButton->setEnabled(enabled); + this->BinaryDirectory->setEnabled(enabled); + this->BrowseBinaryDirectoryButton->setEnabled(enabled); + this->ReloadCacheAction->setEnabled(enabled); + this->DeleteCacheAction->setEnabled(enabled); + this->ExitAction->setEnabled(enabled); + this->ConfigureAction->setEnabled(enabled); + this->AddEntry->setEnabled(enabled); + this->RemoveEntry->setEnabled(false); // let selection re-enable it +} + +bool CMakeSetupDialog::setupFirstConfigure() +{ + FirstConfigure dialog; + + // initialize dialog and restore saved settings + + // add generators + dialog.setGenerators( + this->CMakeThread->cmakeInstance()->availableGenerators()); + + // restore from settings + dialog.loadFromSettings(); + + if (dialog.exec() == QDialog::Accepted) { + dialog.saveToSettings(); + this->CMakeThread->cmakeInstance()->setGenerator(dialog.getGenerator()); + this->CMakeThread->cmakeInstance()->setToolset(dialog.getToolset()); + + QCMakeCacheModel* m = this->CacheValues->cacheModel(); + + if (dialog.compilerSetup()) { + QString fortranCompiler = dialog.getFortranCompiler(); + if (!fortranCompiler.isEmpty()) { + m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER", + "Fortran compiler.", fortranCompiler, false); + } + QString cxxCompiler = dialog.getCXXCompiler(); + if (!cxxCompiler.isEmpty()) { + m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER", + "CXX compiler.", cxxCompiler, false); + } + + QString cCompiler = dialog.getCCompiler(); + if (!cCompiler.isEmpty()) { + m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER", + "C compiler.", cCompiler, false); + } + } else if (dialog.crossCompilerSetup()) { + QString fortranCompiler = dialog.getFortranCompiler(); + if (!fortranCompiler.isEmpty()) { + m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_Fortran_COMPILER", + "Fortran compiler.", fortranCompiler, false); + } + + QString mode = dialog.getCrossIncludeMode(); + m->insertProperty(QCMakeProperty::STRING, + "CMAKE_FIND_ROOT_PATH_MODE_INCLUDE", + tr("CMake Find Include Mode"), mode, false); + mode = dialog.getCrossLibraryMode(); + m->insertProperty(QCMakeProperty::STRING, + "CMAKE_FIND_ROOT_PATH_MODE_LIBRARY", + tr("CMake Find Library Mode"), mode, false); + mode = dialog.getCrossProgramMode(); + m->insertProperty(QCMakeProperty::STRING, + "CMAKE_FIND_ROOT_PATH_MODE_PROGRAM", + tr("CMake Find Program Mode"), mode, false); + + QString rootPath = dialog.getCrossRoot(); + m->insertProperty(QCMakeProperty::PATH, "CMAKE_FIND_ROOT_PATH", + tr("CMake Find Root Path"), rootPath, false); + + QString systemName = dialog.getSystemName(); + m->insertProperty(QCMakeProperty::STRING, "CMAKE_SYSTEM_NAME", + tr("CMake System Name"), systemName, false); + QString systemVersion = dialog.getSystemVersion(); + m->insertProperty(QCMakeProperty::STRING, "CMAKE_SYSTEM_VERSION", + tr("CMake System Version"), systemVersion, false); + QString cxxCompiler = dialog.getCXXCompiler(); + m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_CXX_COMPILER", + tr("CXX compiler."), cxxCompiler, false); + QString cCompiler = dialog.getCCompiler(); + m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_C_COMPILER", + tr("C compiler."), cCompiler, false); + } else if (dialog.crossCompilerToolChainFile()) { + QString toolchainFile = dialog.getCrossCompilerToolChainFile(); + m->insertProperty(QCMakeProperty::FILEPATH, "CMAKE_TOOLCHAIN_FILE", + tr("Cross Compile ToolChain File"), toolchainFile, + false); + } + return true; + } + + return false; +} + +void CMakeSetupDialog::updateGeneratorLabel(const QString& gen) +{ + QString str = tr("Current Generator: "); + if (gen.isEmpty()) { + str += tr("None"); + } else { + str += gen; + } + this->Generator->setText(str); +} + +void CMakeSetupDialog::doReloadCache() +{ + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "reloadCache", + Qt::QueuedConnection); +} + +void CMakeSetupDialog::doDeleteCache() +{ + QString title = tr("Delete Cache"); + QString msg = tr("Are you sure you want to delete the cache?"); + QMessageBox::StandardButton btn; + btn = QMessageBox::information(this, title, msg, + QMessageBox::Yes | QMessageBox::No); + if (btn == QMessageBox::No) { + return; + } + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), "deleteCache", + Qt::QueuedConnection); +} + +void CMakeSetupDialog::doAbout() +{ + QString msg = tr( + "CMake %1 (cmake.org).\n" + "CMake suite maintained and supported by Kitware (kitware.com/cmake).\n" + "Distributed under terms of the BSD 3-Clause License.\n" + "\n" + "CMake GUI maintained by csimsoft,\n" + "built using Qt %2 (qt-project.org).\n" +#ifdef USE_LGPL + "\n" + "The Qt Toolkit is Copyright (C) Digia Plc and/or its subsidiary(-ies).\n" + "Qt is licensed under terms of the GNU LGPLv" USE_LGPL ", available at:\n" + " \"%3\"" +#endif + ); + msg = msg.arg(cmVersion::GetCMakeVersion()); + msg = msg.arg(qVersion()); +#ifdef USE_LGPL + std::string lgpl = + cmSystemTools::GetCMakeRoot() + "/Licenses/LGPLv" USE_LGPL ".txt"; + msg = msg.arg(lgpl.c_str()); +#endif + + QDialog dialog; + dialog.setWindowTitle(tr("About")); + QVBoxLayout* l = new QVBoxLayout(&dialog); + QLabel* lab = new QLabel(&dialog); + l->addWidget(lab); + lab->setText(msg); + lab->setWordWrap(true); + QDialogButtonBox* btns = + new QDialogButtonBox(QDialogButtonBox::Ok, Qt::Horizontal, &dialog); + QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept())); + l->addWidget(btns); + dialog.exec(); +} + +void CMakeSetupDialog::setExitAfterGenerate(bool b) +{ + this->ExitAfterGenerate = b; +} + +void CMakeSetupDialog::addBinaryPath(const QString& path) +{ + QString cleanpath = QDir::cleanPath(path); + + // update UI + this->BinaryDirectory->blockSignals(true); + int idx = this->BinaryDirectory->findText(cleanpath); + if (idx != -1) { + this->BinaryDirectory->removeItem(idx); + } + this->BinaryDirectory->insertItem(0, cleanpath); + this->BinaryDirectory->setCurrentIndex(0); + this->BinaryDirectory->blockSignals(false); + + // save to registry + QStringList buildPaths = this->loadBuildPaths(); + buildPaths.removeAll(cleanpath); + buildPaths.prepend(cleanpath); + this->saveBuildPaths(buildPaths); +} + +void CMakeSetupDialog::dragEnterEvent(QDragEnterEvent* e) +{ + if (!(this->CurrentState == ReadyConfigure || + this->CurrentState == ReadyGenerate)) { + e->ignore(); + return; + } + + const QMimeData* dat = e->mimeData(); + QList<QUrl> urls = dat->urls(); + QString file = urls.count() ? urls[0].toLocalFile() : QString(); + if (!file.isEmpty() && + (file.endsWith("CMakeCache.txt", Qt::CaseInsensitive) || + file.endsWith("CMakeLists.txt", Qt::CaseInsensitive))) { + e->accept(); + } else { + e->ignore(); + } +} + +void CMakeSetupDialog::dropEvent(QDropEvent* e) +{ + if (!(this->CurrentState == ReadyConfigure || + this->CurrentState == ReadyGenerate)) { + return; + } + + const QMimeData* dat = e->mimeData(); + QList<QUrl> urls = dat->urls(); + QString file = urls.count() ? urls[0].toLocalFile() : QString(); + if (file.endsWith("CMakeCache.txt", Qt::CaseInsensitive)) { + QFileInfo info(file); + if (this->CMakeThread->cmakeInstance()->binaryDirectory() != + info.absolutePath()) { + this->setBinaryDirectory(info.absolutePath()); + } + } else if (file.endsWith("CMakeLists.txt", Qt::CaseInsensitive)) { + QFileInfo info(file); + if (this->CMakeThread->cmakeInstance()->binaryDirectory() != + info.absolutePath()) { + this->setSourceDirectory(info.absolutePath()); + this->setBinaryDirectory(info.absolutePath()); + } + } +} + +QStringList CMakeSetupDialog::loadBuildPaths() +{ + QSettings settings; + settings.beginGroup("Settings/StartPath"); + + QStringList buildPaths; + for (int i = 0; i < 10; i++) { + QString p = settings.value(QString("WhereBuild%1").arg(i)).toString(); + if (!p.isEmpty()) { + buildPaths.append(p); + } + } + return buildPaths; +} + +void CMakeSetupDialog::saveBuildPaths(const QStringList& paths) +{ + QSettings settings; + settings.beginGroup("Settings/StartPath"); + + int num = paths.count(); + if (num > 10) { + num = 10; + } + + for (int i = 0; i < num; i++) { + settings.setValue(QString("WhereBuild%1").arg(i), paths[i]); + } +} + +void CMakeSetupDialog::setCacheModified() +{ + this->CacheModified = true; + this->ConfigureNeeded = true; + this->enterState(ReadyConfigure); +} + +void CMakeSetupDialog::removeSelectedCacheEntries() +{ + QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows(); + QList<QPersistentModelIndex> pidxs; + foreach (QModelIndex i, idxs) { + pidxs.append(i); + } + foreach (QPersistentModelIndex pi, pidxs) { + this->CacheValues->model()->removeRow(pi.row(), pi.parent()); + } +} + +void CMakeSetupDialog::selectionChanged() +{ + QModelIndexList idxs = this->CacheValues->selectionModel()->selectedRows(); + if (idxs.count() && (this->CurrentState == ReadyConfigure || + this->CurrentState == ReadyGenerate)) { + this->RemoveEntry->setEnabled(true); + } else { + this->RemoveEntry->setEnabled(false); + } +} + +void CMakeSetupDialog::enterState(CMakeSetupDialog::State s) +{ + if (s == this->CurrentState) { + return; + } + + this->CurrentState = s; + + if (s == Interrupting) { + this->ConfigureButton->setEnabled(false); + this->GenerateButton->setEnabled(false); + this->OpenProjectButton->setEnabled(false); + } else if (s == Configuring) { + this->setEnabledState(false); + this->GenerateButton->setEnabled(false); + this->GenerateAction->setEnabled(false); + this->OpenProjectButton->setEnabled(false); + this->ConfigureButton->setText(tr("&Stop")); + } else if (s == Generating) { + this->CacheModified = false; + this->setEnabledState(false); + this->ConfigureButton->setEnabled(false); + this->GenerateAction->setEnabled(false); + this->OpenProjectButton->setEnabled(false); + this->GenerateButton->setText(tr("&Stop")); + } else if (s == ReadyConfigure) { + this->setEnabledState(true); + this->GenerateButton->setEnabled(true); + this->GenerateAction->setEnabled(true); + this->ConfigureButton->setEnabled(true); + if (!this->getProjectFilename().isEmpty()) { + this->OpenProjectButton->setEnabled(true); + } + this->ConfigureButton->setText(tr("&Configure")); + this->GenerateButton->setText(tr("&Generate")); + } else if (s == ReadyGenerate) { + this->setEnabledState(true); + this->GenerateButton->setEnabled(true); + this->GenerateAction->setEnabled(true); + this->ConfigureButton->setEnabled(true); + if (!this->getProjectFilename().isEmpty()) { + this->OpenProjectButton->setEnabled(true); + } + this->ConfigureButton->setText(tr("&Configure")); + this->GenerateButton->setText(tr("&Generate")); + } +} + +void CMakeSetupDialog::addCacheEntry() +{ + QDialog dialog(this); + dialog.resize(400, 200); + dialog.setWindowTitle(tr("Add Cache Entry")); + QVBoxLayout* l = new QVBoxLayout(&dialog); + AddCacheEntry* w = + new AddCacheEntry(&dialog, this->AddVariableNames, this->AddVariableTypes); + QDialogButtonBox* btns = new QDialogButtonBox( + QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal, &dialog); + QObject::connect(btns, SIGNAL(accepted()), &dialog, SLOT(accept())); + QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(reject())); + l->addWidget(w); + l->addStretch(); + l->addWidget(btns); + if (QDialog::Accepted == dialog.exec()) { + QCMakeCacheModel* m = this->CacheValues->cacheModel(); + m->insertProperty(w->type(), w->name(), w->description(), w->value(), + false); + + // only add variable names to the completion which are new + if (!this->AddVariableNames.contains(w->name())) { + this->AddVariableNames << w->name(); + this->AddVariableTypes << w->typeString(); + // limit to at most 100 completion items + if (this->AddVariableNames.size() > 100) { + this->AddVariableNames.removeFirst(); + this->AddVariableTypes.removeFirst(); + } + // make sure CMAKE_INSTALL_PREFIX is always there + if (!this->AddVariableNames.contains("CMAKE_INSTALL_PREFIX")) { + this->AddVariableNames << "CMAKE_INSTALL_PREFIX"; + this->AddVariableTypes << "PATH"; + } + QSettings settings; + settings.beginGroup("Settings/StartPath"); + settings.setValue("AddVariableNames", this->AddVariableNames); + settings.setValue("AddVariableTypes", this->AddVariableTypes); + } + } +} + +void CMakeSetupDialog::startSearch() +{ + this->Search->setFocus(Qt::OtherFocusReason); + this->Search->selectAll(); +} + +void CMakeSetupDialog::setDebugOutput(bool flag) +{ + QMetaObject::invokeMethod(this->CMakeThread->cmakeInstance(), + "setDebugOutput", Qt::QueuedConnection, + Q_ARG(bool, flag)); +} + +void CMakeSetupDialog::setGroupedView(bool v) +{ + this->CacheValues->cacheModel()->setViewType(v ? QCMakeCacheModel::GroupView + : QCMakeCacheModel::FlatView); + this->CacheValues->setRootIsDecorated(v); + + QSettings settings; + settings.beginGroup("Settings/StartPath"); + settings.setValue("GroupView", v); +} + +void CMakeSetupDialog::setAdvancedView(bool v) +{ + this->CacheValues->setShowAdvanced(v); + QSettings settings; + settings.beginGroup("Settings/StartPath"); + settings.setValue("AdvancedView", v); +} + +void CMakeSetupDialog::showUserChanges() +{ + QSet<QCMakeProperty> changes = + qobject_cast<QCMakeCacheModelDelegate*>(this->CacheValues->itemDelegate()) + ->changes(); + + QDialog dialog(this); + dialog.setWindowTitle(tr("My Changes")); + dialog.resize(600, 400); + QVBoxLayout* l = new QVBoxLayout(&dialog); + QTextEdit* textedit = new QTextEdit(&dialog); + textedit->setReadOnly(true); + l->addWidget(textedit); + QDialogButtonBox* btns = + new QDialogButtonBox(QDialogButtonBox::Close, Qt::Horizontal, &dialog); + QObject::connect(btns, SIGNAL(rejected()), &dialog, SLOT(accept())); + l->addWidget(btns); + + QString command; + QString cache; + + foreach (QCMakeProperty prop, changes) { + QString type; + switch (prop.Type) { + case QCMakeProperty::BOOL: + type = "BOOL"; + break; + case QCMakeProperty::PATH: + type = "PATH"; + break; + case QCMakeProperty::FILEPATH: + type = "FILEPATH"; + break; + case QCMakeProperty::STRING: + type = "STRING"; + break; + } + QString value; + if (prop.Type == QCMakeProperty::BOOL) { + value = prop.Value.toBool() ? "1" : "0"; + } else { + value = prop.Value.toString(); + } + + QString line("%1:%2="); + line = line.arg(prop.Key); + line = line.arg(type); + + command += QString("-D%1\"%2\" ").arg(line).arg(value); + cache += QString("%1%2\n").arg(line).arg(value); + } + + textedit->append(tr("Commandline options:")); + textedit->append(command); + textedit->append("\n"); + textedit->append(tr("Cache file:")); + textedit->append(cache); + + dialog.exec(); +} + +void CMakeSetupDialog::setSearchFilter(const QString& str) +{ + this->CacheValues->selectionModel()->clear(); + this->CacheValues->setSearchFilter(str); +} + +void CMakeSetupDialog::doOutputContextMenu(const QPoint& pt) +{ + QMenu* menu = this->Output->createStandardContextMenu(); + + menu->addSeparator(); + menu->addAction(tr("Find..."), this, SLOT(doOutputFindDialog()), + QKeySequence::Find); + menu->addAction(tr("Find Next"), this, SLOT(doOutputFindNext()), + QKeySequence::FindNext); + menu->addAction(tr("Find Previous"), this, SLOT(doOutputFindPrev()), + QKeySequence::FindPrevious); + menu->addSeparator(); + menu->addAction(tr("Goto Next Error"), this, SLOT(doOutputErrorNext()), + QKeySequence(Qt::Key_F8)); + + menu->exec(this->Output->mapToGlobal(pt)); + delete menu; +} + +void CMakeSetupDialog::doOutputFindDialog() +{ + QStringList strings(this->FindHistory); + + QString selection = this->Output->textCursor().selectedText(); + if (!selection.isEmpty() && !selection.contains(QChar::ParagraphSeparator) && + !selection.contains(QChar::LineSeparator)) { + strings.push_front(selection); + } + + bool ok; + QString search = QInputDialog::getItem(this, tr("Find in Output"), + tr("Find:"), strings, 0, true, &ok); + if (ok && !search.isEmpty()) { + if (!this->FindHistory.contains(search)) { + this->FindHistory.push_front(search); + } + doOutputFindNext(); + } +} + +void CMakeSetupDialog::doRegexExplorerDialog() +{ + RegexExplorer dialog(this); + dialog.exec(); +} + +void CMakeSetupDialog::doOutputFindPrev() +{ + doOutputFindNext(false); +} + +void CMakeSetupDialog::doOutputFindNext(bool directionForward) +{ + if (this->FindHistory.isEmpty()) { + doOutputFindDialog(); // will re-call this function again + return; + } + + QString search = this->FindHistory.front(); + + QTextCursor textCursor = this->Output->textCursor(); + QTextDocument* document = this->Output->document(); + QTextDocument::FindFlags flags; + if (!directionForward) { + flags |= QTextDocument::FindBackward; + } + + textCursor = document->find(search, textCursor, flags); + + if (textCursor.isNull()) { + // first search found nothing, wrap around and search again + textCursor = this->Output->textCursor(); + textCursor.movePosition(directionForward ? QTextCursor::Start + : QTextCursor::End); + textCursor = document->find(search, textCursor, flags); + } + + if (textCursor.hasSelection()) { + this->Output->setTextCursor(textCursor); + } +} + +void CMakeSetupDialog::doOutputErrorNext() +{ + QTextCursor textCursor = this->Output->textCursor(); + bool atEnd = false; + + // move cursor out of current error-block + if (textCursor.blockCharFormat() == this->ErrorFormat) { + atEnd = !textCursor.movePosition(QTextCursor::NextBlock); + } + + // move cursor to next error-block + while (textCursor.blockCharFormat() != this->ErrorFormat && !atEnd) { + atEnd = !textCursor.movePosition(QTextCursor::NextBlock); + } + + if (atEnd) { + // first search found nothing, wrap around and search again + atEnd = !textCursor.movePosition(QTextCursor::Start); + + // move cursor to next error-block + while (textCursor.blockCharFormat() != this->ErrorFormat && !atEnd) { + atEnd = !textCursor.movePosition(QTextCursor::NextBlock); + } + } + + if (!atEnd) { + textCursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor); + + QTextCharFormat selectionFormat; + selectionFormat.setBackground(Qt::yellow); + QTextEdit::ExtraSelection extraSelection = { textCursor, selectionFormat }; + this->Output->setExtraSelections(QList<QTextEdit::ExtraSelection>() + << extraSelection); + + // make the whole error-block visible + this->Output->setTextCursor(textCursor); + + // remove the selection to see the extraSelection + textCursor.setPosition(textCursor.anchor()); + this->Output->setTextCursor(textCursor); + } +} + +void CMakeSetupDialog::doWarningMessagesDialog() +{ + WarningMessagesDialog dialog(this, this->CMakeThread->cmakeInstance()); + dialog.exec(); +} diff --git a/Source/QtDialog/CMakeSetupDialog.h b/Source/QtDialog/CMakeSetupDialog.h new file mode 100644 index 0000000..1abdb46 --- /dev/null +++ b/Source/QtDialog/CMakeSetupDialog.h @@ -0,0 +1,140 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef CMakeSetupDialog_h +#define CMakeSetupDialog_h + +#include "QCMake.h" + +#include "ui_CMakeSetupDialog.h" +#include <QEventLoop> +#include <QMainWindow> +#include <QThread> + +class QCMakeThread; +class CMakeCacheModel; +class QProgressBar; +class QToolButton; + +/// Qt user interface for CMake +class CMakeSetupDialog : public QMainWindow, public Ui::CMakeSetupDialog +{ + Q_OBJECT +public: + CMakeSetupDialog(); + ~CMakeSetupDialog(); + +public slots: + void setBinaryDirectory(const QString& dir); + void setSourceDirectory(const QString& dir); + +protected slots: + void initialize(); + void doConfigure(); + void doGenerate(); + QString getProjectFilename(); + void doOpenProject(); + void doInstallForCommandLine(); + void doHelp(); + void doAbout(); + void doInterrupt(); + void error(const QString& message); + void message(const QString& message); + + void doSourceBrowse(); + void doBinaryBrowse(); + void doReloadCache(); + void doDeleteCache(); + void updateSourceDirectory(const QString& dir); + void updateBinaryDirectory(const QString& dir); + void showProgress(const QString& msg, float percent); + void setEnabledState(bool); + bool setupFirstConfigure(); + void updateGeneratorLabel(const QString& gen); + void setExitAfterGenerate(bool); + void addBinaryPath(const QString&); + QStringList loadBuildPaths(); + void saveBuildPaths(const QStringList&); + void onBinaryDirectoryChanged(const QString& dir); + void onSourceDirectoryChanged(const QString& dir); + void setCacheModified(); + void removeSelectedCacheEntries(); + void selectionChanged(); + void addCacheEntry(); + void startSearch(); + void setDebugOutput(bool); + void setAdvancedView(bool); + void setGroupedView(bool); + void showUserChanges(); + void setSearchFilter(const QString& str); + bool prepareConfigure(); + bool doConfigureInternal(); + bool doGenerateInternal(); + void exitLoop(int); + void doOutputContextMenu(const QPoint&); + void doOutputFindDialog(); + void doOutputFindNext(bool directionForward = true); + void doOutputFindPrev(); + void doOutputErrorNext(); + void doRegexExplorerDialog(); + /// display the modal warning messages dialog window + void doWarningMessagesDialog(); + +protected: + enum State + { + Interrupting, + ReadyConfigure, + ReadyGenerate, + Configuring, + Generating + }; + void enterState(State s); + + void closeEvent(QCloseEvent*); + void dragEnterEvent(QDragEnterEvent*); + void dropEvent(QDropEvent*); + + QCMakeThread* CMakeThread; + bool ExitAfterGenerate; + bool CacheModified; + bool ConfigureNeeded; + QAction* ReloadCacheAction; + QAction* DeleteCacheAction; + QAction* ExitAction; + QAction* ConfigureAction; + QAction* GenerateAction; + QAction* WarnUninitializedAction; + QAction* WarnUnusedAction; + QAction* InstallForCommandLineAction; + State CurrentState; + + QTextCharFormat ErrorFormat; + QTextCharFormat MessageFormat; + + QStringList AddVariableNames; + QStringList AddVariableTypes; + QStringList FindHistory; + + QEventLoop LocalLoop; + + float ProgressOffset; + float ProgressFactor; +}; + +// QCMake instance on a thread +class QCMakeThread : public QThread +{ + Q_OBJECT +public: + QCMakeThread(QObject* p); + QCMake* cmakeInstance() const; + +signals: + void cmakeInitialized(); + +protected: + virtual void run(); + QCMake* CMakeInstance; +}; + +#endif // CMakeSetupDialog_h diff --git a/Source/QtDialog/CMakeSetupDialog.ui b/Source/QtDialog/CMakeSetupDialog.ui new file mode 100644 index 0000000..8d8e0cd --- /dev/null +++ b/Source/QtDialog/CMakeSetupDialog.ui @@ -0,0 +1,320 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>CMakeSetupDialog</class> + <widget class="QWidget" name="CMakeSetupDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>707</width> + <height>582</height> + </rect> + </property> + <layout class="QGridLayout"> + <property name="margin"> + <number>9</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="0" column="0"> + <layout class="QGridLayout"> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>6</number> + </property> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Where is the source code:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="SourceDirectory"/> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="BrowseSourceDirectoryButton"> + <property name="text"> + <string>Browse &Source...</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Where to build the binaries:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="BinaryDirectory"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Ignored" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="editable"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QPushButton" name="BrowseBinaryDirectoryButton"> + <property name="text"> + <string>Browse &Build...</string> + </property> + </widget> + </item> + </layout> + </item> + <item row="1" column="0"> + <widget class="QSplitter" name="Splitter"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <widget class="QFrame" name="frame"> + <property name="frameShape"> + <enum>QFrame::NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>QFrame::Raised</enum> + </property> + <layout class="QVBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QLabel" name="label_4"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>S&earch:</string> + </property> + <property name="buddy"> + <cstring>Search</cstring> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="Search"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Minimum</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>12</width> + <height>23</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QCheckBox" name="groupedCheck"> + <property name="text"> + <string>Grouped</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="advancedCheck"> + <property name="text"> + <string>Advanced</string> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="AddEntry"> + <property name="toolTip"> + <string>Add New Entry</string> + </property> + <property name="text"> + <string>&Add Entry</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/Plus16.png</normaloff>:/Icons/Plus16.png</iconset> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + <item> + <widget class="QToolButton" name="RemoveEntry"> + <property name="toolTip"> + <string>Remove Selected Entries</string> + </property> + <property name="text"> + <string>&Remove Entry</string> + </property> + <property name="icon"> + <iconset resource="CMakeSetup.qrc"> + <normaloff>:/Icons/Delete16.png</normaloff>:/Icons/Delete16.png</iconset> + </property> + <property name="toolButtonStyle"> + <enum>Qt::ToolButtonTextBesideIcon</enum> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QCMakeCacheView" name="CacheValues"> + <property name="alternatingRowColors"> + <bool>true</bool> + </property> + <property name="selectionMode"> + <enum>QAbstractItemView::ExtendedSelection</enum> + </property> + <property name="selectionBehavior"> + <enum>QAbstractItemView::SelectRows</enum> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Press Configure to update and display new values in red, then press Generate to generate selected build files.</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout"> + <property name="spacing"> + <number>6</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <item> + <widget class="QPushButton" name="ConfigureButton"> + <property name="text"> + <string>&Configure</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="GenerateButton"> + <property name="text"> + <string>&Generate</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="OpenProjectButton"> + <property name="text"> + <string>Open &Project</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="Generator"> + <property name="text"> + <string>Current Generator:</string> + </property> + </widget> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Expanding</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>121</width> + <height>27</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QProgressBar" name="ProgressBar"> + <property name="minimum"> + <number>0</number> + </property> + <property name="maximum"> + <number>100</number> + </property> + <property name="value"> + <number>0</number> + </property> + <property name="textVisible"> + <bool>false</bool> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="textDirection"> + <enum>QProgressBar::BottomToTop</enum> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <widget class="QTextEdit" name="Output"> + <property name="lineWrapMode"> + <enum>QTextEdit::NoWrap</enum> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QCMakeCacheView</class> + <extends>QTreeView</extends> + <header>QCMakeCacheView.h</header> + </customwidget> + </customwidgets> + <resources> + <include location="CMakeSetup.qrc"/> + </resources> + <connections/> +</ui> diff --git a/Source/QtDialog/Compilers.h b/Source/QtDialog/Compilers.h new file mode 100644 index 0000000..276e2a5 --- /dev/null +++ b/Source/QtDialog/Compilers.h @@ -0,0 +1,23 @@ + + +#ifndef COMPILERS_HPP +#define COMPILERS_HPP + +#include <cmConfigure.h> + +#include <QWidget> + +#include <ui_Compilers.h> + +class Compilers : public QWidget, public Ui::Compilers +{ + Q_OBJECT +public: + Compilers(QWidget* p = CM_NULLPTR) + : QWidget(p) + { + this->setupUi(this); + } +}; + +#endif diff --git a/Source/QtDialog/Compilers.ui b/Source/QtDialog/Compilers.ui new file mode 100644 index 0000000..41f70ac --- /dev/null +++ b/Source/QtDialog/Compilers.ui @@ -0,0 +1,87 @@ +<ui version="4.0" > + <class>Compilers</class> + <widget class="QWidget" name="Compilers" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>506</width> + <height>115</height> + </rect> + </property> + <property name="windowTitle" > + <string>Form</string> + </property> + <layout class="QGridLayout" > + <property name="leftMargin" > + <number>0</number> + </property> + <property name="topMargin" > + <number>0</number> + </property> + <property name="rightMargin" > + <number>0</number> + </property> + <property name="bottomMargin" > + <number>0</number> + </property> + <item row="0" column="0" > + <widget class="QGroupBox" name="groupBox_4" > + <property name="title" > + <string>Compilers</string> + </property> + <layout class="QGridLayout" > + <property name="leftMargin" > + <number>4</number> + </property> + <property name="topMargin" > + <number>4</number> + </property> + <property name="rightMargin" > + <number>4</number> + </property> + <item row="0" column="0" > + <widget class="QLabel" name="label_16" > + <property name="text" > + <string>C</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QCMakeFilePathEditor" name="CCompiler" /> + </item> + <item row="0" column="2" > + <widget class="QLabel" name="label_17" > + <property name="text" > + <string>C++</string> + </property> + </widget> + </item> + <item row="0" column="3" > + <widget class="QCMakeFilePathEditor" name="CXXCompiler" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_18" > + <property name="text" > + <string>Fortran</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QCMakeFilePathEditor" name="FortranCompiler" /> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QCMakeFilePathEditor</class> + <extends>QLineEdit</extends> + <header>QCMakeWidgets.h</header> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> diff --git a/Source/QtDialog/CrossCompiler.ui b/Source/QtDialog/CrossCompiler.ui new file mode 100644 index 0000000..1fb1ebf --- /dev/null +++ b/Source/QtDialog/CrossCompiler.ui @@ -0,0 +1,213 @@ +<ui version="4.0" > + <class>CrossCompiler</class> + <widget class="QWidget" name="CrossCompiler" > + <property name="geometry" > + <rect> + <x>0</x> + <y>0</y> + <width>433</width> + <height>319</height> + </rect> + </property> + <property name="windowTitle" > + <string>CrossCompiler</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QGroupBox" name="groupBox" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>Target System</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label_6" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Operating System</string> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QLineEdit" name="systemName" /> + </item> + <item row="0" column="2" colspan="2" > + <spacer> + <property name="orientation" > + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" > + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_10" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Version</string> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QLineEdit" name="systemVersion" /> + </item> + <item row="1" column="2" > + <widget class="QLabel" name="label_11" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Processor</string> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QLineEdit" name="systemProcessor" /> + </item> + </layout> + </widget> + </item> + <item row="2" column="0" > + <widget class="QGroupBox" name="groupBox_2" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Preferred" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title" > + <string>Find Program/Library/Include</string> + </property> + <layout class="QGridLayout" > + <item row="0" column="0" > + <widget class="QLabel" name="label_9" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Target Root</string> + </property> + <property name="wordWrap" > + <bool>true</bool> + </property> + </widget> + </item> + <item row="0" column="1" > + <widget class="QCMakePathEditor" name="crossFindRoot" /> + </item> + <item row="0" column="2" > + <widget class="QLabel" name="label_12" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Program Mode</string> + </property> + </widget> + </item> + <item row="0" column="3" > + <widget class="QComboBox" name="crossProgramMode" /> + </item> + <item row="1" column="0" > + <widget class="QLabel" name="label_13" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Library Mode</string> + </property> + </widget> + </item> + <item row="1" column="1" > + <widget class="QComboBox" name="crossLibraryMode" /> + </item> + <item row="1" column="2" > + <widget class="QLabel" name="label_14" > + <property name="sizePolicy" > + <sizepolicy vsizetype="Fixed" hsizetype="Preferred" > + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text" > + <string>Include Mode</string> + </property> + </widget> + </item> + <item row="1" column="3" > + <widget class="QComboBox" name="crossIncludeMode" /> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" > + <widget class="Compilers" native="1" name="CrossCompilers" > + <property name="focusPolicy" > + <enum>Qt::TabFocus</enum> + </property> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>QCMakePathEditor</class> + <extends>QLineEdit</extends> + <header>QCMakeWidgets.h</header> + </customwidget> + <customwidget> + <class>Compilers</class> + <extends>QWidget</extends> + <header>Compilers.h</header> + <container>1</container> + </customwidget> + </customwidgets> + <tabstops> + <tabstop>systemVersion</tabstop> + <tabstop>systemProcessor</tabstop> + <tabstop>CrossCompilers</tabstop> + <tabstop>crossFindRoot</tabstop> + <tabstop>crossProgramMode</tabstop> + <tabstop>crossLibraryMode</tabstop> + <tabstop>crossIncludeMode</tabstop> + </tabstops> + <resources/> + <connections/> +</ui> diff --git a/Source/QtDialog/Delete16.png b/Source/QtDialog/Delete16.png Binary files differnew file mode 100644 index 0000000..16989fee --- /dev/null +++ b/Source/QtDialog/Delete16.png diff --git a/Source/QtDialog/FirstConfigure.cxx b/Source/QtDialog/FirstConfigure.cxx new file mode 100644 index 0000000..c34751a --- /dev/null +++ b/Source/QtDialog/FirstConfigure.cxx @@ -0,0 +1,581 @@ + +#include "FirstConfigure.h" + +#include "Compilers.h" + +#include <QComboBox> +#include <QRadioButton> +#include <QSettings> +#include <QVBoxLayout> + +StartCompilerSetup::StartCompilerSetup(QWidget* p) + : QWizardPage(p) +{ + QVBoxLayout* l = new QVBoxLayout(this); + l->addWidget(new QLabel(tr("Specify the generator for this project"))); + this->GeneratorOptions = new QComboBox(this); + l->addWidget(this->GeneratorOptions); + + // Add the ability to specify toolset (-T parameter) + ToolsetFrame = CreateToolsetWidgets(); + l->addWidget(ToolsetFrame); + + l->addSpacing(6); + + this->CompilerSetupOptions[0] = + new QRadioButton(tr("Use default native compilers"), this); + this->CompilerSetupOptions[1] = + new QRadioButton(tr("Specify native compilers"), this); + this->CompilerSetupOptions[2] = + new QRadioButton(tr("Specify toolchain file for cross-compiling"), this); + this->CompilerSetupOptions[3] = + new QRadioButton(tr("Specify options for cross-compiling"), this); + l->addWidget(this->CompilerSetupOptions[0]); + l->addWidget(this->CompilerSetupOptions[1]); + l->addWidget(this->CompilerSetupOptions[2]); + l->addWidget(this->CompilerSetupOptions[3]); + + this->CompilerSetupOptions[0]->setChecked(true); + + QObject::connect(this->CompilerSetupOptions[0], SIGNAL(toggled(bool)), this, + SLOT(onSelectionChanged(bool))); + QObject::connect(this->CompilerSetupOptions[1], SIGNAL(toggled(bool)), this, + SLOT(onSelectionChanged(bool))); + QObject::connect(this->CompilerSetupOptions[2], SIGNAL(toggled(bool)), this, + SLOT(onSelectionChanged(bool))); + QObject::connect(this->CompilerSetupOptions[3], SIGNAL(toggled(bool)), this, + SLOT(onSelectionChanged(bool))); + QObject::connect(GeneratorOptions, + SIGNAL(currentIndexChanged(QString const&)), this, + SLOT(onGeneratorChanged(QString const&))); +} + +QFrame* StartCompilerSetup::CreateToolsetWidgets() +{ + QFrame* frame = new QFrame(this); + QVBoxLayout* l = new QVBoxLayout(frame); + l->setContentsMargins(0, 0, 0, 0); + + ToolsetLabel = new QLabel(tr("Optional toolset to use (-T parameter)")); + l->addWidget(ToolsetLabel); + + Toolset = new QLineEdit(frame); + l->addWidget(Toolset); + + return frame; +} + +StartCompilerSetup::~StartCompilerSetup() +{ +} + +void StartCompilerSetup::setGenerators( + std::vector<cmake::GeneratorInfo> const& gens) +{ + this->GeneratorOptions->clear(); + + QStringList generator_list; + + std::vector<cmake::GeneratorInfo>::const_iterator it; + for (it = gens.begin(); it != gens.end(); ++it) { + generator_list.append(QString::fromLocal8Bit(it->name.c_str())); + + if (it->supportsToolset) { + this->GeneratorsSupportingToolset.append( + QString::fromLocal8Bit(it->name.c_str())); + } + } + + this->GeneratorOptions->addItems(generator_list); +} + +void StartCompilerSetup::setCurrentGenerator(const QString& gen) +{ + int idx = this->GeneratorOptions->findText(gen); + if (idx != -1) { + this->GeneratorOptions->setCurrentIndex(idx); + } +} + +QString StartCompilerSetup::getGenerator() const +{ + return this->GeneratorOptions->currentText(); +}; + +QString StartCompilerSetup::getToolset() const +{ + return this->Toolset->text(); +}; + +bool StartCompilerSetup::defaultSetup() const +{ + return this->CompilerSetupOptions[0]->isChecked(); +} + +bool StartCompilerSetup::compilerSetup() const +{ + return this->CompilerSetupOptions[1]->isChecked(); +} + +bool StartCompilerSetup::crossCompilerToolChainFile() const +{ + return this->CompilerSetupOptions[2]->isChecked(); +} + +bool StartCompilerSetup::crossCompilerSetup() const +{ + return this->CompilerSetupOptions[3]->isChecked(); +} + +void StartCompilerSetup::onSelectionChanged(bool on) +{ + if (on) { + selectionChanged(); + } +} + +void StartCompilerSetup::onGeneratorChanged(QString const& name) +{ + if (GeneratorsSupportingToolset.contains(name)) { + ToolsetFrame->show(); + } else { + ToolsetFrame->hide(); + } +} + +int StartCompilerSetup::nextId() const +{ + if (compilerSetup()) { + return NativeSetup; + } + if (crossCompilerSetup()) { + return CrossSetup; + } + if (crossCompilerToolChainFile()) { + return ToolchainSetup; + } + return -1; +} + +NativeCompilerSetup::NativeCompilerSetup(QWidget* p) + : QWizardPage(p) +{ + QVBoxLayout* l = new QVBoxLayout(this); + QWidget* c = new QWidget(this); + l->addWidget(c); + this->setupUi(c); +} + +NativeCompilerSetup::~NativeCompilerSetup() +{ +} + +QString NativeCompilerSetup::getCCompiler() const +{ + return this->CCompiler->text(); +} + +void NativeCompilerSetup::setCCompiler(const QString& s) +{ + this->CCompiler->setText(s); +} + +QString NativeCompilerSetup::getCXXCompiler() const +{ + return this->CXXCompiler->text(); +} + +void NativeCompilerSetup::setCXXCompiler(const QString& s) +{ + this->CXXCompiler->setText(s); +} + +QString NativeCompilerSetup::getFortranCompiler() const +{ + return this->FortranCompiler->text(); +} + +void NativeCompilerSetup::setFortranCompiler(const QString& s) +{ + this->FortranCompiler->setText(s); +} + +CrossCompilerSetup::CrossCompilerSetup(QWidget* p) + : QWizardPage(p) +{ + this->setupUi(this); + QWidget::setTabOrder(systemName, systemVersion); + QWidget::setTabOrder(systemVersion, systemProcessor); + QWidget::setTabOrder(systemProcessor, CrossCompilers->CCompiler); + QWidget::setTabOrder(CrossCompilers->CCompiler, CrossCompilers->CXXCompiler); + QWidget::setTabOrder(CrossCompilers->CXXCompiler, + CrossCompilers->FortranCompiler); + QWidget::setTabOrder(CrossCompilers->FortranCompiler, crossFindRoot); + QWidget::setTabOrder(crossFindRoot, crossProgramMode); + QWidget::setTabOrder(crossProgramMode, crossLibraryMode); + QWidget::setTabOrder(crossLibraryMode, crossIncludeMode); + + // fill in combo boxes + QStringList modes; + modes << tr("Search in Target Root, then native system"); + modes << tr("Search only in Target Root"); + modes << tr("Search only in native system"); + crossProgramMode->addItems(modes); + crossLibraryMode->addItems(modes); + crossIncludeMode->addItems(modes); + crossProgramMode->setCurrentIndex(2); + crossLibraryMode->setCurrentIndex(1); + crossIncludeMode->setCurrentIndex(1); + + this->registerField("systemName*", this->systemName); +} + +CrossCompilerSetup::~CrossCompilerSetup() +{ +} + +QString CrossCompilerSetup::getCCompiler() const +{ + return this->CrossCompilers->CCompiler->text(); +} + +void CrossCompilerSetup::setCCompiler(const QString& s) +{ + this->CrossCompilers->CCompiler->setText(s); +} + +QString CrossCompilerSetup::getCXXCompiler() const +{ + return this->CrossCompilers->CXXCompiler->text(); +} + +void CrossCompilerSetup::setCXXCompiler(const QString& s) +{ + this->CrossCompilers->CXXCompiler->setText(s); +} + +QString CrossCompilerSetup::getFortranCompiler() const +{ + return this->CrossCompilers->FortranCompiler->text(); +} + +void CrossCompilerSetup::setFortranCompiler(const QString& s) +{ + this->CrossCompilers->FortranCompiler->setText(s); +} + +QString CrossCompilerSetup::getSystem() const +{ + return this->systemName->text(); +} + +void CrossCompilerSetup::setSystem(const QString& t) +{ + this->systemName->setText(t); +} + +QString CrossCompilerSetup::getVersion() const +{ + return this->systemVersion->text(); +} + +void CrossCompilerSetup::setVersion(const QString& t) +{ + this->systemVersion->setText(t); +} + +QString CrossCompilerSetup::getProcessor() const +{ + return this->systemProcessor->text(); +} + +void CrossCompilerSetup::setProcessor(const QString& t) +{ + this->systemProcessor->setText(t); +} + +QString CrossCompilerSetup::getFindRoot() const +{ + return this->crossFindRoot->text(); +} + +void CrossCompilerSetup::setFindRoot(const QString& t) +{ + return this->crossFindRoot->setText(t); +} + +int CrossCompilerSetup::getProgramMode() const +{ + return this->crossProgramMode->currentIndex(); +} + +int CrossCompilerSetup::getLibraryMode() const +{ + return this->crossLibraryMode->currentIndex(); +} + +int CrossCompilerSetup::getIncludeMode() const +{ + return this->crossIncludeMode->currentIndex(); +} + +void CrossCompilerSetup::setProgramMode(int m) +{ + this->crossProgramMode->setCurrentIndex(m); +} + +void CrossCompilerSetup::setLibraryMode(int m) +{ + this->crossLibraryMode->setCurrentIndex(m); +} + +void CrossCompilerSetup::setIncludeMode(int m) +{ + this->crossIncludeMode->setCurrentIndex(m); +} + +ToolchainCompilerSetup::ToolchainCompilerSetup(QWidget* p) + : QWizardPage(p) +{ + QVBoxLayout* l = new QVBoxLayout(this); + l->addWidget(new QLabel(tr("Specify the Toolchain file"))); + this->ToolchainFile = new QCMakeFilePathEditor(this); + l->addWidget(this->ToolchainFile); +} + +ToolchainCompilerSetup::~ToolchainCompilerSetup() +{ +} + +QString ToolchainCompilerSetup::toolchainFile() const +{ + return this->ToolchainFile->text(); +} + +void ToolchainCompilerSetup::setToolchainFile(const QString& t) +{ + this->ToolchainFile->setText(t); +} + +FirstConfigure::FirstConfigure() +{ + // this->setOption(QWizard::HaveFinishButtonOnEarlyPages, true); + this->mStartCompilerSetupPage = new StartCompilerSetup(this); + this->setPage(Start, this->mStartCompilerSetupPage); + QObject::connect(this->mStartCompilerSetupPage, SIGNAL(selectionChanged()), + this, SLOT(restart())); + + this->mNativeCompilerSetupPage = new NativeCompilerSetup(this); + this->setPage(NativeSetup, this->mNativeCompilerSetupPage); + + this->mCrossCompilerSetupPage = new CrossCompilerSetup(this); + this->setPage(CrossSetup, this->mCrossCompilerSetupPage); + + this->mToolchainCompilerSetupPage = new ToolchainCompilerSetup(this); + this->setPage(ToolchainSetup, this->mToolchainCompilerSetupPage); +} + +FirstConfigure::~FirstConfigure() +{ +} + +void FirstConfigure::setGenerators( + std::vector<cmake::GeneratorInfo> const& gens) +{ + this->mStartCompilerSetupPage->setGenerators(gens); +} + +QString FirstConfigure::getGenerator() const +{ + return this->mStartCompilerSetupPage->getGenerator(); +} + +QString FirstConfigure::getToolset() const +{ + return this->mStartCompilerSetupPage->getToolset(); +} + +void FirstConfigure::loadFromSettings() +{ + QSettings settings; + // restore generator + settings.beginGroup("Settings/StartPath"); + QString lastGen = settings.value("LastGenerator").toString(); + this->mStartCompilerSetupPage->setCurrentGenerator(lastGen); + settings.endGroup(); + + // restore compiler setup + settings.beginGroup("Settings/Compiler"); + this->mNativeCompilerSetupPage->setCCompiler( + settings.value("CCompiler").toString()); + this->mNativeCompilerSetupPage->setCXXCompiler( + settings.value("CXXCompiler").toString()); + this->mNativeCompilerSetupPage->setFortranCompiler( + settings.value("FortranCompiler").toString()); + settings.endGroup(); + + // restore cross compiler setup + settings.beginGroup("Settings/CrossCompiler"); + this->mCrossCompilerSetupPage->setCCompiler( + settings.value("CCompiler").toString()); + this->mCrossCompilerSetupPage->setCXXCompiler( + settings.value("CXXCompiler").toString()); + this->mCrossCompilerSetupPage->setFortranCompiler( + settings.value("FortranCompiler").toString()); + this->mToolchainCompilerSetupPage->setToolchainFile( + settings.value("ToolChainFile").toString()); + this->mCrossCompilerSetupPage->setSystem( + settings.value("SystemName").toString()); + this->mCrossCompilerSetupPage->setVersion( + settings.value("SystemVersion").toString()); + this->mCrossCompilerSetupPage->setProcessor( + settings.value("SystemProcessor").toString()); + this->mCrossCompilerSetupPage->setFindRoot( + settings.value("FindRoot").toString()); + this->mCrossCompilerSetupPage->setProgramMode( + settings.value("ProgramMode", 0).toInt()); + this->mCrossCompilerSetupPage->setLibraryMode( + settings.value("LibraryMode", 0).toInt()); + this->mCrossCompilerSetupPage->setIncludeMode( + settings.value("IncludeMode", 0).toInt()); + settings.endGroup(); +} + +void FirstConfigure::saveToSettings() +{ + QSettings settings; + + // save generator + settings.beginGroup("Settings/StartPath"); + QString lastGen = this->mStartCompilerSetupPage->getGenerator(); + settings.setValue("LastGenerator", lastGen); + settings.endGroup(); + + // save compiler setup + settings.beginGroup("Settings/Compiler"); + settings.setValue("CCompiler", + this->mNativeCompilerSetupPage->getCCompiler()); + settings.setValue("CXXCompiler", + this->mNativeCompilerSetupPage->getCXXCompiler()); + settings.setValue("FortranCompiler", + this->mNativeCompilerSetupPage->getFortranCompiler()); + settings.endGroup(); + + // save cross compiler setup + settings.beginGroup("Settings/CrossCompiler"); + settings.setValue("CCompiler", + this->mCrossCompilerSetupPage->getCCompiler()); + settings.setValue("CXXCompiler", + this->mCrossCompilerSetupPage->getCXXCompiler()); + settings.setValue("FortranCompiler", + this->mCrossCompilerSetupPage->getFortranCompiler()); + settings.setValue("ToolChainFile", this->getCrossCompilerToolChainFile()); + settings.setValue("SystemName", this->mCrossCompilerSetupPage->getSystem()); + settings.setValue("SystemVersion", + this->mCrossCompilerSetupPage->getVersion()); + settings.setValue("SystemProcessor", + this->mCrossCompilerSetupPage->getProcessor()); + settings.setValue("FindRoot", this->mCrossCompilerSetupPage->getFindRoot()); + settings.setValue("ProgramMode", + this->mCrossCompilerSetupPage->getProgramMode()); + settings.setValue("LibraryMode", + this->mCrossCompilerSetupPage->getLibraryMode()); + settings.setValue("IncludeMode", + this->mCrossCompilerSetupPage->getIncludeMode()); + settings.endGroup(); +} + +bool FirstConfigure::defaultSetup() const +{ + return this->mStartCompilerSetupPage->defaultSetup(); +} + +bool FirstConfigure::compilerSetup() const +{ + return this->mStartCompilerSetupPage->compilerSetup(); +} + +bool FirstConfigure::crossCompilerSetup() const +{ + return this->mStartCompilerSetupPage->crossCompilerSetup(); +} + +bool FirstConfigure::crossCompilerToolChainFile() const +{ + return this->mStartCompilerSetupPage->crossCompilerToolChainFile(); +} + +QString FirstConfigure::getCrossCompilerToolChainFile() const +{ + return this->mToolchainCompilerSetupPage->toolchainFile(); +} + +QString FirstConfigure::getSystemName() const +{ + return this->mCrossCompilerSetupPage->getSystem(); +} + +QString FirstConfigure::getCCompiler() const +{ + if (this->compilerSetup()) { + return this->mNativeCompilerSetupPage->getCCompiler(); + } + if (this->crossCompilerSetup()) { + return this->mCrossCompilerSetupPage->getCCompiler(); + } + return QString(); +} + +QString FirstConfigure::getCXXCompiler() const +{ + if (this->compilerSetup()) { + return this->mNativeCompilerSetupPage->getCXXCompiler(); + } + if (this->crossCompilerSetup()) { + return this->mCrossCompilerSetupPage->getCXXCompiler(); + } + return QString(); +} + +QString FirstConfigure::getFortranCompiler() const +{ + if (this->compilerSetup()) { + return this->mNativeCompilerSetupPage->getFortranCompiler(); + } + if (this->crossCompilerSetup()) { + return this->mCrossCompilerSetupPage->getFortranCompiler(); + } + return QString(); +} + +QString FirstConfigure::getSystemVersion() const +{ + return this->mCrossCompilerSetupPage->getVersion(); +} + +QString FirstConfigure::getSystemProcessor() const +{ + return this->mCrossCompilerSetupPage->getProcessor(); +} + +QString FirstConfigure::getCrossRoot() const +{ + return this->mCrossCompilerSetupPage->getFindRoot(); +} + +const QString CrossModes[] = { "BOTH", "ONLY", "NEVER" }; + +QString FirstConfigure::getCrossProgramMode() const +{ + return CrossModes[this->mCrossCompilerSetupPage->getProgramMode()]; +} + +QString FirstConfigure::getCrossLibraryMode() const +{ + return CrossModes[this->mCrossCompilerSetupPage->getLibraryMode()]; +} + +QString FirstConfigure::getCrossIncludeMode() const +{ + return CrossModes[this->mCrossCompilerSetupPage->getIncludeMode()]; +} diff --git a/Source/QtDialog/FirstConfigure.h b/Source/QtDialog/FirstConfigure.h new file mode 100644 index 0000000..c467ddb --- /dev/null +++ b/Source/QtDialog/FirstConfigure.h @@ -0,0 +1,189 @@ + +#ifndef FirstConfigure_h +#define FirstConfigure_h + +#include <QWizard> +#include <QWizardPage> + +#include "cmake.h" + +#include "ui_Compilers.h" +#include "ui_CrossCompiler.h" + +class QRadioButton; +class QComboBox; + +//! the wizard pages we'll use for the first configure of a build +enum FirstConfigurePages +{ + Start, + NativeSetup, + ToolchainSetup, + CrossSetup, + Done +}; + +//! the first page that gives basic options for what compilers setup to choose +//! from +class StartCompilerSetup : public QWizardPage +{ + Q_OBJECT +public: + StartCompilerSetup(QWidget* p); + ~StartCompilerSetup(); + void setGenerators(std::vector<cmake::GeneratorInfo> const& gens); + void setCurrentGenerator(const QString& gen); + QString getGenerator() const; + QString getToolset() const; + + bool defaultSetup() const; + bool compilerSetup() const; + bool crossCompilerSetup() const; + bool crossCompilerToolChainFile() const; + + int nextId() const; + +signals: + void selectionChanged(); + +protected slots: + void onSelectionChanged(bool); + void onGeneratorChanged(QString const& name); + +protected: + QComboBox* GeneratorOptions; + QRadioButton* CompilerSetupOptions[4]; + QFrame* ToolsetFrame; + QLineEdit* Toolset; + QLabel* ToolsetLabel; + QStringList GeneratorsSupportingToolset; + +private: + QFrame* CreateToolsetWidgets(); +}; + +//! the page that gives basic options for native compilers +class NativeCompilerSetup : public QWizardPage, protected Ui::Compilers +{ + Q_OBJECT +public: + NativeCompilerSetup(QWidget* p); + ~NativeCompilerSetup(); + + QString getCCompiler() const; + void setCCompiler(const QString&); + + QString getCXXCompiler() const; + void setCXXCompiler(const QString&); + + QString getFortranCompiler() const; + void setFortranCompiler(const QString&); + + int nextId() const { return -1; } +}; + +//! the page that gives options for cross compilers +class CrossCompilerSetup : public QWizardPage, protected Ui::CrossCompiler +{ + Q_OBJECT +public: + CrossCompilerSetup(QWidget* p); + ~CrossCompilerSetup(); + + QString getSystem() const; + void setSystem(const QString&); + + QString getVersion() const; + void setVersion(const QString&); + + QString getProcessor() const; + void setProcessor(const QString&); + + QString getCCompiler() const; + void setCCompiler(const QString&); + + QString getCXXCompiler() const; + void setCXXCompiler(const QString&); + + QString getFortranCompiler() const; + void setFortranCompiler(const QString&); + + QString getFindRoot() const; + void setFindRoot(const QString&); + + enum CrossMode + { + BOTH, + ONLY, + NEVER + }; + + int getProgramMode() const; + void setProgramMode(int); + int getLibraryMode() const; + void setLibraryMode(int); + int getIncludeMode() const; + void setIncludeMode(int); + + int nextId() const { return -1; } +}; + +//! the page that gives options for a toolchain file +class ToolchainCompilerSetup : public QWizardPage +{ + Q_OBJECT +public: + ToolchainCompilerSetup(QWidget* p); + ~ToolchainCompilerSetup(); + + QString toolchainFile() const; + void setToolchainFile(const QString&); + + int nextId() const { return -1; } + +protected: + QCMakeFilePathEditor* ToolchainFile; +}; + +//! the wizard with the pages +class FirstConfigure : public QWizard +{ + Q_OBJECT +public: + FirstConfigure(); + ~FirstConfigure(); + + void setGenerators(std::vector<cmake::GeneratorInfo> const& gens); + QString getGenerator() const; + QString getToolset() const; + + bool defaultSetup() const; + bool compilerSetup() const; + bool crossCompilerSetup() const; + bool crossCompilerToolChainFile() const; + + QString getCCompiler() const; + QString getCXXCompiler() const; + QString getFortranCompiler() const; + + QString getSystemName() const; + QString getSystemVersion() const; + QString getSystemProcessor() const; + QString getCrossRoot() const; + QString getCrossProgramMode() const; + QString getCrossLibraryMode() const; + QString getCrossIncludeMode() const; + + QString getCrossCompilerToolChainFile() const; + + void loadFromSettings(); + void saveToSettings(); + +protected: + StartCompilerSetup* mStartCompilerSetupPage; + NativeCompilerSetup* mNativeCompilerSetupPage; + CrossCompilerSetup* mCrossCompilerSetupPage; + ToolchainCompilerSetup* mToolchainCompilerSetupPage; +}; + +#endif // FirstConfigure_h diff --git a/Source/QtDialog/Info.plist.in b/Source/QtDialog/Info.plist.in new file mode 100644 index 0000000..00a27c3 --- /dev/null +++ b/Source/QtDialog/Info.plist.in @@ -0,0 +1,34 @@ +<?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> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>CFBundleSignature</key> + <string>????</string> + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CSResourcesFileMapped</key> + <true/> + <key>LSApplicationCategoryType</key> + <string>public.app-category.developer-tools</string> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + <key>NSHighResolutionCapable</key> + <true/> +</dict> +</plist> diff --git a/Source/QtDialog/Plus16.png b/Source/QtDialog/Plus16.png Binary files differnew file mode 100644 index 0000000..552f6f0 --- /dev/null +++ b/Source/QtDialog/Plus16.png diff --git a/Source/QtDialog/QCMake.cxx b/Source/QtDialog/QCMake.cxx new file mode 100644 index 0000000..abeff97 --- /dev/null +++ b/Source/QtDialog/QCMake.cxx @@ -0,0 +1,452 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMake.h" + +#include <QCoreApplication> +#include <QDir> + +#include "cmExternalMakefileProjectGenerator.h" +#include "cmState.h" +#include "cmSystemTools.h" + +#ifdef Q_OS_WIN +#include "qt_windows.h" // For SetErrorMode +#endif + +QCMake::QCMake(QObject* p) + : QObject(p) +{ + this->WarnUninitializedMode = false; + this->WarnUnusedMode = false; + qRegisterMetaType<QCMakeProperty>(); + qRegisterMetaType<QCMakePropertyList>(); + + cmSystemTools::DisableRunCommandOutput(); + cmSystemTools::SetRunCommandHideConsole(true); + cmSystemTools::SetMessageCallback(QCMake::messageCallback, this); + cmSystemTools::SetStdoutCallback(QCMake::stdoutCallback, this); + cmSystemTools::SetStderrCallback(QCMake::stderrCallback, this); + + this->CMakeInstance = new cmake; + this->CMakeInstance->SetCMakeEditCommand( + cmSystemTools::GetCMakeGUICommand()); + this->CMakeInstance->SetProgressCallback(QCMake::progressCallback, this); + + cmSystemTools::SetInterruptCallback(QCMake::interruptCallback, this); + + std::vector<cmake::GeneratorInfo> generators; + this->CMakeInstance->GetRegisteredGenerators(generators); + + std::vector<cmake::GeneratorInfo>::const_iterator it; + for (it = generators.begin(); it != generators.end(); ++it) { + // Skip the generator "KDevelop3", since there is also + // "KDevelop3 - Unix Makefiles", which is the full and official name. + // The short name is actually only still there since this was the name + // in CMake 2.4, to keep "command line argument compatibility", but + // this is not necessary in the GUI. + if (it->name == "KDevelop3") { + continue; + } + + this->AvailableGenerators.push_back(*it); + } +} + +QCMake::~QCMake() +{ + delete this->CMakeInstance; + // cmDynamicLoader::FlushCache(); +} + +void QCMake::loadCache(const QString& dir) +{ + this->setBinaryDirectory(dir); +} + +void QCMake::setSourceDirectory(const QString& _dir) +{ + QString dir = QString::fromLocal8Bit( + cmSystemTools::GetActualCaseForPath(_dir.toLocal8Bit().data()).c_str()); + if (this->SourceDirectory != dir) { + this->SourceDirectory = QDir::fromNativeSeparators(dir); + emit this->sourceDirChanged(this->SourceDirectory); + } +} + +void QCMake::setBinaryDirectory(const QString& _dir) +{ + QString dir = QString::fromLocal8Bit( + cmSystemTools::GetActualCaseForPath(_dir.toLocal8Bit().data()).c_str()); + if (this->BinaryDirectory != dir) { + this->BinaryDirectory = QDir::fromNativeSeparators(dir); + emit this->binaryDirChanged(this->BinaryDirectory); + cmState* state = this->CMakeInstance->GetState(); + this->setGenerator(QString()); + this->setToolset(QString()); + if (!this->CMakeInstance->LoadCache( + this->BinaryDirectory.toLocal8Bit().data())) { + QDir testDir(this->BinaryDirectory); + if (testDir.exists("CMakeCache.txt")) { + cmSystemTools::Error( + "There is a CMakeCache.txt file for the current binary " + "tree but cmake does not have permission to read it. " + "Please check the permissions of the directory you are trying to " + "run CMake on."); + } + } + + QCMakePropertyList props = this->properties(); + emit this->propertiesChanged(props); + const char* homeDir = state->GetCacheEntryValue("CMAKE_HOME_DIRECTORY"); + if (homeDir) { + setSourceDirectory(QString::fromLocal8Bit(homeDir)); + } + const char* gen = state->GetCacheEntryValue("CMAKE_GENERATOR"); + if (gen) { + const char* extraGen = + state->GetInitializedCacheValue("CMAKE_EXTRA_GENERATOR"); + std::string curGen = + cmExternalMakefileProjectGenerator::CreateFullGeneratorName( + gen, extraGen ? extraGen : ""); + this->setGenerator(QString::fromLocal8Bit(curGen.c_str())); + } + + const char* toolset = state->GetCacheEntryValue("CMAKE_GENERATOR_TOOLSET"); + if (toolset) { + this->setToolset(QString::fromLocal8Bit(toolset)); + } + } +} + +void QCMake::setGenerator(const QString& gen) +{ + if (this->Generator != gen) { + this->Generator = gen; + emit this->generatorChanged(this->Generator); + } +} + +void QCMake::setToolset(const QString& toolset) +{ + if (this->Toolset != toolset) { + this->Toolset = toolset; + emit this->toolsetChanged(this->Toolset); + } +} + +void QCMake::configure() +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + this->CMakeInstance->SetHomeDirectory( + this->SourceDirectory.toLocal8Bit().data()); + this->CMakeInstance->SetHomeOutputDirectory( + this->BinaryDirectory.toLocal8Bit().data()); + this->CMakeInstance->SetGlobalGenerator( + this->CMakeInstance->CreateGlobalGenerator( + this->Generator.toLocal8Bit().data())); + this->CMakeInstance->SetGeneratorPlatform(""); + this->CMakeInstance->SetGeneratorToolset(this->Toolset.toLocal8Bit().data()); + this->CMakeInstance->LoadCache(); + this->CMakeInstance->SetWarnUninitialized(this->WarnUninitializedMode); + this->CMakeInstance->SetWarnUnused(this->WarnUnusedMode); + this->CMakeInstance->PreLoadCMakeFiles(); + + InterruptFlag = 0; + cmSystemTools::ResetErrorOccuredFlag(); + + int err = this->CMakeInstance->Configure(); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + emit this->propertiesChanged(this->properties()); + emit this->configureDone(err); +} + +void QCMake::generate() +{ +#ifdef Q_OS_WIN + UINT lastErrorMode = SetErrorMode(0); +#endif + + InterruptFlag = 0; + cmSystemTools::ResetErrorOccuredFlag(); + + int err = this->CMakeInstance->Generate(); + +#ifdef Q_OS_WIN + SetErrorMode(lastErrorMode); +#endif + + emit this->generateDone(err); +} + +void QCMake::setProperties(const QCMakePropertyList& newProps) +{ + QCMakePropertyList props = newProps; + + QStringList toremove; + + // set the value of properties + cmState* state = this->CMakeInstance->GetState(); + std::vector<std::string> cacheKeys = state->GetCacheEntryKeys(); + for (std::vector<std::string>::const_iterator it = cacheKeys.begin(); + it != cacheKeys.end(); ++it) { + cmStateEnums::CacheEntryType t = state->GetCacheEntryType(*it); + if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC) { + continue; + } + + QCMakeProperty prop; + prop.Key = QString::fromLocal8Bit(it->c_str()); + int idx = props.indexOf(prop); + if (idx == -1) { + toremove.append(QString::fromLocal8Bit(it->c_str())); + } else { + prop = props[idx]; + if (prop.Value.type() == QVariant::Bool) { + state->SetCacheEntryValue(*it, prop.Value.toBool() ? "ON" : "OFF"); + } else { + state->SetCacheEntryValue(*it, + prop.Value.toString().toLocal8Bit().data()); + } + props.removeAt(idx); + } + } + + // remove some properites + foreach (QString s, toremove) { + this->CMakeInstance->UnwatchUnusedCli(s.toLocal8Bit().data()); + + state->RemoveCacheEntry(s.toLocal8Bit().data()); + } + + // add some new properites + foreach (QCMakeProperty s, props) { + this->CMakeInstance->WatchUnusedCli(s.Key.toLocal8Bit().data()); + + if (s.Type == QCMakeProperty::BOOL) { + this->CMakeInstance->AddCacheEntry( + s.Key.toLocal8Bit().data(), s.Value.toBool() ? "ON" : "OFF", + s.Help.toLocal8Bit().data(), cmStateEnums::BOOL); + } else if (s.Type == QCMakeProperty::STRING) { + this->CMakeInstance->AddCacheEntry( + s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(), + s.Help.toLocal8Bit().data(), cmStateEnums::STRING); + } else if (s.Type == QCMakeProperty::PATH) { + this->CMakeInstance->AddCacheEntry( + s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(), + s.Help.toLocal8Bit().data(), cmStateEnums::PATH); + } else if (s.Type == QCMakeProperty::FILEPATH) { + this->CMakeInstance->AddCacheEntry( + s.Key.toLocal8Bit().data(), s.Value.toString().toLocal8Bit().data(), + s.Help.toLocal8Bit().data(), cmStateEnums::FILEPATH); + } + } + + this->CMakeInstance->SaveCache(this->BinaryDirectory.toLocal8Bit().data()); +} + +QCMakePropertyList QCMake::properties() const +{ + QCMakePropertyList ret; + + cmState* state = this->CMakeInstance->GetState(); + std::vector<std::string> cacheKeys = state->GetCacheEntryKeys(); + for (std::vector<std::string>::const_iterator i = cacheKeys.begin(); + i != cacheKeys.end(); ++i) { + cmStateEnums::CacheEntryType t = state->GetCacheEntryType(*i); + if (t == cmStateEnums::INTERNAL || t == cmStateEnums::STATIC || + t == cmStateEnums::UNINITIALIZED) { + continue; + } + + const char* cachedValue = state->GetCacheEntryValue(*i); + + QCMakeProperty prop; + prop.Key = QString::fromLocal8Bit(i->c_str()); + prop.Help = + QString::fromLocal8Bit(state->GetCacheEntryProperty(*i, "HELPSTRING")); + prop.Value = QString::fromLocal8Bit(cachedValue); + prop.Advanced = state->GetCacheEntryPropertyAsBool(*i, "ADVANCED"); + if (t == cmStateEnums::BOOL) { + prop.Type = QCMakeProperty::BOOL; + prop.Value = cmSystemTools::IsOn(cachedValue); + } else if (t == cmStateEnums::PATH) { + prop.Type = QCMakeProperty::PATH; + } else if (t == cmStateEnums::FILEPATH) { + prop.Type = QCMakeProperty::FILEPATH; + } else if (t == cmStateEnums::STRING) { + prop.Type = QCMakeProperty::STRING; + const char* stringsProperty = + state->GetCacheEntryProperty(*i, "STRINGS"); + if (stringsProperty) { + prop.Strings = QString::fromLocal8Bit(stringsProperty).split(";"); + } + } + + ret.append(prop); + } + + return ret; +} + +void QCMake::interrupt() +{ + this->InterruptFlag.ref(); +} + +bool QCMake::interruptCallback(void* cd) +{ + QCMake* self = reinterpret_cast<QCMake*>(cd); +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) + return self->InterruptFlag; +#else + return self->InterruptFlag.load(); +#endif +} + +void QCMake::progressCallback(const char* msg, float percent, void* cd) +{ + QCMake* self = reinterpret_cast<QCMake*>(cd); + if (percent >= 0) { + emit self->progressChanged(QString::fromLocal8Bit(msg), percent); + } else { + emit self->outputMessage(QString::fromLocal8Bit(msg)); + } + QCoreApplication::processEvents(); +} + +void QCMake::messageCallback(const char* msg, const char* /*title*/, + bool& /*stop*/, void* cd) +{ + QCMake* self = reinterpret_cast<QCMake*>(cd); + emit self->errorMessage(QString::fromLocal8Bit(msg)); + QCoreApplication::processEvents(); +} + +void QCMake::stdoutCallback(const char* msg, size_t len, void* cd) +{ + QCMake* self = reinterpret_cast<QCMake*>(cd); + emit self->outputMessage(QString::fromLocal8Bit(msg, int(len))); + QCoreApplication::processEvents(); +} + +void QCMake::stderrCallback(const char* msg, size_t len, void* cd) +{ + QCMake* self = reinterpret_cast<QCMake*>(cd); + emit self->outputMessage(QString::fromLocal8Bit(msg, int(len))); + QCoreApplication::processEvents(); +} + +QString QCMake::binaryDirectory() const +{ + return this->BinaryDirectory; +} + +QString QCMake::sourceDirectory() const +{ + return this->SourceDirectory; +} + +QString QCMake::generator() const +{ + return this->Generator; +} + +std::vector<cmake::GeneratorInfo> const& QCMake::availableGenerators() const +{ + return AvailableGenerators; +} + +void QCMake::deleteCache() +{ + // delete cache + this->CMakeInstance->DeleteCache(this->BinaryDirectory.toLocal8Bit().data()); + // reload to make our cache empty + this->CMakeInstance->LoadCache(this->BinaryDirectory.toLocal8Bit().data()); + // emit no generator and no properties + this->setGenerator(QString()); + this->setToolset(QString()); + QCMakePropertyList props = this->properties(); + emit this->propertiesChanged(props); +} + +void QCMake::reloadCache() +{ + // emit that the cache was cleaned out + QCMakePropertyList props; + emit this->propertiesChanged(props); + // reload + this->CMakeInstance->LoadCache(this->BinaryDirectory.toLocal8Bit().data()); + // emit new cache properties + props = this->properties(); + emit this->propertiesChanged(props); +} + +void QCMake::setDebugOutput(bool flag) +{ + if (flag != this->CMakeInstance->GetDebugOutput()) { + this->CMakeInstance->SetDebugOutputOn(flag); + emit this->debugOutputChanged(flag); + } +} + +bool QCMake::getDebugOutput() const +{ + return this->CMakeInstance->GetDebugOutput(); +} + +bool QCMake::getSuppressDevWarnings() +{ + return this->CMakeInstance->GetSuppressDevWarnings(); +} + +void QCMake::setSuppressDevWarnings(bool value) +{ + this->CMakeInstance->SetSuppressDevWarnings(value); +} + +bool QCMake::getSuppressDeprecatedWarnings() +{ + return this->CMakeInstance->GetSuppressDeprecatedWarnings(); +} + +void QCMake::setSuppressDeprecatedWarnings(bool value) +{ + this->CMakeInstance->SetSuppressDeprecatedWarnings(value); +} + +bool QCMake::getDevWarningsAsErrors() +{ + return this->CMakeInstance->GetDevWarningsAsErrors(); +} + +void QCMake::setDevWarningsAsErrors(bool value) +{ + this->CMakeInstance->SetDevWarningsAsErrors(value); +} + +bool QCMake::getDeprecatedWarningsAsErrors() +{ + return this->CMakeInstance->GetDeprecatedWarningsAsErrors(); +} + +void QCMake::setDeprecatedWarningsAsErrors(bool value) +{ + this->CMakeInstance->SetDeprecatedWarningsAsErrors(value); +} + +void QCMake::setWarnUninitializedMode(bool value) +{ + this->WarnUninitializedMode = value; +} + +void QCMake::setWarnUnusedMode(bool value) +{ + this->WarnUnusedMode = value; +} diff --git a/Source/QtDialog/QCMake.h b/Source/QtDialog/QCMake.h new file mode 100644 index 0000000..12f6037 --- /dev/null +++ b/Source/QtDialog/QCMake.h @@ -0,0 +1,176 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef QCMake_h +#define QCMake_h + +#include <cmConfigure.h> + +#include "cmake.h" + +#ifdef _MSC_VER +#pragma warning(disable : 4127) +#pragma warning(disable : 4512) +#endif + +#include <vector> + +#include <QAtomicInt> +#include <QList> +#include <QMetaType> +#include <QObject> +#include <QString> +#include <QStringList> +#include <QVariant> + +/// struct to represent cmake properties in Qt +/// Value is of type String or Bool +struct QCMakeProperty +{ + enum PropertyType + { + BOOL, + PATH, + FILEPATH, + STRING + }; + QString Key; + QVariant Value; + QStringList Strings; + QString Help; + PropertyType Type; + bool Advanced; + bool operator==(const QCMakeProperty& other) const + { + return this->Key == other.Key; + } + bool operator<(const QCMakeProperty& other) const + { + return this->Key < other.Key; + } +}; + +// list of properties +typedef QList<QCMakeProperty> QCMakePropertyList; + +// allow QVariant to be a property or list of properties +Q_DECLARE_METATYPE(QCMakeProperty) +Q_DECLARE_METATYPE(QCMakePropertyList) + +/// Qt API for CMake library. +/// Wrapper like class allows for easier integration with +/// Qt features such as, signal/slot connections, multi-threading, etc.. +class QCMake : public QObject +{ + Q_OBJECT +public: + QCMake(QObject* p = CM_NULLPTR); + ~QCMake(); +public slots: + /// load the cache file in a directory + void loadCache(const QString& dir); + /// set the source directory containing the source + void setSourceDirectory(const QString& dir); + /// set the binary directory to build in + void setBinaryDirectory(const QString& dir); + /// set the desired generator to use + void setGenerator(const QString& generator); + /// set the desired generator to use + void setToolset(const QString& toolset); + /// do the configure step + void configure(); + /// generate the files + void generate(); + /// set the property values + void setProperties(const QCMakePropertyList&); + /// interrupt the configure or generate process (if connecting, make a direct + /// connection) + void interrupt(); + /// delete the cache in binary directory + void deleteCache(); + /// reload the cache in binary directory + void reloadCache(); + /// set whether to do debug output + void setDebugOutput(bool); + /// get whether to do suppress dev warnings + bool getSuppressDevWarnings(); + /// set whether to do suppress dev warnings + void setSuppressDevWarnings(bool value); + /// get whether to do suppress deprecated warnings + bool getSuppressDeprecatedWarnings(); + /// set whether to do suppress deprecated warnings + void setSuppressDeprecatedWarnings(bool value); + /// get whether to treat developer (author) warnings as errors + bool getDevWarningsAsErrors(); + /// set whether to treat developer (author) warnings as errors + void setDevWarningsAsErrors(bool value); + /// get whether to treat deprecated warnings as errors + bool getDeprecatedWarningsAsErrors(); + /// set whether to treat deprecated warnings as errors + void setDeprecatedWarningsAsErrors(bool value); + /// set whether to run cmake with warnings about uninitialized variables + void setWarnUninitializedMode(bool value); + /// set whether to run cmake with warnings about unused variables + void setWarnUnusedMode(bool value); + +public: + /// get the list of cache properties + QCMakePropertyList properties() const; + /// get the current binary directory + QString binaryDirectory() const; + /// get the current source directory + QString sourceDirectory() const; + /// get the current generator + QString generator() const; + /// get the available generators + std::vector<cmake::GeneratorInfo> const& availableGenerators() const; + /// get whether to do debug output + bool getDebugOutput() const; + +signals: + /// signal when properties change (during read from disk or configure + /// process) + void propertiesChanged(const QCMakePropertyList& vars); + /// signal when the generator changes + void generatorChanged(const QString& gen); + /// signal when the source directory changes (binary directory already + /// containing a CMakeCache.txt file) + void sourceDirChanged(const QString& dir); + /// signal when the binary directory changes + void binaryDirChanged(const QString& dir); + /// signal for progress events + void progressChanged(const QString& msg, float percent); + /// signal when configure is done + void configureDone(int error); + /// signal when generate is done + void generateDone(int error); + /// signal when there is an output message + void outputMessage(const QString& msg); + /// signal when there is an error message + void errorMessage(const QString& msg); + /// signal when debug output changes + void debugOutputChanged(bool); + /// signal when the toolset changes + void toolsetChanged(const QString& toolset); + +protected: + cmake* CMakeInstance; + + static bool interruptCallback(void*); + static void progressCallback(const char* msg, float percent, void* cd); + static void messageCallback(const char* msg, const char* title, bool&, + void* cd); + static void stdoutCallback(const char* msg, size_t len, void* cd); + static void stderrCallback(const char* msg, size_t len, void* cd); + bool WarnUninitializedMode; + bool WarnUnusedMode; + bool WarnUnusedAllMode; + QString SourceDirectory; + QString BinaryDirectory; + QString Generator; + QString Toolset; + std::vector<cmake::GeneratorInfo> AvailableGenerators; + QString CMakeExecutable; + QAtomicInt InterruptFlag; +}; + +#endif // QCMake_h diff --git a/Source/QtDialog/QCMakeCacheView.cxx b/Source/QtDialog/QCMakeCacheView.cxx new file mode 100644 index 0000000..9f5208a --- /dev/null +++ b/Source/QtDialog/QCMakeCacheView.cxx @@ -0,0 +1,678 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMakeCacheView.h" + +#include <QApplication> +#include <QEvent> +#include <QHBoxLayout> +#include <QHeaderView> +#include <QKeyEvent> +#include <QMetaProperty> +#include <QSortFilterProxyModel> +#include <QStyle> + +#include "QCMakeWidgets.h" + +// filter for searches +class QCMakeSearchFilter : public QSortFilterProxyModel +{ +public: + QCMakeSearchFilter(QObject* o) + : QSortFilterProxyModel(o) + { + } + +protected: + bool filterAcceptsRow(int row, const QModelIndex& p) const CM_OVERRIDE + { + QStringList strs; + const QAbstractItemModel* m = this->sourceModel(); + QModelIndex idx = m->index(row, 0, p); + + // if there are no children, get strings for column 0 and 1 + if (!m->hasChildren(idx)) { + strs.append(m->data(idx).toString()); + idx = m->index(row, 1, p); + strs.append(m->data(idx).toString()); + } else { + // get strings for children entries to compare with + // instead of comparing with the parent + int num = m->rowCount(idx); + for (int i = 0; i < num; i++) { + QModelIndex tmpidx = m->index(i, 0, idx); + strs.append(m->data(tmpidx).toString()); + tmpidx = m->index(i, 1, idx); + strs.append(m->data(tmpidx).toString()); + } + } + + // check all strings for a match + foreach (QString str, strs) { + if (str.contains(this->filterRegExp())) { + return true; + } + } + + return false; + } +}; + +// filter for searches +class QCMakeAdvancedFilter : public QSortFilterProxyModel +{ +public: + QCMakeAdvancedFilter(QObject* o) + : QSortFilterProxyModel(o) + , ShowAdvanced(false) + { + } + + void setShowAdvanced(bool f) + { + this->ShowAdvanced = f; + this->invalidate(); + } + bool showAdvanced() const { return this->ShowAdvanced; } + +protected: + bool ShowAdvanced; + + bool filterAcceptsRow(int row, const QModelIndex& p) const CM_OVERRIDE + { + const QAbstractItemModel* m = this->sourceModel(); + QModelIndex idx = m->index(row, 0, p); + + // if there are no children + if (!m->hasChildren(idx)) { + bool adv = m->data(idx, QCMakeCacheModel::AdvancedRole).toBool(); + return !adv || this->ShowAdvanced; + } + + // check children + int num = m->rowCount(idx); + for (int i = 0; i < num; i++) { + bool accept = this->filterAcceptsRow(i, idx); + if (accept) { + return true; + } + } + return false; + } +}; + +QCMakeCacheView::QCMakeCacheView(QWidget* p) + : QTreeView(p) +{ + // hook up our model and search/filter proxies + this->CacheModel = new QCMakeCacheModel(this); + this->AdvancedFilter = new QCMakeAdvancedFilter(this); + this->AdvancedFilter->setSourceModel(this->CacheModel); + this->AdvancedFilter->setDynamicSortFilter(true); + this->SearchFilter = new QCMakeSearchFilter(this); + this->SearchFilter->setSourceModel(this->AdvancedFilter); + this->SearchFilter->setFilterCaseSensitivity(Qt::CaseInsensitive); + this->SearchFilter->setDynamicSortFilter(true); + this->setModel(this->SearchFilter); + + // our delegate for creating our editors + QCMakeCacheModelDelegate* delegate = new QCMakeCacheModelDelegate(this); + this->setItemDelegate(delegate); + + this->setUniformRowHeights(true); + + this->setEditTriggers(QAbstractItemView::AllEditTriggers); + + // tab, backtab doesn't step through items + this->setTabKeyNavigation(false); + + this->setRootIsDecorated(false); +} + +bool QCMakeCacheView::event(QEvent* e) +{ + if (e->type() == QEvent::Show) { + this->header()->setDefaultSectionSize(this->viewport()->width() / 2); + } + return QTreeView::event(e); +} + +QCMakeCacheModel* QCMakeCacheView::cacheModel() const +{ + return this->CacheModel; +} + +QModelIndex QCMakeCacheView::moveCursor(CursorAction act, + Qt::KeyboardModifiers mod) +{ + // want home/end to go to begin/end of rows, not columns + if (act == MoveHome) { + return this->model()->index(0, 1); + } + if (act == MoveEnd) { + return this->model()->index(this->model()->rowCount() - 1, 1); + } + return QTreeView::moveCursor(act, mod); +} + +void QCMakeCacheView::setShowAdvanced(bool s) +{ +#if QT_VERSION >= 040300 + // new 4.3 API that needs to be called. what about an older Qt? + this->SearchFilter->invalidate(); +#endif + + this->AdvancedFilter->setShowAdvanced(s); +} + +bool QCMakeCacheView::showAdvanced() const +{ + return this->AdvancedFilter->showAdvanced(); +} + +void QCMakeCacheView::setSearchFilter(const QString& s) +{ + this->SearchFilter->setFilterFixedString(s); +} + +QCMakeCacheModel::QCMakeCacheModel(QObject* p) + : QStandardItemModel(p) + , EditEnabled(true) + , NewPropertyCount(0) + , View(FlatView) +{ + this->ShowNewProperties = true; + QStringList labels; + labels << tr("Name") << tr("Value"); + this->setHorizontalHeaderLabels(labels); +} + +QCMakeCacheModel::~QCMakeCacheModel() +{ +} + +static uint qHash(const QCMakeProperty& p) +{ + return qHash(p.Key); +} + +void QCMakeCacheModel::setShowNewProperties(bool f) +{ + this->ShowNewProperties = f; +} + +void QCMakeCacheModel::clear() +{ + this->QStandardItemModel::clear(); + this->NewPropertyCount = 0; + + QStringList labels; + labels << tr("Name") << tr("Value"); + this->setHorizontalHeaderLabels(labels); +} + +void QCMakeCacheModel::setProperties(const QCMakePropertyList& props) +{ + QSet<QCMakeProperty> newProps, newProps2; + + if (this->ShowNewProperties) { + newProps = props.toSet(); + newProps2 = newProps; + QSet<QCMakeProperty> oldProps = this->properties().toSet(); + oldProps.intersect(newProps); + newProps.subtract(oldProps); + newProps2.subtract(newProps); + } else { + newProps2 = props.toSet(); + } + + bool b = this->blockSignals(true); + + this->clear(); + this->NewPropertyCount = newProps.size(); + + if (View == FlatView) { + QCMakePropertyList newP = newProps.toList(); + QCMakePropertyList newP2 = newProps2.toList(); + qSort(newP); + qSort(newP2); + int row_count = 0; + foreach (QCMakeProperty p, newP) { + this->insertRow(row_count); + this->setPropertyData(this->index(row_count, 0), p, true); + row_count++; + } + foreach (QCMakeProperty p, newP2) { + this->insertRow(row_count); + this->setPropertyData(this->index(row_count, 0), p, false); + row_count++; + } + } else if (this->View == GroupView) { + QMap<QString, QCMakePropertyList> newPropsTree; + this->breakProperties(newProps, newPropsTree); + QMap<QString, QCMakePropertyList> newPropsTree2; + this->breakProperties(newProps2, newPropsTree2); + + QStandardItem* root = this->invisibleRootItem(); + + foreach (QString key, newPropsTree.keys()) { + QCMakePropertyList props2 = newPropsTree[key]; + + QList<QStandardItem*> parentItems; + parentItems.append( + new QStandardItem(key.isEmpty() ? tr("Ungrouped Entries") : key)); + parentItems.append(new QStandardItem()); + parentItems[0]->setData(QBrush(QColor(255, 100, 100)), + Qt::BackgroundColorRole); + parentItems[1]->setData(QBrush(QColor(255, 100, 100)), + Qt::BackgroundColorRole); + parentItems[0]->setData(1, GroupRole); + parentItems[1]->setData(1, GroupRole); + root->appendRow(parentItems); + + int num = props2.size(); + for (int i = 0; i < num; i++) { + QCMakeProperty prop = props2[i]; + QList<QStandardItem*> items; + items.append(new QStandardItem()); + items.append(new QStandardItem()); + parentItems[0]->appendRow(items); + this->setPropertyData(this->indexFromItem(items[0]), prop, true); + } + } + + foreach (QString key, newPropsTree2.keys()) { + QCMakePropertyList props2 = newPropsTree2[key]; + + QStandardItem* parentItem = + new QStandardItem(key.isEmpty() ? tr("Ungrouped Entries") : key); + root->appendRow(parentItem); + parentItem->setData(1, GroupRole); + + int num = props2.size(); + for (int i = 0; i < num; i++) { + QCMakeProperty prop = props2[i]; + QList<QStandardItem*> items; + items.append(new QStandardItem()); + items.append(new QStandardItem()); + parentItem->appendRow(items); + this->setPropertyData(this->indexFromItem(items[0]), prop, false); + } + } + } + + this->blockSignals(b); + this->reset(); +} + +QCMakeCacheModel::ViewType QCMakeCacheModel::viewType() const +{ + return this->View; +} + +void QCMakeCacheModel::setViewType(QCMakeCacheModel::ViewType t) +{ + this->View = t; + + QCMakePropertyList props = this->properties(); + QCMakePropertyList oldProps; + int numNew = this->NewPropertyCount; + int numTotal = props.count(); + for (int i = numNew; i < numTotal; i++) { + oldProps.append(props[i]); + } + + bool b = this->blockSignals(true); + this->clear(); + this->setProperties(oldProps); + this->setProperties(props); + this->blockSignals(b); + this->reset(); +} + +void QCMakeCacheModel::setPropertyData(const QModelIndex& idx1, + const QCMakeProperty& prop, bool isNew) +{ + QModelIndex idx2 = idx1.sibling(idx1.row(), 1); + + this->setData(idx1, prop.Key, Qt::DisplayRole); + this->setData(idx1, prop.Help, QCMakeCacheModel::HelpRole); + this->setData(idx1, prop.Type, QCMakeCacheModel::TypeRole); + this->setData(idx1, prop.Advanced, QCMakeCacheModel::AdvancedRole); + + if (prop.Type == QCMakeProperty::BOOL) { + int check = prop.Value.toBool() ? Qt::Checked : Qt::Unchecked; + this->setData(idx2, check, Qt::CheckStateRole); + } else { + this->setData(idx2, prop.Value, Qt::DisplayRole); + } + this->setData(idx2, prop.Help, QCMakeCacheModel::HelpRole); + + if (!prop.Strings.isEmpty()) { + this->setData(idx1, prop.Strings, QCMakeCacheModel::StringsRole); + } + + if (isNew) { + this->setData(idx1, QBrush(QColor(255, 100, 100)), + Qt::BackgroundColorRole); + this->setData(idx2, QBrush(QColor(255, 100, 100)), + Qt::BackgroundColorRole); + } +} + +void QCMakeCacheModel::getPropertyData(const QModelIndex& idx1, + QCMakeProperty& prop) const +{ + QModelIndex idx2 = idx1.sibling(idx1.row(), 1); + + prop.Key = this->data(idx1, Qt::DisplayRole).toString(); + prop.Help = this->data(idx1, HelpRole).toString(); + prop.Type = static_cast<QCMakeProperty::PropertyType>( + this->data(idx1, TypeRole).toInt()); + prop.Advanced = this->data(idx1, AdvancedRole).toBool(); + prop.Strings = + this->data(idx1, QCMakeCacheModel::StringsRole).toStringList(); + if (prop.Type == QCMakeProperty::BOOL) { + int check = this->data(idx2, Qt::CheckStateRole).toInt(); + prop.Value = check == Qt::Checked; + } else { + prop.Value = this->data(idx2, Qt::DisplayRole).toString(); + } +} + +QString QCMakeCacheModel::prefix(const QString& s) +{ + QString prefix = s.section('_', 0, 0); + if (prefix == s) { + prefix = QString(); + } + return prefix; +} + +void QCMakeCacheModel::breakProperties( + const QSet<QCMakeProperty>& props, QMap<QString, QCMakePropertyList>& result) +{ + QMap<QString, QCMakePropertyList> tmp; + // return a map of properties grouped by prefixes, and sorted + foreach (QCMakeProperty p, props) { + QString prefix = QCMakeCacheModel::prefix(p.Key); + tmp[prefix].append(p); + } + // sort it and re-org any properties with only one sub item + QCMakePropertyList reorgProps; + QMap<QString, QCMakePropertyList>::iterator iter; + for (iter = tmp.begin(); iter != tmp.end();) { + if (iter->count() == 1) { + reorgProps.append((*iter)[0]); + iter = tmp.erase(iter); + } else { + qSort(*iter); + ++iter; + } + } + if (reorgProps.count()) { + tmp[QString()] += reorgProps; + } + result = tmp; +} + +QCMakePropertyList QCMakeCacheModel::properties() const +{ + QCMakePropertyList props; + + if (!this->rowCount()) { + return props; + } + + QList<QModelIndex> idxs; + idxs.append(this->index(0, 0)); + + // walk the entire model for property entries + // this works regardless of a flat view or a tree view + while (!idxs.isEmpty()) { + QModelIndex idx = idxs.last(); + if (this->hasChildren(idx) && this->rowCount(idx)) { + idxs.append(this->index(0, 0, idx)); + } else { + if (!data(idx, GroupRole).toInt()) { + // get data + QCMakeProperty prop; + this->getPropertyData(idx, prop); + props.append(prop); + } + + // go to the next in the tree + while (!idxs.isEmpty() && + ( +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) && \ + QT_VERSION < QT_VERSION_CHECK(5, 1, 0) + (idxs.last().row() + 1) >= rowCount(idxs.last().parent()) || +#endif + !idxs.last().sibling(idxs.last().row() + 1, 0).isValid())) { + idxs.removeLast(); + } + if (!idxs.isEmpty()) { + idxs.last() = idxs.last().sibling(idxs.last().row() + 1, 0); + } + } + } + + return props; +} + +bool QCMakeCacheModel::insertProperty(QCMakeProperty::PropertyType t, + const QString& name, + const QString& description, + const QVariant& value, bool advanced) +{ + QCMakeProperty prop; + prop.Key = name; + prop.Value = value; + prop.Help = description; + prop.Type = t; + prop.Advanced = advanced; + + // insert at beginning + this->insertRow(0); + this->setPropertyData(this->index(0, 0), prop, true); + this->NewPropertyCount++; + return true; +} + +void QCMakeCacheModel::setEditEnabled(bool e) +{ + this->EditEnabled = e; +} + +bool QCMakeCacheModel::editEnabled() const +{ + return this->EditEnabled; +} + +int QCMakeCacheModel::newPropertyCount() const +{ + return this->NewPropertyCount; +} + +Qt::ItemFlags QCMakeCacheModel::flags(const QModelIndex& idx) const +{ + Qt::ItemFlags f = QStandardItemModel::flags(idx); + if (!this->EditEnabled) { + f &= ~Qt::ItemIsEditable; + return f; + } + if (QCMakeProperty::BOOL == this->data(idx, TypeRole).toInt()) { + f |= Qt::ItemIsUserCheckable; + } + return f; +} + +QModelIndex QCMakeCacheModel::buddy(const QModelIndex& idx) const +{ + if (!this->hasChildren(idx) && + this->data(idx, TypeRole).toInt() != QCMakeProperty::BOOL) { + return this->index(idx.row(), 1, idx.parent()); + } + return idx; +} + +QCMakeCacheModelDelegate::QCMakeCacheModelDelegate(QObject* p) + : QItemDelegate(p) + , FileDialogFlag(false) +{ +} + +void QCMakeCacheModelDelegate::setFileDialogFlag(bool f) +{ + this->FileDialogFlag = f; +} + +QWidget* QCMakeCacheModelDelegate::createEditor( + QWidget* p, const QStyleOptionViewItem& /*option*/, + const QModelIndex& idx) const +{ + QModelIndex var = idx.sibling(idx.row(), 0); + int type = var.data(QCMakeCacheModel::TypeRole).toInt(); + if (type == QCMakeProperty::BOOL) { + return CM_NULLPTR; + } + if (type == QCMakeProperty::PATH) { + QCMakePathEditor* editor = + new QCMakePathEditor(p, var.data(Qt::DisplayRole).toString()); + QObject::connect(editor, SIGNAL(fileDialogExists(bool)), this, + SLOT(setFileDialogFlag(bool))); + return editor; + } + if (type == QCMakeProperty::FILEPATH) { + QCMakeFilePathEditor* editor = + new QCMakeFilePathEditor(p, var.data(Qt::DisplayRole).toString()); + QObject::connect(editor, SIGNAL(fileDialogExists(bool)), this, + SLOT(setFileDialogFlag(bool))); + return editor; + } + if (type == QCMakeProperty::STRING && + var.data(QCMakeCacheModel::StringsRole).isValid()) { + QCMakeComboBox* editor = new QCMakeComboBox( + p, var.data(QCMakeCacheModel::StringsRole).toStringList()); + editor->setFrame(false); + return editor; + } + + QLineEdit* editor = new QLineEdit(p); + editor->setFrame(false); + return editor; +} + +bool QCMakeCacheModelDelegate::editorEvent(QEvent* e, + QAbstractItemModel* model, + const QStyleOptionViewItem& option, + const QModelIndex& index) +{ + Qt::ItemFlags flags = model->flags(index); + if (!(flags & Qt::ItemIsUserCheckable) || + !(option.state & QStyle::State_Enabled) || + !(flags & Qt::ItemIsEnabled)) { + return false; + } + + QVariant value = index.data(Qt::CheckStateRole); + if (!value.isValid()) { + return false; + } + + if ((e->type() == QEvent::MouseButtonRelease) || + (e->type() == QEvent::MouseButtonDblClick)) { + // eat the double click events inside the check rect + if (e->type() == QEvent::MouseButtonDblClick) { + return true; + } + } else if (e->type() == QEvent::KeyPress) { + if (static_cast<QKeyEvent*>(e)->key() != Qt::Key_Space && + static_cast<QKeyEvent*>(e)->key() != Qt::Key_Select) { + return false; + } + } else { + return false; + } + + Qt::CheckState state = + (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked ? Qt::Unchecked + : Qt::Checked); + bool success = model->setData(index, state, Qt::CheckStateRole); + if (success) { + this->recordChange(model, index); + } + return success; +} + +// Issue 205903 fixed in Qt 4.5.0. +// Can remove this function and FileDialogFlag when minimum Qt version is 4.5 +bool QCMakeCacheModelDelegate::eventFilter(QObject* object, QEvent* evt) +{ + // workaround for what looks like a bug in Qt on Mac OS X + // where it doesn't create a QWidget wrapper for the native file dialog + // so the Qt library ends up assuming the focus was lost to something else + + if (evt->type() == QEvent::FocusOut && this->FileDialogFlag) { + return false; + } + return QItemDelegate::eventFilter(object, evt); +} + +void QCMakeCacheModelDelegate::setModelData(QWidget* editor, + QAbstractItemModel* model, + const QModelIndex& index) const +{ + QItemDelegate::setModelData(editor, model, index); + const_cast<QCMakeCacheModelDelegate*>(this)->recordChange(model, index); +} + +QSize QCMakeCacheModelDelegate::sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QSize sz = QItemDelegate::sizeHint(option, index); + QStyle* style = QApplication::style(); + + // increase to checkbox size + QStyleOptionButton opt; + opt.QStyleOption::operator=(option); + sz = sz.expandedTo( + style->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt, CM_NULLPTR) + .size()); + + return sz; +} + +QSet<QCMakeProperty> QCMakeCacheModelDelegate::changes() const +{ + return mChanges; +} + +void QCMakeCacheModelDelegate::clearChanges() +{ + mChanges.clear(); +} + +void QCMakeCacheModelDelegate::recordChange(QAbstractItemModel* model, + const QModelIndex& index) +{ + QModelIndex idx = index; + QAbstractItemModel* mymodel = model; + while (qobject_cast<QAbstractProxyModel*>(mymodel)) { + idx = static_cast<QAbstractProxyModel*>(mymodel)->mapToSource(idx); + mymodel = static_cast<QAbstractProxyModel*>(mymodel)->sourceModel(); + } + QCMakeCacheModel* cache_model = qobject_cast<QCMakeCacheModel*>(mymodel); + if (cache_model && idx.isValid()) { + QCMakeProperty prop; + idx = idx.sibling(idx.row(), 0); + cache_model->getPropertyData(idx, prop); + + // clean out an old one + QSet<QCMakeProperty>::iterator iter = mChanges.find(prop); + if (iter != mChanges.end()) { + mChanges.erase(iter); + } + // now add the new item + mChanges.insert(prop); + } +} diff --git a/Source/QtDialog/QCMakeCacheView.h b/Source/QtDialog/QCMakeCacheView.h new file mode 100644 index 0000000..c1debf5 --- /dev/null +++ b/Source/QtDialog/QCMakeCacheView.h @@ -0,0 +1,170 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef QCMakeCacheView_h +#define QCMakeCacheView_h + +#include "QCMake.h" + +#include <QItemDelegate> +#include <QSet> +#include <QStandardItemModel> +#include <QTreeView> + +class QSortFilterProxyModel; +class QCMakeCacheModel; +class QCMakeAdvancedFilter; + +/// Qt view class for cache properties +class QCMakeCacheView : public QTreeView +{ + Q_OBJECT +public: + QCMakeCacheView(QWidget* p); + + // retrieve the QCMakeCacheModel storing all the pointers + // this isn't necessarily the model one would get from model() + QCMakeCacheModel* cacheModel() const; + + // get whether to show advanced entries + bool showAdvanced() const; + + QSize sizeHint() const { return QSize(200, 200); } + +public slots: + // set whether to show advanced entries + void setShowAdvanced(bool); + // set the search filter string. any property key or value not matching will + // be filtered out + void setSearchFilter(const QString&); + +protected: + QModelIndex moveCursor(CursorAction, Qt::KeyboardModifiers); + bool event(QEvent* e); + QCMakeCacheModel* CacheModel; + QCMakeAdvancedFilter* AdvancedFilter; + QSortFilterProxyModel* SearchFilter; +}; + +/// Qt model class for cache properties +class QCMakeCacheModel : public QStandardItemModel +{ + Q_OBJECT +public: + QCMakeCacheModel(QObject* parent); + ~QCMakeCacheModel(); + + // roles used to retrieve extra data such has help strings, types of + // properties, and the advanced flag + enum + { + HelpRole = Qt::ToolTipRole, + TypeRole = Qt::UserRole, + AdvancedRole, + StringsRole, + GroupRole + }; + + enum ViewType + { + FlatView, + GroupView + }; + +public slots: + // set a list of properties. This list will be sorted and grouped according + // to prefix. Any property that existed already and which is found in this + // list of properties to set will become an old property. All others will + // become new properties and be marked red. + void setProperties(const QCMakePropertyList& props); + + // set whether to show new properties in red + void setShowNewProperties(bool); + + // clear everything from the model + void clear(); + + // set flag whether the model can currently be edited. + void setEditEnabled(bool); + + // insert a new property at a row specifying all the information about the + // property + bool insertProperty(QCMakeProperty::PropertyType t, const QString& name, + const QString& description, const QVariant& value, + bool advanced); + + // set the view type + void setViewType(ViewType t); + ViewType viewType() const; + +public: + // get the properties + QCMakePropertyList properties() const; + + // editing enabled + bool editEnabled() const; + + // returns how many new properties there are + int newPropertyCount() const; + + // return flags (overloaded to modify flag based on EditEnabled flag) + Qt::ItemFlags flags(const QModelIndex& index) const; + QModelIndex buddy(const QModelIndex& idx) const; + + // get the data in the model for this property + void getPropertyData(const QModelIndex& idx1, QCMakeProperty& prop) const; + +protected: + bool EditEnabled; + int NewPropertyCount; + bool ShowNewProperties; + ViewType View; + + // set the data in the model for this property + void setPropertyData(const QModelIndex& idx1, const QCMakeProperty& p, + bool isNew); + + // breaks up he property list into groups + // where each group has the same prefix up to the first underscore + static void breakProperties(const QSet<QCMakeProperty>& props, + QMap<QString, QCMakePropertyList>& result); + + // gets the prefix of a string up to the first _ + static QString prefix(const QString& s); +}; + +/// Qt delegate class for interaction (or other customization) +/// with cache properties +class QCMakeCacheModelDelegate : public QItemDelegate +{ + Q_OBJECT +public: + QCMakeCacheModelDelegate(QObject* p); + /// create our own editors for cache properties + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, + const QModelIndex& index) const; + bool editorEvent(QEvent* event, QAbstractItemModel* model, + const QStyleOptionViewItem& option, + const QModelIndex& index); + bool eventFilter(QObject* object, QEvent* event); + void setModelData(QWidget* editor, QAbstractItemModel* model, + const QModelIndex& index) const; + QSize sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const; + + QSet<QCMakeProperty> changes() const; + void clearChanges(); + +protected slots: + void setFileDialogFlag(bool); + +protected: + bool FileDialogFlag; + // record a change to an item in the model. + // this simply saves the item in the set of changes + void recordChange(QAbstractItemModel* model, const QModelIndex& index); + + // properties changed by user via this delegate + QSet<QCMakeProperty> mChanges; +}; + +#endif diff --git a/Source/QtDialog/QCMakeWidgets.cxx b/Source/QtDialog/QCMakeWidgets.cxx new file mode 100644 index 0000000..6a55a76 --- /dev/null +++ b/Source/QtDialog/QCMakeWidgets.cxx @@ -0,0 +1,118 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "QCMakeWidgets.h" + +#include <QDirModel> +#include <QFileDialog> +#include <QFileInfo> +#include <QResizeEvent> +#include <QToolButton> + +QCMakeFileEditor::QCMakeFileEditor(QWidget* p, const QString& var) + : QLineEdit(p) + , Variable(var) +{ + this->ToolButton = new QToolButton(this); + this->ToolButton->setText("..."); + this->ToolButton->setCursor(QCursor(Qt::ArrowCursor)); + QObject::connect(this->ToolButton, SIGNAL(clicked(bool)), this, + SLOT(chooseFile())); +} + +QCMakeFilePathEditor::QCMakeFilePathEditor(QWidget* p, const QString& var) + : QCMakeFileEditor(p, var) +{ + this->setCompleter(new QCMakeFileCompleter(this, false)); +} + +QCMakePathEditor::QCMakePathEditor(QWidget* p, const QString& var) + : QCMakeFileEditor(p, var) +{ + this->setCompleter(new QCMakeFileCompleter(this, true)); +} + +void QCMakeFileEditor::resizeEvent(QResizeEvent* e) +{ + // make the tool button fit on the right side + int h = e->size().height(); + // move the line edit to make room for the tool button + this->setContentsMargins(0, 0, h, 0); + // put the tool button in its place + this->ToolButton->resize(h, h); + this->ToolButton->move(this->width() - h, 0); +} + +void QCMakeFilePathEditor::chooseFile() +{ + // choose a file and set it + QString path; + QFileInfo info(this->text()); + QString title; + if (this->Variable.isEmpty()) { + title = tr("Select File"); + } else { + title = tr("Select File for %1"); + title = title.arg(this->Variable); + } + this->fileDialogExists(true); + path = + QFileDialog::getOpenFileName(this, title, info.absolutePath(), QString(), + CM_NULLPTR, QFileDialog::DontResolveSymlinks); + this->fileDialogExists(false); + + if (!path.isEmpty()) { + this->setText(QDir::fromNativeSeparators(path)); + } +} + +void QCMakePathEditor::chooseFile() +{ + // choose a file and set it + QString path; + QString title; + if (this->Variable.isEmpty()) { + title = tr("Select Path"); + } else { + title = tr("Select Path for %1"); + title = title.arg(this->Variable); + } + this->fileDialogExists(true); + path = QFileDialog::getExistingDirectory(this, title, this->text(), + QFileDialog::ShowDirsOnly | + QFileDialog::DontResolveSymlinks); + this->fileDialogExists(false); + if (!path.isEmpty()) { + this->setText(QDir::fromNativeSeparators(path)); + } +} + +// use same QDirModel for all completers +static QDirModel* fileDirModel() +{ + static QDirModel* m = CM_NULLPTR; + if (!m) { + m = new QDirModel(); + } + return m; +} +static QDirModel* pathDirModel() +{ + static QDirModel* m = CM_NULLPTR; + if (!m) { + m = new QDirModel(); + m->setFilter(QDir::AllDirs | QDir::Drives | QDir::NoDotAndDotDot); + } + return m; +} + +QCMakeFileCompleter::QCMakeFileCompleter(QObject* o, bool dirs) + : QCompleter(o) +{ + QDirModel* m = dirs ? pathDirModel() : fileDirModel(); + this->setModel(m); +} + +QString QCMakeFileCompleter::pathFromIndex(const QModelIndex& idx) const +{ + return QDir::fromNativeSeparators(QCompleter::pathFromIndex(idx)); +} diff --git a/Source/QtDialog/QCMakeWidgets.h b/Source/QtDialog/QCMakeWidgets.h new file mode 100644 index 0000000..0db810c --- /dev/null +++ b/Source/QtDialog/QCMakeWidgets.h @@ -0,0 +1,82 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef QCMakeWidgets_h +#define QCMakeWidgets_h + +#include <cmConfigure.h> + +#include <QComboBox> +#include <QCompleter> +#include <QLineEdit> + +class QToolButton; + +// common widgets for Qt based CMake + +/// Editor widget for editing paths or file paths +class QCMakeFileEditor : public QLineEdit +{ + Q_OBJECT +public: + QCMakeFileEditor(QWidget* p, const QString& var); +protected slots: + virtual void chooseFile() = 0; +signals: + void fileDialogExists(bool); + +protected: + void resizeEvent(QResizeEvent* e); + QToolButton* ToolButton; + QString Variable; +}; + +/// editor widget for editing files +class QCMakePathEditor : public QCMakeFileEditor +{ + Q_OBJECT +public: + QCMakePathEditor(QWidget* p = CM_NULLPTR, const QString& var = QString()); + void chooseFile(); +}; + +/// editor widget for editing paths +class QCMakeFilePathEditor : public QCMakeFileEditor +{ + Q_OBJECT +public: + QCMakeFilePathEditor(QWidget* p = CM_NULLPTR, + const QString& var = QString()); + void chooseFile(); +}; + +/// completer class that returns native cmake paths +class QCMakeFileCompleter : public QCompleter +{ + Q_OBJECT +public: + QCMakeFileCompleter(QObject* o, bool dirs); + virtual QString pathFromIndex(const QModelIndex& idx) const; +}; + +// editor for strings +class QCMakeComboBox : public QComboBox +{ + Q_OBJECT + Q_PROPERTY(QString value READ currentText WRITE setValue USER true); + +public: + QCMakeComboBox(QWidget* p, QStringList strings) + : QComboBox(p) + { + this->addItems(strings); + } + void setValue(const QString& v) + { + int i = this->findText(v); + if (i != -1) { + this->setCurrentIndex(i); + } + } +}; + +#endif diff --git a/Source/QtDialog/QtDialogCPack.cmake.in b/Source/QtDialog/QtDialogCPack.cmake.in new file mode 100644 index 0000000..7ae8605 --- /dev/null +++ b/Source/QtDialog/QtDialogCPack.cmake.in @@ -0,0 +1,15 @@ +set(IS_APPLE @APPLE@) +set(CMAKE_PACKAGE_QTGUI @CMAKE_PACKAGE_QTGUI@) + +if(CMAKE_PACKAGE_QTGUI) + set(CPACK_PACKAGE_EXECUTABLES "cmake-gui" "CMake (cmake-gui)" ${CPACK_PACKAGE_EXECUTABLES}) + set(CPACK_CREATE_DESKTOP_LINKS "cmake-gui" ${CPACK_CREATE_DESKTOP_LINKS}) + if(IS_APPLE) + # for apple install we set the install prefix to + # / and then install + # cmake into the bundle for cmake-gui and must use DESTDIR + set(CPACK_SET_DESTDIR TRUE) + endif() +endif() + + diff --git a/Source/QtDialog/RegexExplorer.cxx b/Source/QtDialog/RegexExplorer.cxx new file mode 100644 index 0000000..1512166 --- /dev/null +++ b/Source/QtDialog/RegexExplorer.cxx @@ -0,0 +1,136 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "RegexExplorer.h" + +RegexExplorer::RegexExplorer(QWidget* p) + : QDialog(p) + , m_matched(false) +{ + this->setupUi(this); + + for (int i = 1; i < cmsys::RegularExpression::NSUBEXP; ++i) { + matchNumber->addItem(QString("Match %1").arg(QString::number(i)), + QVariant(i)); + } + matchNumber->setCurrentIndex(0); +} + +void RegexExplorer::setStatusColor(QWidget* widget, bool successful) +{ + QColor color = successful ? QColor(0, 127, 0) : Qt::red; + + QPalette palette = widget->palette(); + palette.setColor(QPalette::Foreground, color); + widget->setPalette(palette); +} + +void RegexExplorer::on_regularExpression_textChanged(const QString& text) +{ +#ifdef QT_NO_STL + m_regex = text.toAscii().constData(); +#else + m_regex = text.toStdString(); +#endif + + bool validExpression = + stripEscapes(m_regex) && m_regexParser.compile(m_regex); + if (!validExpression) { + m_regexParser.set_invalid(); + } + + setStatusColor(labelRegexValid, validExpression); + + on_inputText_textChanged(); +} + +void RegexExplorer::on_inputText_textChanged() +{ + if (m_regexParser.is_valid()) { + QString plainText = inputText->toPlainText(); +#ifdef QT_NO_STL + m_text = plainText.toAscii().constData(); +#else + m_text = plainText.toStdString(); +#endif + m_matched = m_regexParser.find(m_text); + } else { + m_matched = false; + } + + setStatusColor(labelRegexMatch, m_matched); + + if (!m_matched) { + clearMatch(); + return; + } + +#ifdef QT_NO_STL + QString matchText = m_regexParser.match(0).c_str(); +#else + QString matchText = QString::fromStdString(m_regexParser.match(0)); +#endif + match0->setPlainText(matchText); + + on_matchNumber_currentIndexChanged(matchNumber->currentIndex()); +} + +void RegexExplorer::on_matchNumber_currentIndexChanged(int index) +{ + if (!m_matched) { + return; + } + + QVariant itemData = matchNumber->itemData(index); + int idx = itemData.toInt(); + + if (idx < 1 || idx >= cmsys::RegularExpression::NSUBEXP) { + return; + } + +#ifdef QT_NO_STL + QString match = m_regexParser.match(idx).c_str(); +#else + QString match = QString::fromStdString(m_regexParser.match(idx)); +#endif + matchN->setPlainText(match); +} + +void RegexExplorer::clearMatch() +{ + match0->clear(); + matchN->clear(); +} + +bool RegexExplorer::stripEscapes(std::string& source) +{ + const char* in = source.c_str(); + + std::string result; + result.reserve(source.size()); + + for (char inc = *in; inc != '\0'; inc = *++in) { + if (inc == '\\') { + char nextc = in[1]; + if (nextc == 't') { + result.append(1, '\t'); + in++; + } else if (nextc == 'n') { + result.append(1, '\n'); + in++; + } else if (nextc == 't') { + result.append(1, '\t'); + in++; + } else if (isalnum(nextc) || nextc == '\0') { + return false; + } else { + result.append(1, nextc); + in++; + } + } else { + result.append(1, inc); + } + } + + source = result; + return true; +} diff --git a/Source/QtDialog/RegexExplorer.h b/Source/QtDialog/RegexExplorer.h new file mode 100644 index 0000000..f1c1e5f --- /dev/null +++ b/Source/QtDialog/RegexExplorer.h @@ -0,0 +1,38 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef RegexExplorer_h +#define RegexExplorer_h + +#include <QDialog> +#include <cmsys/RegularExpression.hxx> +#include <string> + +#include "ui_RegexExplorer.h" + +class QString; +class QWidget; + +class RegexExplorer : public QDialog, public Ui::RegexExplorer +{ + Q_OBJECT +public: + RegexExplorer(QWidget* p); + +private slots: + void on_regularExpression_textChanged(const QString& text); + void on_inputText_textChanged(); + void on_matchNumber_currentIndexChanged(int index); + +private: + static void setStatusColor(QWidget* widget, bool successful); + static bool stripEscapes(std::string& regex); + + void clearMatch(); + + cmsys::RegularExpression m_regexParser; + std::string m_text; + std::string m_regex; + bool m_matched; +}; + +#endif diff --git a/Source/QtDialog/RegexExplorer.ui b/Source/QtDialog/RegexExplorer.ui new file mode 100644 index 0000000..2c2d761 --- /dev/null +++ b/Source/QtDialog/RegexExplorer.ui @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>RegexExplorer</class> + <widget class="QDialog" name="RegexExplorer"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>639</width> + <height>555</height> + </rect> + </property> + <property name="windowTitle"> + <string>Regular Expression Explorer</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Input Text</string> + </property> + </widget> + </item> + <item> + <widget class="QPlainTextEdit" name="inputText"/> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Regular Expression</string> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_3"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="labelRegexValid"> + <property name="text"> + <string>Valid</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_4"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeType"> + <enum>QSizePolicy::Fixed</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item> + <widget class="QLabel" name="labelRegexMatch"> + <property name="text"> + <string>Match</string> + </property> + <property name="alignment"> + <set>Qt::AlignCenter</set> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QLineEdit" name="regularExpression"/> + </item> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Complete Match</string> + </property> + </widget> + </item> + <item> + <widget class="QPlainTextEdit" name="match0"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QComboBox" name="matchNumber"> + <property name="editable"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </item> + <item> + <widget class="QPlainTextEdit" name="matchN"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/Source/QtDialog/WarningMessagesDialog.cxx b/Source/QtDialog/WarningMessagesDialog.cxx new file mode 100644 index 0000000..f608a84 --- /dev/null +++ b/Source/QtDialog/WarningMessagesDialog.cxx @@ -0,0 +1,86 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#include "WarningMessagesDialog.h" + +WarningMessagesDialog::WarningMessagesDialog(QWidget* prnt, QCMake* instance) + : QDialog(prnt) + , cmakeInstance(instance) +{ + this->setupUi(this); + this->setInitialValues(); + this->setupSignals(); +} + +void WarningMessagesDialog::setInitialValues() +{ + this->suppressDeveloperWarnings->setChecked( + this->cmakeInstance->getSuppressDevWarnings()); + this->suppressDeprecatedWarnings->setChecked( + this->cmakeInstance->getSuppressDeprecatedWarnings()); + + this->developerWarningsAsErrors->setChecked( + this->cmakeInstance->getDevWarningsAsErrors()); + this->deprecatedWarningsAsErrors->setChecked( + this->cmakeInstance->getDeprecatedWarningsAsErrors()); +} + +void WarningMessagesDialog::setupSignals() +{ + QObject::connect(this->buttonBox, SIGNAL(accepted()), this, + SLOT(doAccept())); + + QObject::connect(this->suppressDeveloperWarnings, SIGNAL(stateChanged(int)), + this, SLOT(doSuppressDeveloperWarningsChanged(int))); + QObject::connect(this->suppressDeprecatedWarnings, SIGNAL(stateChanged(int)), + this, SLOT(doSuppressDeprecatedWarningsChanged(int))); + + QObject::connect(this->developerWarningsAsErrors, SIGNAL(stateChanged(int)), + this, SLOT(doDeveloperWarningsAsErrorsChanged(int))); + QObject::connect(this->deprecatedWarningsAsErrors, SIGNAL(stateChanged(int)), + this, SLOT(doDeprecatedWarningsAsErrorsChanged(int))); +} + +void WarningMessagesDialog::doAccept() +{ + this->cmakeInstance->setSuppressDevWarnings( + this->suppressDeveloperWarnings->isChecked()); + this->cmakeInstance->setSuppressDeprecatedWarnings( + this->suppressDeprecatedWarnings->isChecked()); + + this->cmakeInstance->setDevWarningsAsErrors( + this->developerWarningsAsErrors->isChecked()); + this->cmakeInstance->setDeprecatedWarningsAsErrors( + this->deprecatedWarningsAsErrors->isChecked()); +} + +void WarningMessagesDialog::doSuppressDeveloperWarningsChanged(int state) +{ + // no warnings implies no errors either + if (state) { + this->developerWarningsAsErrors->setChecked(false); + } +} + +void WarningMessagesDialog::doSuppressDeprecatedWarningsChanged(int state) +{ + // no warnings implies no errors either + if (state) { + this->deprecatedWarningsAsErrors->setChecked(false); + } +} + +void WarningMessagesDialog::doDeveloperWarningsAsErrorsChanged(int state) +{ + // warnings as errors implies warnings are not suppressed + if (state) { + this->suppressDeveloperWarnings->setChecked(false); + } +} + +void WarningMessagesDialog::doDeprecatedWarningsAsErrorsChanged(int state) +{ + // warnings as errors implies warnings are not suppressed + if (state) { + this->suppressDeprecatedWarnings->setChecked(false); + } +} diff --git a/Source/QtDialog/WarningMessagesDialog.h b/Source/QtDialog/WarningMessagesDialog.h new file mode 100644 index 0000000..acb830d --- /dev/null +++ b/Source/QtDialog/WarningMessagesDialog.h @@ -0,0 +1,65 @@ +/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying + file Copyright.txt or https://cmake.org/licensing for details. */ +#ifndef WarningMessagesDialog_h +#define WarningMessagesDialog_h + +#include <QDialog> +#include <QWidget> + +#include "QCMake.h" +#include "ui_WarningMessagesDialog.h" + +/** + * Dialog window for setting the warning message related options. + */ +class WarningMessagesDialog : public QDialog, public Ui_MessagesDialog +{ + Q_OBJECT + +public: + WarningMessagesDialog(QWidget* prnt, QCMake* instance); + +private slots: + /** + * Handler for the accept event of the ok/cancel button box. + */ + void doAccept(); + + /** + * Handler for checked state changed event of the suppress developer warnings + * checkbox. + */ + void doSuppressDeveloperWarningsChanged(int state); + /** + * Handler for checked state changed event of the suppress deprecated + * warnings checkbox. + */ + void doSuppressDeprecatedWarningsChanged(int state); + + /** + * Handler for checked state changed event of the developer warnings as + * errors checkbox. + */ + void doDeveloperWarningsAsErrorsChanged(int state); + /** + * Handler for checked state changed event of the deprecated warnings as + * errors checkbox. + */ + void doDeprecatedWarningsAsErrorsChanged(int state); + +private: + QCMake* cmakeInstance; + + /** + * Set the initial values of the widgets on this dialog window, using the + * current state of the cache. + */ + void setInitialValues(); + + /** + * Setup the signals for the widgets on this dialog window. + */ + void setupSignals(); +}; + +#endif /* MessageDialog_h */ diff --git a/Source/QtDialog/WarningMessagesDialog.ui b/Source/QtDialog/WarningMessagesDialog.ui new file mode 100644 index 0000000..3b35cbc --- /dev/null +++ b/Source/QtDialog/WarningMessagesDialog.ui @@ -0,0 +1,173 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MessagesDialog</class> + <widget class="QDialog" name="MessagesDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>300</width> + <height>300</height> + </rect> + </property> + <property name="windowTitle"> + <string>Warning Messages</string> + </property> + <property name="modal"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Suppress Warnings</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <widget class="QCheckBox" name="suppressDeveloperWarnings"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Suppress developer (author) warnings.</string> + </property> + <property name="text"> + <string>Developer Warnings</string> + </property> + <property name="tristate"> + <bool>false</bool> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="suppressDeprecatedWarnings"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Suppress deprecated warnings.</string> + </property> + <property name="text"> + <string>Deprecated Warnings</string> + </property> + <property name="tristate"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="title"> + <string>Warnings as Errors</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QCheckBox" name="developerWarningsAsErrors"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Treat developer (author) warnings as errors.</string> + </property> + <property name="text"> + <string>Developer Warnings as Errors</string> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="deprecatedWarningsAsErrors"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip"> + <string>Treat deprecated warnings as errors.</string> + </property> + <property name="text"> + <string>Deprecated Warnings as Errors</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>MessagesDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>MessagesDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/Source/QtDialog/cmakecache.xml b/Source/QtDialog/cmakecache.xml new file mode 100644 index 0000000..a13b5b1 --- /dev/null +++ b/Source/QtDialog/cmakecache.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info"> + <mime-type type="application/x-cmakecache"> + <comment>CMake cache file</comment> + <glob pattern="CMakeCache.txt"/> + <sub-class-of type="text/plain"/> + </mime-type> +</mime-info> |