diff options
Diffstat (limited to 'src/libs/utils')
77 files changed, 2464 insertions, 555 deletions
diff --git a/src/libs/utils/CMakeLists.txt b/src/libs/utils/CMakeLists.txt new file mode 100644 index 0000000000..00307728a6 --- /dev/null +++ b/src/libs/utils/CMakeLists.txt @@ -0,0 +1,204 @@ +if (IDE_LIBEXEC_PATH AND IDE_BIN_PATH) + file(RELATIVE_PATH RELATIVE_TOOLS_PATH + "${CMAKE_INSTALL_PREFIX}/${IDE_BIN_PATH}" "${CMAKE_INSTALL_PREFIX}/${IDE_LIBEXEC_PATH}") +else() + message(WARNING "IDE_LIBEXEC_PATH or IDE_BIN_PATH undefined when calculating tools path") + set(RELATIVE_TOOLS_PATH "") +endif() + +add_qtc_library(Utils + DEPENDS Qt5::Xml + PUBLIC_DEPENDS Qt5::Concurrent Qt5::Core Qt5::Network Qt5::Qml Qt5::Gui Qt5::Widgets + DEFINES + "QTC_REL_TOOLS_PATH=\"${RELATIVE_TOOLS_PATH}\"" + SOURCES + ../3rdparty/optional/optional.hpp + ../3rdparty/variant/variant.hpp + QtConcurrentTools + algorithm.h + ansiescapecodehandler.cpp ansiescapecodehandler.h + appmainwindow.cpp appmainwindow.h + basetreeview.cpp basetreeview.h + benchmarker.cpp benchmarker.h + buildablehelperlibrary.cpp buildablehelperlibrary.h + categorysortfiltermodel.cpp categorysortfiltermodel.h + changeset.cpp changeset.h + checkablemessagebox.cpp checkablemessagebox.h + classnamevalidatinglineedit.cpp classnamevalidatinglineedit.h + codegeneration.cpp codegeneration.h + completinglineedit.cpp completinglineedit.h + completingtextedit.cpp completingtextedit.h + consoleprocess.cpp consoleprocess.h consoleprocess_p.h + cpplanguage_details.h + crumblepath.cpp crumblepath.h + delegates.cpp delegates.h + declarationmacros.h + detailsbutton.cpp detailsbutton.h + detailswidget.cpp detailswidget.h + differ.cpp differ.h + dropsupport.cpp dropsupport.h + elfreader.cpp elfreader.h + elidinglabel.cpp elidinglabel.h + environment.cpp environment.h + environmentdialog.cpp environmentdialog.h + environmentmodel.cpp environmentmodel.h + execmenu.cpp execmenu.h + executeondestruction.h + fadingindicator.cpp fadingindicator.h + faketooltip.cpp faketooltip.h + fancylineedit.cpp fancylineedit.h + fancymainwindow.cpp fancymainwindow.h + filecrumblabel.cpp filecrumblabel.h + fileinprojectfinder.cpp fileinprojectfinder.h + filenamevalidatinglineedit.cpp filenamevalidatinglineedit.h + filesearch.cpp filesearch.h + filesystemwatcher.cpp filesystemwatcher.h + fileutils.cpp fileutils.h + filewizardpage.cpp filewizardpage.h + fixedsizeclicklabel.cpp fixedsizeclicklabel.h + flowlayout.cpp flowlayout.h + functiontraits.h + fuzzymatcher.cpp fuzzymatcher.h + genericconstants.h + globalfilechangeblocker.cpp globalfilechangeblocker.h + guard.cpp guard.h + headerviewstretcher.cpp headerviewstretcher.h + highlightingitemdelegate.cpp highlightingitemdelegate.h + historycompleter.cpp historycompleter.h + hostosinfo.cpp hostosinfo.h + htmldocextractor.cpp htmldocextractor.h + icon.cpp icon.h + itemviews.cpp itemviews.h + json.cpp json.h + jsontreeitem.cpp jsontreeitem.h + linecolumn.h + link.h + listmodel.h + listutils.h + macroexpander.cpp macroexpander.h + mapreduce.h + mimetypes/mimedatabase.cpp mimetypes/mimedatabase.h mimetypes/mimedatabase_p.h + mimetypes/mimeglobpattern.cpp mimetypes/mimeglobpattern_p.h + mimetypes/mimemagicrule.cpp mimetypes/mimemagicrule_p.h + mimetypes/mimemagicrulematcher.cpp mimetypes/mimemagicrulematcher_p.h + mimetypes/mimeprovider.cpp mimetypes/mimeprovider_p.h + mimetypes/mimetype.cpp mimetypes/mimetype.h mimetypes/mimetype_p.h + mimetypes/mimetypeparser.cpp mimetypes/mimetypeparser_p.h + navigationtreeview.cpp navigationtreeview.h + networkaccessmanager.cpp networkaccessmanager.h + newclasswidget.cpp newclasswidget.h newclasswidget.ui + optional.h + osspecificaspects.h + outputformat.h + outputformatter.cpp outputformatter.h + overridecursor.cpp overridecursor.h + parameteraction.cpp parameteraction.h + pathchooser.cpp pathchooser.h + pathlisteditor.cpp pathlisteditor.h + persistentsettings.cpp persistentsettings.h + pointeralgorithm.h + port.cpp port.h + portlist.cpp portlist.h + predicates.h + processhandle.cpp processhandle.h + progressindicator.cpp progressindicator.h + projectintropage.cpp projectintropage.h projectintropage.ui + proxyaction.cpp proxyaction.h + proxycredentialsdialog.cpp proxycredentialsdialog.h proxycredentialsdialog.ui + qrcparser.cpp qrcparser.h + qtcassert.cpp qtcassert.h + qtcolorbutton.cpp qtcolorbutton.h + qtcprocess.cpp qtcprocess.h + reloadpromptutils.cpp reloadpromptutils.h + removefiledialog.cpp removefiledialog.h removefiledialog.ui + runextensions.cpp runextensions.h + savedaction.cpp savedaction.h + savefile.cpp savefile.h + scopedswap.h + settingsaccessor.cpp settingsaccessor.h + settingsselector.cpp settingsselector.h + settingsutils.h + shellcommand.cpp shellcommand.h + shellcommandpage.cpp shellcommandpage.h + sizedarray.h + smallstring.h + smallstringfwd.h + smallstringio.h + smallstringiterator.h + smallstringlayout.h + smallstringliteral.h + smallstringmemory.h + smallstringvector.h + smallstringview.h + statuslabel.cpp statuslabel.h + stringutils.cpp stringutils.h + styledbar.cpp styledbar.h + stylehelper.cpp stylehelper.h + synchronousprocess.cpp synchronousprocess.h + templateengine.cpp templateengine.h + temporarydirectory.cpp temporarydirectory.h + temporaryfile.cpp temporaryfile.h + textfieldcheckbox.cpp textfieldcheckbox.h + textfieldcombobox.cpp textfieldcombobox.h + textfileformat.cpp textfileformat.h + textutils.cpp textutils.h + theme/theme.cpp theme/theme.h theme/theme_p.h + tooltip/effects.h + tooltip/reuse.h + tooltip/tips.cpp tooltip/tips.h + tooltip/tooltip.cpp tooltip/tooltip.h + touchbar/touchbar.h + treemodel.cpp treemodel.h + treeviewcombobox.cpp treeviewcombobox.h + uncommentselection.cpp uncommentselection.h + unixutils.cpp unixutils.h + url.cpp url.h + utils.qrc + utils_global.h + utilsicons.cpp utilsicons.h + variant.h + winutils.cpp winutils.h + wizard.cpp wizard.h + wizardpage.cpp wizardpage.h +) + +extend_qtc_target(Utils CONDITION WIN32 + SOURCES + consoleprocess_win.cpp + process_ctrlc_stub.cpp + touchbar/touchbar.cpp + DEPENDS + user32 iphlpapi ws2_32 shell32 + DEFINES + _UNICODE UNICODE + PUBLIC_DEFINES + _CRT_SECURE_NO_WARNINGS _SCL_SECURE_NO_WARNINGS +) + +extend_qtc_target(Utils CONDITION APPLE + SOURCES + consoleprocess_unix.cpp + fileutils_mac.mm fileutils_mac.h + processhandle_mac.mm + theme/theme_mac.mm theme/theme_mac.h + touchbar/touchbar_appdelegate_mac.mm touchbar/touchbar_appdelegate_mac_p.h + touchbar/touchbar_mac.mm touchbar/touchbar_mac_p.h + DEPENDS + ${FWFoundation} ${FWAppKit} +) + +extend_qtc_target(Utils CONDITION UNIX AND NOT APPLE + SOURCES + consoleprocess_unix.cpp + touchbar/touchbar.cpp +) + +if (WIN32) + add_qtc_executable(qtcreator_process_stub + SOURCES process_stub_win.c + DEPENDS shell32 + DEFINES _UNICODE UNICODE _CRT_SECURE_NO_WARNINGS + ) +else() + add_qtc_executable(qtcreator_process_stub SOURCES process_stub_unix.c) +endif() diff --git a/src/libs/utils/algorithm.h b/src/libs/utils/algorithm.h index bae78aacda..9e80755a3a 100644 --- a/src/libs/utils/algorithm.h +++ b/src/libs/utils/algorithm.h @@ -48,6 +48,323 @@ namespace Utils { +///////////////////////// +// anyOf +///////////////////////// +template<typename T, typename F> +bool anyOf(const T &container, F predicate); +template<typename T, typename R, typename S> +bool anyOf(const T &container, R (S::*predicate)() const); +template<typename T, typename R, typename S> +bool anyOf(const T &container, R S::*member); + +///////////////////////// +// count +///////////////////////// +template<typename T, typename F> +int count(const T &container, F predicate); + +///////////////////////// +// allOf +///////////////////////// +template<typename T, typename F> +bool allOf(const T &container, F predicate); + +///////////////////////// +// erase +///////////////////////// +template<typename T, typename F> +void erase(T &container, F predicate); + +///////////////////////// +// contains +///////////////////////// +template<typename T, typename F> +bool contains(const T &container, F function); +template<typename T, typename R, typename S> +bool contains(const T &container, R (S::*function)() const); +template<typename C, typename R, typename S> +bool contains(const C &container, R S::*member); + +///////////////////////// +// findOr +///////////////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT typename C::value_type findOr(const C &container, + typename C::value_type other, + F function); +template<typename T, typename R, typename S> +Q_REQUIRED_RESULT typename T::value_type findOr(const T &container, + typename T::value_type other, + R (S::*function)() const); +template<typename T, typename R, typename S> +Q_REQUIRED_RESULT typename T::value_type findOr(const T &container, + typename T::value_type other, + R S::*member); + +///////////////////////// +// findOrDefault +///////////////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, + typename C::value_type> +findOrDefault(const C &container, F function); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, + typename C::value_type> +findOrDefault(const C &container, R (S::*function)() const); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT typename std::enable_if_t<std::is_copy_assignable<typename C::value_type>::value, + typename C::value_type> +findOrDefault(const C &container, R S::*member); + +///////////////////////// +// indexOf +///////////////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT int indexOf(const C &container, F function); + +///////////////////////// +// maxElementOr +///////////////////////// +template<typename T> +typename T::value_type maxElementOr(const T &container, typename T::value_type other); + +///////////////////////// +// filtered +///////////////////////// +template<typename C, typename F> +Q_REQUIRED_RESULT C filtered(const C &container, F predicate); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT C filtered(const C &container, R (S::*predicate)() const); + +///////////////////////// +// partition +///////////////////////// +// Recommended usage: +// C hit; +// C miss; +// std::tie(hit, miss) = Utils::partition(container, predicate); +template<typename C, typename F> +Q_REQUIRED_RESULT std::tuple<C, C> partition(const C &container, F predicate); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT std::tuple<C, C> partition(const C &container, R (S::*predicate)() const); + +///////////////////////// +// filteredUnique +///////////////////////// +template<typename C> +Q_REQUIRED_RESULT C filteredUnique(const C &container); + +///////////////////////// +// qobject_container_cast +///////////////////////// +template<class T, template<typename> class Container, typename Base> +Container<T> qobject_container_cast(const Container<Base> &container); + +///////////////////////// +// static_container_cast +///////////////////////// +template<class T, template<typename> class Container, typename Base> +Container<T> static_container_cast(const Container<Base> &container); + +///////////////////////// +// sort +///////////////////////// +template<typename Container> +inline void sort(Container &container); +template<typename Container, typename Predicate> +inline void sort(Container &container, Predicate p); +template<typename Container, typename R, typename S> +inline void sort(Container &container, R S::*member); +template<typename Container, typename R, typename S> +inline void sort(Container &container, R (S::*function)() const); + +///////////////////////// +// reverseForeach +///////////////////////// +template<typename Container, typename Op> +inline void reverseForeach(const Container &c, const Op &operation); + +///////////////////////// +// toReferences +///////////////////////// +template<template<typename...> class ResultContainer, typename SourceContainer> +auto toReferences(SourceContainer &sources); +template<typename SourceContainer> +auto toReferences(SourceContainer &sources); + +///////////////////////// +// toConstReferences +///////////////////////// +template<template<typename...> class ResultContainer, typename SourceContainer> +auto toConstReferences(const SourceContainer &sources); +template<typename SourceContainer> +auto toConstReferences(const SourceContainer &sources); + +///////////////////////// +// take +///////////////////////// +template<class C, typename P> +Q_REQUIRED_RESULT optional<typename C::value_type> take(C &container, P predicate); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT decltype(auto) take(C &container, R S::*member); +template<typename C, typename R, typename S> +Q_REQUIRED_RESULT decltype(auto) take(C &container, R (S::*function)() const); + +///////////////////////// +// setUnionMerge +///////////////////////// +// Works like std::set_union but provides a merge function for items that match +// !(a > b) && !(b > a) which normally means that there is an "equal" match. +// It uses iterators to support move_iterators. +template<class InputIt1, class InputIt2, class OutputIt, class Merge, class Compare> +OutputIt setUnionMerge(InputIt1 first1, + InputIt1 last1, + InputIt2 first2, + InputIt2 last2, + OutputIt d_first, + Merge merge, + Compare comp); +template<class InputIt1, class InputIt2, class OutputIt, class Merge> +OutputIt setUnionMerge( + InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, OutputIt d_first, Merge merge); +template<class OutputContainer, class InputContainer1, class InputContainer2, class Merge, class Compare> +OutputContainer setUnionMerge(InputContainer1 &&input1, + InputContainer2 &&input2, + Merge merge, + Compare comp); +template<class OutputContainer, class InputContainer1, class InputContainer2, class Merge> +OutputContainer setUnionMerge(InputContainer1 &&input1, InputContainer2 &&input2, Merge merge); + +///////////////////////// +// usize / ssize +///////////////////////// +template<typename Container> +std::make_unsigned_t<typename Container::size_type> usize(Container container); +template<typename Container> +std::make_signed_t<typename Container::size_type> ssize(Container container); + +///////////////////////// +// setUnion +///////////////////////// +template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare> +OutputIterator set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Compare comp); +template<typename InputIterator1, typename InputIterator2, typename OutputIterator> +OutputIterator set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result); + +///////////////////////// +// transform +///////////////////////// +// function without result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container type + typename F> // function type +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function); + +// function with result type deduction: +template<template<typename> class C, // result container type + typename SC, // input container type + typename F, // function type + typename Value = typename std::decay_t<SC>::value_type, + typename Result = std::decay_t<std::result_of_t<F(Value &)>>, + typename ResultContainer = C<Result>> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function); +template<template<typename, typename> class C, // result container type + typename SC, // input container type + typename F, // function type + typename Value = typename std::decay_t<SC>::value_type, + typename Result = std::decay_t<std::result_of_t<F(Value &)>>, + typename ResultContainer = C<Result, std::allocator<Result>>> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function); + +// member function without result type deduction: +template<template<typename...> class C, // result container type + typename SC, // input container type + typename R, + typename S> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R (S::*p)() const); + +// member function with result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container type + typename R, + typename S> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R (S::*p)() const); + +// member without result type deduction: +template<typename ResultContainer, // complete result container type + typename SC, // input container + typename R, + typename S> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R S::*p); + +// member with result type deduction: +template<template<typename...> class C, // result container + typename SC, // input container + typename R, + typename S> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, R S::*p); + +// same container types for input and output, const input +// function: +template<template<typename...> class C, // container type + typename F, // function type + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, F function); + +// same container types for input and output, const input +// member function: +template<template<typename...> class C, // container type + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, R (S::*p)() const); + +// same container types for input and output, const input +// members: +template<template<typename...> class C, // container + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(const C<CArgs...> &container, R S::*p); + +// same container types for input and output, non-const input +// function: +template<template<typename...> class C, // container type + typename F, // function type + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, F function); + +// same container types for input and output, non-const input +// member function: +template<template<typename...> class C, // container type + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, R (S::*p)() const); + +// same container types for input and output, non-const input +// members: +template<template<typename...> class C, // container + typename R, + typename S, + typename... CArgs> // Arguments to SC +Q_REQUIRED_RESULT decltype(auto) transform(C<CArgs...> &container, R S::*p); + +///////////////////////////////////////////////////////////////////////////// +//////// Implementations ////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////// + ////////////////// // anyOf ///////////////// @@ -367,25 +684,23 @@ decltype(auto) transform(SC &&container, F function) // function with result type deduction: template<template<typename> class C, // result container type - typename SC, // input container type - typename F, // function type - typename Value = typename std::decay_t<SC>::value_type, - typename Result = std::decay_t<std::result_of_t<F(Value&)>>, - typename ResultContainer = C<Result>> -Q_REQUIRED_RESULT -decltype(auto) transform(SC &&container, F function) + typename SC, // input container type + typename F, // function type + typename Value, + typename Result, + typename ResultContainer> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function) { return transform<ResultContainer>(std::forward<SC>(container), function); } template<template<typename, typename> class C, // result container type - typename SC, // input container type - typename F, // function type - typename Value = typename std::decay_t<SC>::value_type, - typename Result = std::decay_t<std::result_of_t<F(Value&)>>, - typename ResultContainer = C<Result, std::allocator<Result>>> -Q_REQUIRED_RESULT -decltype(auto) transform(SC &&container, F function) + typename SC, // input container type + typename F, // function type + typename Value, + typename Result, + typename ResultContainer> +Q_REQUIRED_RESULT decltype(auto) transform(SC &&container, F function) { return transform<ResultContainer>(std::forward<SC>(container), function); } @@ -862,4 +1177,70 @@ std::make_signed_t<typename Container::size_type> ssize(Container container) { return static_cast<std::make_signed_t<typename Container::size_type>>(container.size()); } + +template<typename Compare> +struct CompareIter +{ + Compare compare; + + explicit constexpr CompareIter(Compare compare) + : compare(std::move(compare)) + {} + + template<typename Iterator1, typename Iterator2> + constexpr bool operator()(Iterator1 it1, Iterator2 it2) + { + return bool(compare(*it1, *it2)); + } +}; + +template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare> +OutputIterator set_union_impl(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Compare comp) +{ + auto compare = CompareIter<Compare>(comp); + + while (first1 != last1 && first2 != last2) { + if (compare(first1, first2)) { + *result = *first1; + ++first1; + } else if (compare(first2, first1)) { + *result = *first2; + ++first2; + } else { + *result = *first1; + ++first1; + ++first2; + } + ++result; + } + + return std::copy(first2, last2, std::copy(first1, last1, result)); +} + +template<typename InputIterator1, typename InputIterator2, typename OutputIterator, typename Compare> +OutputIterator set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result, + Compare comp) +{ + return Utils::set_union_impl(first1, last1, first2, last2, result, comp); +} + +template<typename InputIterator1, typename InputIterator2, typename OutputIterator> +OutputIterator set_union(InputIterator1 first1, + InputIterator1 last1, + InputIterator2 first2, + InputIterator2 last2, + OutputIterator result) +{ + return Utils::set_union_impl( + first1, last1, first2, last2, result, std::less<typename InputIterator1::value_type>{}); +} } // namespace Utils diff --git a/src/libs/utils/basetreeview.cpp b/src/libs/utils/basetreeview.cpp index d77b8d4d55..352b17bb4b 100644 --- a/src/libs/utils/basetreeview.cpp +++ b/src/libs/utils/basetreeview.cpp @@ -183,7 +183,7 @@ public: QAbstractItemModel *m = q->model(); for (int i = 0; i < 100 && a.isValid(); ++i) { const QString s = m->data(a).toString(); - int w = fm.width(s) + 10; + int w = fm.horizontalAdvance(s) + 10; if (column == 0) { for (QModelIndex b = a.parent(); b.isValid(); b = b.parent()) w += ind; @@ -204,7 +204,8 @@ public: QTC_ASSERT(m, return -1); QFontMetrics fm = q->fontMetrics(); - int minimum = fm.width(m->headerData(column, Qt::Horizontal).toString()) + 2 * fm.width(QLatin1Char('m')); + int minimum = fm.horizontalAdvance(m->headerData(column, Qt::Horizontal).toString()) + + 2 * fm.horizontalAdvance(QLatin1Char('m')); considerItems(column, q->indexAt(QPoint(1, 1)), &minimum, false); QVariant extraIndices = m->data(QModelIndex(), BaseTreeView::ExtraIndicesForColumnWidth); @@ -254,8 +255,8 @@ public: // when we have that size already, in that case minimize. if (currentSize == suggestedSize) { QFontMetrics fm = q->fontMetrics(); - int headerSize = fm.width(q->model()->headerData(logicalIndex, Qt::Horizontal).toString()); - int minSize = 10 * fm.width(QLatin1Char('x')); + int headerSize = fm.horizontalAdvance(q->model()->headerData(logicalIndex, Qt::Horizontal).toString()); + int minSize = 10 * fm.horizontalAdvance(QLatin1Char('x')); targetSize = qMax(minSize, headerSize); } diff --git a/src/libs/utils/buildablehelperlibrary.cpp b/src/libs/utils/buildablehelperlibrary.cpp index 3c53e09b2a..33ba624dc4 100644 --- a/src/libs/utils/buildablehelperlibrary.cpp +++ b/src/libs/utils/buildablehelperlibrary.cpp @@ -27,11 +27,13 @@ #include "hostosinfo.h" #include "synchronousprocess.h" -#include <QDir> #include <QDateTime> #include <QDebug> +#include <QDir> #include <QRegExp> +#include <set> + namespace Utils { bool BuildableHelperLibrary::isQtChooser(const QFileInfo &info) @@ -71,32 +73,54 @@ static bool isQmake(const QString &path) return !BuildableHelperLibrary::qtVersionForQMake(fi.absoluteFilePath()).isEmpty(); } -FileName BuildableHelperLibrary::findSystemQt(const Environment &env) +static FilePath findQmakeInDir(const FilePath &path) { - const QString qmake = QLatin1String("qmake"); - FileNameList paths = env.path(); - foreach (const FileName &path, paths) { - if (path.isEmpty()) - continue; - - QDir dir(path.toString()); + if (path.isEmpty()) + return FilePath(); + + const QString qmake = "qmake"; + QDir dir(path.toString()); + if (dir.exists(qmake)) { + const QString qmakePath = dir.absoluteFilePath(qmake); + if (isQmake(qmakePath)) + return FilePath::fromString(qmakePath); + } - if (dir.exists(qmake)) { - const QString qmakePath = dir.absoluteFilePath(qmake); - if (isQmake(qmakePath)) - return FileName::fromString(qmakePath); - } + // Prefer qmake-qt5 to qmake-qt4 by sorting the filenames in reverse order. + const QFileInfoList candidates = dir.entryInfoList( + BuildableHelperLibrary::possibleQMakeCommands(), + QDir::Files, QDir::Name | QDir::Reversed); + for (const QFileInfo &fi : candidates) { + if (fi.fileName() == qmake) + continue; + if (isQmake(fi.absoluteFilePath())) + return FilePath::fromFileInfo(fi); + } + return FilePath(); +} - // Prefer qmake-qt5 to qmake-qt4 by sorting the filenames in reverse order. - foreach (const QFileInfo &fi, dir.entryInfoList(possibleQMakeCommands(), QDir::Files, QDir::Name | QDir::Reversed)) { - if (fi.fileName() == qmake) - continue; +FilePath BuildableHelperLibrary::findSystemQt(const Environment &env) +{ + const FilePathList list = findQtsInEnvironment(env, 1); + return list.size() == 1 ? list.first() : FilePath(); +} - if (isQmake(fi.absoluteFilePath())) - return FileName(fi); - } +FilePathList BuildableHelperLibrary::findQtsInEnvironment(const Environment &env, int maxCount) +{ + FilePathList qmakeList; + std::set<QString> canonicalEnvPaths; + const FilePathList paths = env.path(); + for (const FilePath &path : paths) { + if (!canonicalEnvPaths.insert(path.toFileInfo().canonicalFilePath()).second) + continue; + const FilePath qmake = findQmakeInDir(path); + if (qmake.isEmpty()) + continue; + qmakeList << qmake; + if (maxCount != -1 && qmakeList.size() == maxCount) + break; } - return FileName(); + return qmakeList; } QString BuildableHelperLibrary::qtVersionForQMake(const QString &qmakePath) @@ -165,7 +189,7 @@ bool BuildableHelperLibrary::copyFiles(const QString &sourcePath, QString *errorMessage) { // try remove the directory - if (!FileUtils::removeRecursively(FileName::fromString(targetDirectory), errorMessage)) + if (!FileUtils::removeRecursively(FilePath::fromString(targetDirectory), errorMessage)) return false; if (!QDir().mkpath(targetDirectory)) { *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", "The target directory %1 could not be created.").arg(targetDirectory); @@ -196,7 +220,7 @@ bool BuildableHelperLibrary::copyFiles(const QString &sourcePath, // Helper: Run a build process with merged stdout/stderr static inline bool runBuildProcessI(QProcess &proc, - const FileName &binary, + const FilePath &binary, const QStringList &args, int timeoutS, bool ignoreNonNullExitCode, @@ -237,7 +261,7 @@ static inline bool runBuildProcessI(QProcess &proc, // Run a build process with merged stdout/stderr and qWarn about errors. static bool runBuildProcess(QProcess &proc, - const FileName &binary, + const FilePath &binary, const QStringList &args, int timeoutS, bool ignoreNonNullExitCode, @@ -276,7 +300,7 @@ bool BuildableHelperLibrary::buildHelper(const BuildHelperArguments &arguments, arguments.directory)); log->append(newline); - const FileName makeFullPath = arguments.environment.searchInPath(arguments.makeCommand); + const FilePath makeFullPath = arguments.environment.searchInPath(arguments.makeCommand); if (QFileInfo::exists(arguments.directory + QLatin1String("/Makefile"))) { if (makeFullPath.isEmpty()) { *errorMessage = QCoreApplication::translate("ProjectExplorer::DebuggingHelperLibrary", diff --git a/src/libs/utils/buildablehelperlibrary.h b/src/libs/utils/buildablehelperlibrary.h index c818749f2b..45d37d65c4 100644 --- a/src/libs/utils/buildablehelperlibrary.h +++ b/src/libs/utils/buildablehelperlibrary.h @@ -37,7 +37,8 @@ class QTCREATOR_UTILS_EXPORT BuildableHelperLibrary public: // returns the full path to the first qmake, qmake-qt4, qmake4 that has // at least version 2.0.0 and thus is a qt4 qmake - static FileName findSystemQt(const Environment &env); + static FilePath findSystemQt(const Environment &env); + static FilePathList findQtsInEnvironment(const Environment &env, int maxCount = -1); static bool isQtChooser(const QFileInfo &info); static QString qtChooserToQmakePath(const QString &path); // return true if the qmake at qmakePath is a Qt (used by QtVersion) @@ -60,9 +61,9 @@ public: QString directory; Environment environment; - FileName qmakeCommand; + FilePath qmakeCommand; QString targetMode; - FileName mkspec; + FilePath mkspec; QString proFilename; QStringList qmakeArguments; diff --git a/src/libs/utils/checkablemessagebox.cpp b/src/libs/utils/checkablemessagebox.cpp index 857711fd8a..3ec411cfb3 100644 --- a/src/libs/utils/checkablemessagebox.cpp +++ b/src/libs/utils/checkablemessagebox.cpp @@ -32,6 +32,7 @@ #include <QLabel> #include <QPushButton> #include <QSettings> +#include <QStyle> /*! \class Utils::CheckableMessageBox @@ -107,6 +108,7 @@ public: QCheckBox *checkBox = nullptr; QDialogButtonBox *buttonBox = nullptr; QAbstractButton *clickedButton = nullptr; + QMessageBox::Icon icon = QMessageBox::NoIcon; }; CheckableMessageBox::CheckableMessageBox(QWidget *parent) : @@ -148,17 +150,53 @@ void CheckableMessageBox::setText(const QString &t) d->messageLabel->setText(t); } -QPixmap CheckableMessageBox::iconPixmap() const -{ - if (const QPixmap *p = d->pixmapLabel->pixmap()) - return QPixmap(*p); +QMessageBox::Icon CheckableMessageBox::icon() const +{ + return d->icon; +} + +// See QMessageBoxPrivate::standardIcon +static QPixmap pixmapForIcon(QMessageBox::Icon icon, QWidget *w) +{ + const QStyle *style = w ? w->style() : QApplication::style(); + const int iconSize = style->pixelMetric(QStyle::PM_MessageBoxIconSize, nullptr, w); + QIcon tmpIcon; + switch (icon) { + case QMessageBox::Information: + tmpIcon = style->standardIcon(QStyle::SP_MessageBoxInformation, nullptr, w); + break; + case QMessageBox::Warning: + tmpIcon = style->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, w); + break; + case QMessageBox::Critical: + tmpIcon = style->standardIcon(QStyle::SP_MessageBoxCritical, nullptr, w); + break; + case QMessageBox::Question: + tmpIcon = style->standardIcon(QStyle::SP_MessageBoxQuestion, nullptr, w); + break; + default: + break; + } + if (!tmpIcon.isNull()) { + QWindow *window = nullptr; + if (w) { + window = w->windowHandle(); + if (!window) { + if (const QWidget *nativeParent = w->nativeParentWidget()) + window = nativeParent->windowHandle(); + } + } + return tmpIcon.pixmap(window, QSize(iconSize, iconSize)); + } return QPixmap(); } -void CheckableMessageBox::setIconPixmap(const QPixmap &p) +void CheckableMessageBox::setIcon(QMessageBox::Icon icon) { - d->pixmapLabel->setPixmap(p); - d->pixmapLabel->setVisible(!p.isNull()); + d->icon = icon; + const QPixmap pixmap = pixmapForIcon(icon, this); + d->pixmapLabel->setPixmap(pixmap); + d->pixmapLabel->setVisible(!pixmap.isNull()); } bool CheckableMessageBox::isChecked() const @@ -239,7 +277,7 @@ CheckableMessageBox::question(QWidget *parent, { CheckableMessageBox mb(parent); mb.setWindowTitle(title); - mb.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Question)); + mb.setIcon(QMessageBox::Question); mb.setText(question); mb.setCheckBoxText(checkBoxText); mb.setChecked(*checkBoxSetting); @@ -261,7 +299,7 @@ CheckableMessageBox::information(QWidget *parent, { CheckableMessageBox mb(parent); mb.setWindowTitle(title); - mb.setIconPixmap(QMessageBox::standardIcon(QMessageBox::Information)); + mb.setIcon(QMessageBox::Information); mb.setText(text); mb.setCheckBoxText(checkBoxText); mb.setChecked(*checkBoxSetting); @@ -297,9 +335,7 @@ void initDoNotAskAgainMessageBox(CheckableMessageBox &messageBox, const QString DoNotAskAgainType type) { messageBox.setWindowTitle(title); - messageBox.setIconPixmap(QMessageBox::standardIcon(type == Information - ? QMessageBox::Information - : QMessageBox::Question)); + messageBox.setIcon(type == Information ? QMessageBox::Information : QMessageBox::Question); messageBox.setText(text); messageBox.setCheckBoxVisible(true); messageBox.setCheckBoxText(type == Information ? CheckableMessageBox::msgDoNotShowAgain() diff --git a/src/libs/utils/checkablemessagebox.h b/src/libs/utils/checkablemessagebox.h index c6359f72cd..042e88aa96 100644 --- a/src/libs/utils/checkablemessagebox.h +++ b/src/libs/utils/checkablemessagebox.h @@ -42,7 +42,7 @@ class QTCREATOR_UTILS_EXPORT CheckableMessageBox : public QDialog { Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText) - Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(QMessageBox::Icon icon READ icon WRITE setIcon) Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) @@ -109,9 +109,8 @@ public: QDialogButtonBox::StandardButton defaultButton() const; void setDefaultButton(QDialogButtonBox::StandardButton s); - // See static QMessageBox::standardPixmap() - QPixmap iconPixmap() const; - void setIconPixmap (const QPixmap &p); + QMessageBox::Icon icon() const; + void setIcon(QMessageBox::Icon icon); // Query the result QAbstractButton *clickedButton() const; diff --git a/src/libs/utils/completingtextedit.cpp b/src/libs/utils/completingtextedit.cpp index f4d7b0441c..3a34973c00 100644 --- a/src/libs/utils/completingtextedit.cpp +++ b/src/libs/utils/completingtextedit.cpp @@ -114,7 +114,7 @@ void CompletingTextEdit::setCompleter(QCompleter *c) completer()->setWidget(this); completer()->setCompletionMode(QCompleter::PopupCompletion); - connect(completer(), static_cast<void (QCompleter::*)(const QString &)>(&QCompleter::activated), + connect(completer(), QOverload<const QString &>::of(&QCompleter::activated), this, [this](const QString &str) { d->insertCompletion(str); }); } diff --git a/src/libs/utils/consoleprocess.h b/src/libs/utils/consoleprocess.h index 28be67f0bf..3dec9838c3 100644 --- a/src/libs/utils/consoleprocess.h +++ b/src/libs/utils/consoleprocess.h @@ -70,7 +70,9 @@ public: QProcess::ProcessError error() const; QString errorString() const; - bool start(const QString &program, const QString &args); + enum class MetaCharMode { Abort, Ignore }; + bool start(const QString &program, const QString &args, + MetaCharMode metaCharMode = MetaCharMode::Abort); public slots: void stop(); diff --git a/src/libs/utils/consoleprocess_unix.cpp b/src/libs/utils/consoleprocess_unix.cpp index 92b69952f6..43afffa0a2 100644 --- a/src/libs/utils/consoleprocess_unix.cpp +++ b/src/libs/utils/consoleprocess_unix.cpp @@ -65,7 +65,7 @@ void ConsoleProcess::setSettings(QSettings *settings) d->m_settings = settings; } -bool ConsoleProcess::start(const QString &program, const QString &args) +bool ConsoleProcess::start(const QString &program, const QString &args, MetaCharMode metaCharMode) { if (isRunning()) return false; @@ -75,7 +75,8 @@ bool ConsoleProcess::start(const QString &program, const QString &args) QtcProcess::SplitError perr; QtcProcess::Arguments pargs = QtcProcess::prepareArgs(args, &perr, HostOsInfo::hostOs(), - &d->m_environment, &d->m_workingDir); + &d->m_environment, &d->m_workingDir, + metaCharMode == MetaCharMode::Abort); QString pcmd; if (perr == QtcProcess::SplitOk) { pcmd = program; @@ -436,12 +437,6 @@ bool ConsoleProcess::startTerminalEmulator(QSettings *settings, const QString &w const Utils::Environment &env) { const TerminalCommand term = terminalEmulator(settings); -#if QT_VERSION < QT_VERSION_CHECK(5, 10, 0) - // for 5.9 and below we cannot set the environment - Q_UNUSED(env); - return QProcess::startDetached(term.command, QtcProcess::splitArgs(term.openArgs), - workingDir); -#else QProcess process; process.setProgram(term.command); process.setArguments(QtcProcess::splitArgs(term.openArgs)); @@ -449,7 +444,6 @@ bool ConsoleProcess::startTerminalEmulator(QSettings *settings, const QString &w process.setWorkingDirectory(workingDir); return process.startDetached(); -#endif } } // namespace Utils diff --git a/src/libs/utils/consoleprocess_win.cpp b/src/libs/utils/consoleprocess_win.cpp index 2a68ddaba8..be65c42cc8 100644 --- a/src/libs/utils/consoleprocess_win.cpp +++ b/src/libs/utils/consoleprocess_win.cpp @@ -51,8 +51,10 @@ qint64 ConsoleProcess::applicationMainThreadID() const return d->m_appMainThreadId; } -bool ConsoleProcess::start(const QString &program, const QString &args) +bool ConsoleProcess::start(const QString &program, const QString &args, MetaCharMode metaCharMode) { + Q_UNUSED(metaCharMode); + if (isRunning()) return false; diff --git a/src/libs/utils/cpplanguage_details.h b/src/libs/utils/cpplanguage_details.h index 43474d03cb..e9c9a5aa40 100644 --- a/src/libs/utils/cpplanguage_details.h +++ b/src/libs/utils/cpplanguage_details.h @@ -29,9 +29,10 @@ namespace Utils { -enum class Language : unsigned char { C, Cxx }; +enum class Language : unsigned char { None, C, Cxx }; enum class LanguageVersion : unsigned char { + None, C89, C99, C11, diff --git a/src/libs/utils/delegates.cpp b/src/libs/utils/delegates.cpp index a677c8c992..2240e8a6a4 100644 --- a/src/libs/utils/delegates.cpp +++ b/src/libs/utils/delegates.cpp @@ -84,7 +84,7 @@ void AnnotatedItemDelegate::paint(QPainter *painter, painter->save(); painter->setPen(disabled.color(QPalette::WindowText)); - static int extra = opt.fontMetrics.width(m_delimiter) + 10; + static int extra = opt.fontMetrics.horizontalAdvance(m_delimiter) + 10; const QPixmap &pixmap = opt.icon.pixmap(opt.decorationSize); const QRect &iconRect = style->itemPixmapRect(opt.rect, opt.decorationAlignment, pixmap); const QRect &displayRect = style->itemTextRect(opt.fontMetrics, opt.rect, diff --git a/src/libs/utils/detailsbutton.cpp b/src/libs/utils/detailsbutton.cpp index 5f2c88eca3..329975afdb 100644 --- a/src/libs/utils/detailsbutton.cpp +++ b/src/libs/utils/detailsbutton.cpp @@ -79,7 +79,7 @@ DetailsButton::DetailsButton(QWidget *parent) : QAbstractButton(parent), m_fader QSize DetailsButton::sizeHint() const { // TODO: Adjust this when icons become available! - const int w = fontMetrics().width(text()) + 32; + const int w = fontMetrics().horizontalAdvance(text()) + 32; if (HostOsInfo::isMacHost()) return QSize(w, 34); return QSize(w, 22); diff --git a/src/libs/utils/elidinglabel.cpp b/src/libs/utils/elidinglabel.cpp index e584bd8bb6..1d90665af5 100644 --- a/src/libs/utils/elidinglabel.cpp +++ b/src/libs/utils/elidinglabel.cpp @@ -66,7 +66,7 @@ void ElidingLabel::paintEvent(QPaintEvent *) QRect contents = contentsRect().adjusted(m, m, -m, -m); QFontMetrics fm = fontMetrics(); QString txt = text(); - if (txt.length() > 4 && fm.width(txt) > contents.width()) { + if (txt.length() > 4 && fm.horizontalAdvance(txt) > contents.width()) { setToolTip(txt); txt = fm.elidedText(txt, m_elideMode, contents.width()); } else { diff --git a/src/libs/utils/environment.cpp b/src/libs/utils/environment.cpp index a82d6c27f0..d61a75ebf7 100644 --- a/src/libs/utils/environment.cpp +++ b/src/libs/utils/environment.cpp @@ -392,14 +392,14 @@ void Environment::clear() m_values.clear(); } -FileName Environment::searchInDirectory(const QStringList &execs, const FileName &directory, - QSet<FileName> &alreadyChecked) const +FilePath Environment::searchInDirectory(const QStringList &execs, const FilePath &directory, + QSet<FilePath> &alreadyChecked) const { const int checkedCount = alreadyChecked.count(); alreadyChecked.insert(directory); if (directory.isEmpty() || alreadyChecked.count() == checkedCount) - return FileName(); + return FilePath(); const QString dir = directory.toString(); @@ -407,9 +407,9 @@ FileName Environment::searchInDirectory(const QStringList &execs, const FileName for (const QString &exec : execs) { fi.setFile(dir, exec); if (fi.isFile() && fi.isExecutable()) - return FileName::fromString(fi.absoluteFilePath()); + return FilePath::fromString(fi.absoluteFilePath()); } - return FileName(); + return FilePath(); } QStringList Environment::appendExeExtensions(const QString &executable) const @@ -435,23 +435,25 @@ bool Environment::isSameExecutable(const QString &exe1, const QString &exe2) con const QStringList exe2List = appendExeExtensions(exe2); for (const QString &i1 : exe1List) { for (const QString &i2 : exe2List) { - const FileName f1 = FileName::fromString(i1); - const FileName f2 = FileName::fromString(i2); + const FilePath f1 = FilePath::fromString(i1); + const FilePath f2 = FilePath::fromString(i2); if (f1 == f2) return true; if (FileUtils::resolveSymlinks(f1) == FileUtils::resolveSymlinks(f2)) return true; + if (FileUtils::fileId(f1) == FileUtils::fileId(f2)) + return true; } } return false; } -FileName Environment::searchInPath(const QString &executable, - const FileNameList &additionalDirs, +FilePath Environment::searchInPath(const QString &executable, + const FilePathList &additionalDirs, const PathFilter &func) const { if (executable.isEmpty()) - return FileName(); + return FilePath(); const QString exec = QDir::cleanPath(expandVariables(executable)); const QFileInfo fi(exec); @@ -462,34 +464,34 @@ FileName Environment::searchInPath(const QString &executable, for (const QString &path : execs) { QFileInfo pfi = QFileInfo(path); if (pfi.isFile() && pfi.isExecutable()) - return FileName::fromString(path); + return FilePath::fromString(path); } - return FileName::fromString(exec); + return FilePath::fromString(exec); } - QSet<FileName> alreadyChecked; - for (const FileName &dir : additionalDirs) { - FileName tmp = searchInDirectory(execs, dir, alreadyChecked); + QSet<FilePath> alreadyChecked; + for (const FilePath &dir : additionalDirs) { + FilePath tmp = searchInDirectory(execs, dir, alreadyChecked); if (!tmp.isEmpty() && (!func || func(tmp))) return tmp; } if (executable.contains('/')) - return FileName(); + return FilePath(); - for (const FileName &p : path()) { - FileName tmp = searchInDirectory(execs, p, alreadyChecked); + for (const FilePath &p : path()) { + FilePath tmp = searchInDirectory(execs, p, alreadyChecked); if (!tmp.isEmpty() && (!func || func(tmp))) return tmp; } - return FileName(); + return FilePath(); } -FileNameList Environment::path() const +FilePathList Environment::path() const { const QStringList pathComponents = value("PATH") .split(OsSpecificAspects::pathListSeparator(m_osType), QString::SkipEmptyParts); - return Utils::transform(pathComponents, &FileName::fromUserInput); + return Utils::transform(pathComponents, &FilePath::fromUserInput); } QString Environment::value(const QString &key) const @@ -688,6 +690,11 @@ QString Environment::expandVariables(const QString &input) const return result; } +FilePath Environment::expandVariables(const FilePath &variables) const +{ + return FilePath::fromString(expandVariables(variables.toString())); +} + QStringList Environment::expandVariables(const QStringList &variables) const { return Utils::transform(variables, [this](const QString &i) { return expandVariables(i); }); diff --git a/src/libs/utils/environment.h b/src/libs/utils/environment.h index cab78a082b..f1e957ab6d 100644 --- a/src/libs/utils/environment.h +++ b/src/libs/utils/environment.h @@ -124,17 +124,18 @@ public: Environment::const_iterator constEnd() const; Environment::const_iterator constFind(const QString &name) const; - using PathFilter = std::function<bool(const FileName &)>; - FileName searchInPath(const QString &executable, - const FileNameList &additionalDirs = FileNameList(), + using PathFilter = std::function<bool(const FilePath &)>; + FilePath searchInPath(const QString &executable, + const FilePathList &additionalDirs = FilePathList(), const PathFilter &func = PathFilter()) const; - FileNameList path() const; + FilePathList path() const; QStringList appendExeExtensions(const QString &executable) const; bool isSameExecutable(const QString &exe1, const QString &exe2) const; QString expandVariables(const QString &input) const; + FilePath expandVariables(const FilePath &input) const; QStringList expandVariables(const QStringList &input) const; bool operator!=(const Environment &other) const; @@ -143,8 +144,8 @@ public: static void modifySystemEnvironment(const QList<EnvironmentItem> &list); // use with care!!! private: - FileName searchInDirectory(const QStringList &execs, const FileName &directory, - QSet<FileName> &alreadyChecked) const; + FilePath searchInDirectory(const QStringList &execs, const FilePath &directory, + QSet<FilePath> &alreadyChecked) const; QMap<QString, QString> m_values; OsType m_osType; }; diff --git a/src/libs/utils/filecrumblabel.cpp b/src/libs/utils/filecrumblabel.cpp index a4bbe37c44..cf03af553c 100644 --- a/src/libs/utils/filecrumblabel.cpp +++ b/src/libs/utils/filecrumblabel.cpp @@ -38,22 +38,22 @@ FileCrumbLabel::FileCrumbLabel(QWidget *parent) setTextFormat(Qt::RichText); setWordWrap(true); connect(this, &QLabel::linkActivated, this, [this](const QString &url) { - emit pathClicked(FileName::fromString(QUrl(url).toLocalFile())); + emit pathClicked(FilePath::fromString(QUrl(url).toLocalFile())); }); - setPath(FileName()); + setPath(FilePath()); } -static QString linkForPath(const FileName &path, const QString &display) +static QString linkForPath(const FilePath &path, const QString &display) { return "<a href=\"" + QUrl::fromLocalFile(path.toString()).toString(QUrl::FullyEncoded) + "\">" + display + "</a>"; } -void FileCrumbLabel::setPath(const FileName &path) +void FileCrumbLabel::setPath(const FilePath &path) { QStringList links; - FileName current = path; + FilePath current = path; while (!current.isEmpty()) { const QString fileName = current.fileName(); if (!fileName.isEmpty()) { diff --git a/src/libs/utils/filecrumblabel.h b/src/libs/utils/filecrumblabel.h index 6320b115c0..05dd050812 100644 --- a/src/libs/utils/filecrumblabel.h +++ b/src/libs/utils/filecrumblabel.h @@ -38,10 +38,10 @@ class QTCREATOR_UTILS_EXPORT FileCrumbLabel : public QLabel public: FileCrumbLabel(QWidget *parent = nullptr); - void setPath(const FileName &path); + void setPath(const FilePath &path); signals: - void pathClicked(const FileName &path); + void pathClicked(const FilePath &path); }; } // Utils diff --git a/src/libs/utils/fileinprojectfinder.cpp b/src/libs/utils/fileinprojectfinder.cpp index 2be64260e8..3dbde17792 100644 --- a/src/libs/utils/fileinprojectfinder.cpp +++ b/src/libs/utils/fileinprojectfinder.cpp @@ -24,15 +24,20 @@ ****************************************************************************/ #include "fileinprojectfinder.h" + +#include "algorithm.h" #include "fileutils.h" #include "hostosinfo.h" +#include "qrcparser.h" #include "qtcassert.h" +#include <QCursor> #include <QDebug> +#include <QDir> #include <QFileInfo> #include <QLoggingCategory> +#include <QMenu> #include <QUrl> -#include <QDir> #include <algorithm> @@ -77,7 +82,7 @@ static bool checkPath(const QString &candidate, int matchLength, FileInProjectFinder::FileInProjectFinder() = default; FileInProjectFinder::~FileInProjectFinder() = default; -void FileInProjectFinder::setProjectDirectory(const FileName &absoluteProjectPath) +void FileInProjectFinder::setProjectDirectory(const FilePath &absoluteProjectPath) { if (absoluteProjectPath == m_projectDir) return; @@ -90,21 +95,22 @@ void FileInProjectFinder::setProjectDirectory(const FileName &absoluteProjectPat m_cache.clear(); } -FileName FileInProjectFinder::projectDirectory() const +FilePath FileInProjectFinder::projectDirectory() const { return m_projectDir; } -void FileInProjectFinder::setProjectFiles(const FileNameList &projectFiles) +void FileInProjectFinder::setProjectFiles(const FilePathList &projectFiles) { if (m_projectFiles == projectFiles) return; m_projectFiles = projectFiles; m_cache.clear(); + m_qrcUrlFinder.setProjectFiles(projectFiles); } -void FileInProjectFinder::setSysroot(const FileName &sysroot) +void FileInProjectFinder::setSysroot(const FilePath &sysroot) { if (m_sysroot == sysroot) return; @@ -113,7 +119,7 @@ void FileInProjectFinder::setSysroot(const FileName &sysroot) m_cache.clear(); } -void FileInProjectFinder::addMappedPath(const FileName &localFilePath, const QString &remoteFilePath) +void FileInProjectFinder::addMappedPath(const FilePath &localFilePath, const QString &remoteFilePath) { const QStringList segments = remoteFilePath.split('/', QString::SkipEmptyParts); @@ -136,18 +142,29 @@ void FileInProjectFinder::addMappedPath(const FileName &localFilePath, const QSt folder specified. Third, we walk the list of project files, and search for a file name match there. If all fails, it returns the original path from the file URL. */ -QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const +FilePathList FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const { qCDebug(finderLog) << "FileInProjectFinder: trying to find file" << fileUrl.toString() << "..."; + if (fileUrl.scheme() == "qrc" || fileUrl.toString().startsWith(':')) { + const FilePathList result = m_qrcUrlFinder.find(fileUrl); + if (!result.isEmpty()) { + if (success) + *success = true; + return result; + } + } + QString originalPath = fileUrl.toLocalFile(); if (originalPath.isEmpty()) // e.g. qrc:// originalPath = fileUrl.path(); - QString result = originalPath; + FilePathList result; bool found = findFileOrDirectory(originalPath, [&](const QString &fileName, int) { - result = fileName; + result << FilePath::fromString(fileName); }); + if (!found) + result << FilePath::fromString(originalPath); if (success) *success = found; @@ -155,12 +172,12 @@ QString FileInProjectFinder::findFile(const QUrl &fileUrl, bool *success) const return result; } -bool FileInProjectFinder::handleSuccess(const QString &originalPath, const QString &found, +bool FileInProjectFinder::handleSuccess(const QString &originalPath, const QStringList &found, int matchLength, const char *where) const { qCDebug(finderLog) << "FileInProjectFinder: found" << found << where; CacheEntry entry; - entry.path = found; + entry.paths = found; entry.matchLength = matchLength; m_cache.insert(originalPath, entry); return true; @@ -189,8 +206,10 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH if (node) { if (!node->localPath.isEmpty()) { const QString localPath = node->localPath.toString(); - if (checkPath(localPath, origLength, fileHandler, directoryHandler)) - return handleSuccess(originalPath, localPath, origLength, "in mapped paths"); + if (checkPath(localPath, origLength, fileHandler, directoryHandler)) { + return handleSuccess(originalPath, QStringList(localPath), origLength, + "in mapped paths"); + } } else if (directoryHandler) { directoryHandler(node->children.keys(), origLength); qCDebug(finderLog) << "FileInProjectFinder: found virtual directory" << originalPath @@ -203,13 +222,18 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH if (it != m_cache.end()) { qCDebug(finderLog) << "FileInProjectFinder: checking cache ..."; // check if cached path is still there - const CacheEntry &candidate = it.value(); - if (checkPath(candidate.path, candidate.matchLength, fileHandler, directoryHandler)) { - qCDebug(finderLog) << "FileInProjectFinder: found" << candidate.path << "in the cache"; - return true; - } else { - m_cache.erase(it); + CacheEntry &candidate = it.value(); + for (auto pathIt = candidate.paths.begin(); pathIt != candidate.paths.end();) { + if (checkPath(*pathIt, candidate.matchLength, fileHandler, directoryHandler)) { + qCDebug(finderLog) << "FileInProjectFinder: found" << *pathIt << "in the cache"; + ++pathIt; + } else { + pathIt = candidate.paths.erase(pathIt); + } } + if (!candidate.paths.empty()) + return true; + m_cache.erase(it); } if (!m_projectDir.isEmpty()) { @@ -231,7 +255,7 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH if (prefixToIgnore == -1 && checkPath(originalPath, origLength, fileHandler, directoryHandler)) { - return handleSuccess(originalPath, originalPath, origLength, + return handleSuccess(originalPath, QStringList(originalPath), origLength, "in project directory"); } } @@ -254,8 +278,11 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH candidate.remove(0, prefixToIgnore); candidate.prepend(m_projectDir.toString()); const int matchLength = origLength - prefixToIgnore; - if (checkPath(candidate, matchLength, fileHandler, directoryHandler)) - return handleSuccess(originalPath, candidate, matchLength, "in project directory"); + // FIXME: This might be a worse match than what we find later. + if (checkPath(candidate, matchLength, fileHandler, directoryHandler)) { + return handleSuccess(originalPath, QStringList(candidate), matchLength, + "in project directory"); + } prefixToIgnore = originalPath.indexOf(separator, prefixToIgnore + 1); } } @@ -264,31 +291,38 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH qCDebug(finderLog) << "FileInProjectFinder: checking project files ..."; QStringList matches; - const QString lastSegment = FileName::fromString(originalPath).fileName(); + const QString lastSegment = FilePath::fromString(originalPath).fileName(); if (fileHandler) matches.append(filesWithSameFileName(lastSegment)); if (directoryHandler) matches.append(pathSegmentsWithSameName(lastSegment)); - const QString matchedFilePath = bestMatch(matches, originalPath); - const int matchLength = commonPostFixLength(matchedFilePath, originalPath); - if (!matchedFilePath.isEmpty() - && checkPath(matchedFilePath, matchLength, fileHandler, directoryHandler)) { - return handleSuccess(originalPath, matchedFilePath, matchLength, - "when matching project files"); + const QStringList matchedFilePaths = bestMatches(matches, originalPath); + if (!matchedFilePaths.empty()) { + const int matchLength = commonPostFixLength(matchedFilePaths.first(), originalPath); + QStringList hits; + for (const QString &matchedFilePath : matchedFilePaths) { + if (checkPath(matchedFilePath, matchLength, fileHandler, directoryHandler)) + hits << matchedFilePath; + } + if (!hits.empty()) + return handleSuccess(originalPath, hits, matchLength, "when matching project files"); } CacheEntry foundPath = findInSearchPaths(originalPath, fileHandler, directoryHandler); - if (!foundPath.path.isEmpty()) - return handleSuccess(originalPath, foundPath.path, foundPath.matchLength, "in search path"); + if (!foundPath.paths.isEmpty()) { + return handleSuccess(originalPath, foundPath.paths, foundPath.matchLength, + "in search path"); + } qCDebug(finderLog) << "FileInProjectFinder: checking absolute path in sysroot ..."; // check if absolute path is found in sysroot if (!m_sysroot.isEmpty()) { - FileName sysrootPath = m_sysroot; - sysrootPath.appendPath(originalPath); - if (checkPath(sysrootPath.toString(), origLength, fileHandler, directoryHandler)) - return handleSuccess(originalPath, sysrootPath.toString(), origLength, "in sysroot"); + const FilePath sysrootPath = m_sysroot.pathAppended(originalPath); + if (checkPath(sysrootPath.toString(), origLength, fileHandler, directoryHandler)) { + return handleSuccess(originalPath, QStringList(sysrootPath.toString()), origLength, + "in sysroot"); + } } qCDebug(finderLog) << "FileInProjectFinder: couldn't find file!"; @@ -299,10 +333,10 @@ bool FileInProjectFinder::findFileOrDirectory(const QString &originalPath, FileH FileInProjectFinder::CacheEntry FileInProjectFinder::findInSearchPaths( const QString &filePath, FileHandler fileHandler, DirectoryHandler directoryHandler) const { - for (const FileName &dirPath : m_searchDirectories) { + for (const FilePath &dirPath : m_searchDirectories) { const CacheEntry found = findInSearchPath(dirPath.toString(), filePath, fileHandler, directoryHandler); - if (!found.path.isEmpty()) + if (!found.paths.isEmpty()) return found; } @@ -327,17 +361,17 @@ FileInProjectFinder::CacheEntry FileInProjectFinder::findInSearchPath( QString s = filePath; while (!s.isEmpty()) { CacheEntry result; - result.path = searchPath + QLatin1Char('/') + s; + result.paths << searchPath + '/' + s; result.matchLength = s.length() + 1; - qCDebug(finderLog) << "FileInProjectFinder: trying" << result.path; + qCDebug(finderLog) << "FileInProjectFinder: trying" << result.paths.first(); - if (checkPath(result.path, result.matchLength, fileHandler, directoryHandler)) + if (checkPath(result.paths.first(), result.matchLength, fileHandler, directoryHandler)) return result; QString next = chopFirstDir(s); if (next.isEmpty()) { if (directoryHandler && QFileInfo(searchPath).fileName() == s) { - result.path = searchPath; + result.paths = QStringList{searchPath}; directoryHandler(QDir(searchPath).entryList(), result.matchLength); return result; } @@ -352,7 +386,7 @@ FileInProjectFinder::CacheEntry FileInProjectFinder::findInSearchPath( QStringList FileInProjectFinder::filesWithSameFileName(const QString &fileName) const { QStringList result; - foreach (const FileName &f, m_projectFiles) { + foreach (const FilePath &f, m_projectFiles) { if (f.fileName() == fileName) result << f.toString(); } @@ -362,8 +396,8 @@ QStringList FileInProjectFinder::filesWithSameFileName(const QString &fileName) QStringList FileInProjectFinder::pathSegmentsWithSameName(const QString &pathSegment) const { QStringList result; - for (const FileName &f : m_projectFiles) { - FileName currentPath = f.parentDir(); + for (const FilePath &f : m_projectFiles) { + FilePath currentPath = f.parentDir(); do { if (currentPath.fileName() == pathSegment) { if (result.isEmpty() || result.last() != currentPath.toString()) @@ -386,32 +420,38 @@ int FileInProjectFinder::commonPostFixLength(const QString &candidatePath, return rank; } -QString FileInProjectFinder::bestMatch(const QStringList &filePaths, const QString &filePathToFind) +QStringList FileInProjectFinder::bestMatches(const QStringList &filePaths, + const QString &filePathToFind) { if (filePaths.isEmpty()) - return QString(); + return {}; if (filePaths.length() == 1) { qCDebug(finderLog) << "FileInProjectFinder: found" << filePaths.first() << "in project files"; - return filePaths.first(); + return filePaths; } - auto it = std::max_element(filePaths.constBegin(), filePaths.constEnd(), - [&filePathToFind] (const QString &a, const QString &b) -> bool { - return commonPostFixLength(a, filePathToFind) < commonPostFixLength(b, filePathToFind); - }); - if (it != filePaths.cend()) { - qCDebug(finderLog) << "FileInProjectFinder: found best match" << *it << "in project files"; - return *it; + int bestRank = -1; + QStringList bestFilePaths; + for (const QString &fp : filePaths) { + const int currentRank = commonPostFixLength(fp, filePathToFind); + if (currentRank < bestRank) + continue; + if (currentRank > bestRank) { + bestRank = currentRank; + bestFilePaths.clear(); + } + bestFilePaths << fp; } - return QString(); + QTC_CHECK(!bestFilePaths.empty()); + return bestFilePaths; } -FileNameList FileInProjectFinder::searchDirectories() const +FilePathList FileInProjectFinder::searchDirectories() const { return m_searchDirectories; } -void FileInProjectFinder::setAdditionalSearchDirectories(const FileNameList &searchDirectories) +void FileInProjectFinder::setAdditionalSearchDirectories(const FilePathList &searchDirectories) { m_searchDirectories = searchDirectories; } @@ -421,4 +461,43 @@ FileInProjectFinder::PathMappingNode::~PathMappingNode() qDeleteAll(children); } +FilePathList FileInProjectFinder::QrcUrlFinder::find(const QUrl &fileUrl) const +{ + const auto fileIt = m_fileCache.constFind(fileUrl); + if (fileIt != m_fileCache.cend()) + return fileIt.value(); + QStringList hits; + for (const FilePath &f : m_allQrcFiles) { + QrcParser::Ptr &qrcParser = m_parserCache[f]; + if (!qrcParser) + qrcParser = QrcParser::parseQrcFile(f.toString(), QString()); + if (!qrcParser->isValid()) + continue; + qrcParser->collectFilesAtPath(QrcParser::normalizedQrcFilePath(fileUrl.toString()), &hits); + } + hits.removeDuplicates(); + const FilePathList result = transform(hits, &FilePath::fromString); + m_fileCache.insert(fileUrl, result); + return result; +} + +void FileInProjectFinder::QrcUrlFinder::setProjectFiles(const FilePathList &projectFiles) +{ + m_allQrcFiles = filtered(projectFiles, [](const FilePath &f) { return f.endsWith(".qrc"); }); + m_fileCache.clear(); + m_parserCache.clear(); +} + +FilePath chooseFileFromList(const FilePathList &candidates) +{ + if (candidates.length() == 1) + return candidates.first(); + QMenu filesMenu; + for (const FilePath &candidate : candidates) + filesMenu.addAction(candidate.toUserOutput()); + if (const QAction * const action = filesMenu.exec(QCursor::pos())) + return FilePath::fromUserInput(action->text()); + return FilePath(); +} + } // namespace Utils diff --git a/src/libs/utils/fileinprojectfinder.h b/src/libs/utils/fileinprojectfinder.h index 43a954ff93..66b5317235 100644 --- a/src/libs/utils/fileinprojectfinder.h +++ b/src/libs/utils/fileinprojectfinder.h @@ -29,11 +29,13 @@ #include <utils/fileutils.h> #include <QHash> +#include <QSharedPointer> #include <QStringList> QT_FORWARD_DECLARE_CLASS(QUrl) namespace Utils { +class QrcParser; class QTCREATOR_UTILS_EXPORT FileInProjectFinder { @@ -45,34 +47,44 @@ public: FileInProjectFinder(); ~FileInProjectFinder(); - void setProjectDirectory(const FileName &absoluteProjectPath); - FileName projectDirectory() const; + void setProjectDirectory(const FilePath &absoluteProjectPath); + FilePath projectDirectory() const; - void setProjectFiles(const FileNameList &projectFiles); - void setSysroot(const FileName &sysroot); + void setProjectFiles(const FilePathList &projectFiles); + void setSysroot(const FilePath &sysroot); - void addMappedPath(const FileName &localFilePath, const QString &remoteFilePath); + void addMappedPath(const FilePath &localFilePath, const QString &remoteFilePath); - QString findFile(const QUrl &fileUrl, bool *success = nullptr) const; + FilePathList findFile(const QUrl &fileUrl, bool *success = nullptr) const; bool findFileOrDirectory(const QString &originalPath, FileHandler fileHandler = nullptr, DirectoryHandler directoryHandler = nullptr) const; - FileNameList searchDirectories() const; - void setAdditionalSearchDirectories(const FileNameList &searchDirectories); + FilePathList searchDirectories() const; + void setAdditionalSearchDirectories(const FilePathList &searchDirectories); private: struct PathMappingNode { ~PathMappingNode(); - FileName localPath; + FilePath localPath; QHash<QString, PathMappingNode *> children; }; struct CacheEntry { - QString path; + QStringList paths; int matchLength = 0; }; + class QrcUrlFinder { + public: + FilePathList find(const QUrl &fileUrl) const; + void setProjectFiles(const FilePathList &projectFiles); + private: + FilePathList m_allQrcFiles; + mutable QHash<QUrl, FilePathList> m_fileCache; + mutable QHash<FilePath, QSharedPointer<QrcParser>> m_parserCache; + }; + CacheEntry findInSearchPaths(const QString &filePath, FileHandler fileHandler, DirectoryHandler directoryHandler) const; static CacheEntry findInSearchPath(const QString &searchPath, const QString &filePath, @@ -80,19 +92,22 @@ private: QStringList filesWithSameFileName(const QString &fileName) const; QStringList pathSegmentsWithSameName(const QString &path) const; - bool handleSuccess(const QString &originalPath, const QString &found, int confidence, + bool handleSuccess(const QString &originalPath, const QStringList &found, int confidence, const char *where) const; static int commonPostFixLength(const QString &candidatePath, const QString &filePathToFind); - static QString bestMatch(const QStringList &filePaths, const QString &filePathToFind); + static QStringList bestMatches(const QStringList &filePaths, const QString &filePathToFind); - FileName m_projectDir; - FileName m_sysroot; - FileNameList m_projectFiles; - FileNameList m_searchDirectories; + FilePath m_projectDir; + FilePath m_sysroot; + FilePathList m_projectFiles; + FilePathList m_searchDirectories; PathMappingNode m_pathMapRoot; mutable QHash<QString, CacheEntry> m_cache; + QrcUrlFinder m_qrcUrlFinder; }; +QTCREATOR_UTILS_EXPORT FilePath chooseFileFromList(const FilePathList &candidates); + } // namespace Utils diff --git a/src/libs/utils/filesearch.cpp b/src/libs/utils/filesearch.cpp index c34532a1de..0bdf3991c5 100644 --- a/src/libs/utils/filesearch.cpp +++ b/src/libs/utils/filesearch.cpp @@ -482,7 +482,7 @@ static bool matches(const QList<QRegExp> &exprList, const QString &filePath) { return Utils::anyOf(exprList, [&filePath](QRegExp reg) { return (reg.exactMatch(filePath) - || reg.exactMatch(FileName::fromString(filePath).fileName())); + || reg.exactMatch(FilePath::fromString(filePath).fileName())); }); } diff --git a/src/libs/utils/fileutils.cpp b/src/libs/utils/fileutils.cpp index a476cd8d89..acf9c6190d 100644 --- a/src/libs/utils/fileutils.cpp +++ b/src/libs/utils/fileutils.cpp @@ -28,20 +28,27 @@ #include "algorithm.h" #include "qtcassert.h" +#include "qtcprocess.h" #include <QDataStream> #include <QDir> #include <QDebug> #include <QDateTime> +#include <QOperatingSystemVersion> #include <QRegExp> #include <QTimer> #include <QUrl> +#include <qplatformdefs.h> #ifdef QT_GUI_LIB #include <QMessageBox> #endif #ifdef Q_OS_WIN +// We need defines for Windows 8 +#undef _WIN32_WINNT +#define _WIN32_WINNT _WIN32_WINNT_WIN8 + #include <qt_windows.h> #include <shlobj.h> #endif @@ -51,7 +58,7 @@ #endif QT_BEGIN_NAMESPACE -QDebug operator<<(QDebug dbg, const Utils::FileName &c) +QDebug operator<<(QDebug dbg, const Utils::FilePath &c) { return dbg << c.toString(); } @@ -60,6 +67,34 @@ QT_END_NAMESPACE namespace Utils { +/*! \class Utils::CommandLine + + \brief The CommandLine class represents a command line of a QProcess + or similar utility. + +*/ + +void CommandLine::addArg(const QString &arg, OsType osType) +{ + QtcProcess::addArg(&m_arguments, arg, osType); +} + +void CommandLine::addArgs(const QStringList &inArgs, OsType osType) +{ + for (const QString &arg : inArgs) + addArg(arg, osType); +} + +void CommandLine::addArgs(const QString &inArgs) +{ + QtcProcess::addArgs(&m_arguments, inArgs); +} + +QString CommandLine::toUserOutput() const +{ + return m_executable.toUserOutput() + ' ' + m_arguments; +} + /*! \class Utils::FileUtils \brief The FileUtils class contains file and directory related convenience @@ -74,7 +109,7 @@ namespace Utils { Returns whether the operation succeeded. */ -bool FileUtils::removeRecursively(const FileName &filePath, QString *error) +bool FileUtils::removeRecursively(const FilePath &filePath, QString *error) { QFileInfo fileInfo = filePath.toFileInfo(); if (!fileInfo.exists() && !fileInfo.isSymLink()) @@ -101,7 +136,7 @@ bool FileUtils::removeRecursively(const FileName &filePath, QString *error) QStringList fileNames = dir.entryList(QDir::Files | QDir::Hidden | QDir::System | QDir::Dirs | QDir::NoDotAndDotDot); foreach (const QString &fileName, fileNames) { - if (!removeRecursively(FileName(filePath).appendPath(fileName), error)) + if (!removeRecursively(filePath.pathAppended(fileName), error)) return false; } if (!QDir::root().rmdir(dir.path())) { @@ -140,7 +175,7 @@ bool FileUtils::removeRecursively(const FileName &filePath, QString *error) Returns whether the operation succeeded. */ -bool FileUtils::copyRecursively(const FileName &srcFilePath, const FileName &tgtFilePath, +bool FileUtils::copyRecursively(const FilePath &srcFilePath, const FilePath &tgtFilePath, QString *error, const std::function<bool (QFileInfo, QFileInfo, QString *)> ©Helper) { QFileInfo srcFileInfo = srcFilePath.toFileInfo(); @@ -160,10 +195,8 @@ bool FileUtils::copyRecursively(const FileName &srcFilePath, const FileName &tgt QStringList fileNames = sourceDir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot | QDir::Hidden | QDir::System); foreach (const QString &fileName, fileNames) { - FileName newSrcFilePath = srcFilePath; - newSrcFilePath.appendPath(fileName); - FileName newTgtFilePath = tgtFilePath; - newTgtFilePath.appendPath(fileName); + const FilePath newSrcFilePath = srcFilePath.pathAppended(fileName); + const FilePath newTgtFilePath = tgtFilePath.pathAppended(fileName); if (!copyRecursively(newSrcFilePath, newTgtFilePath, error, copyHelper)) return false; } @@ -185,23 +218,23 @@ bool FileUtils::copyRecursively(const FileName &srcFilePath, const FileName &tgt } /*! - If \a filePath is a directory, the function will recursively check all files and return - true if one of them is newer than \a timeStamp. If \a filePath is a single file, true will + If this is a directory, the function will recursively check all files and return + true if one of them is newer than \a timeStamp. If this is a single file, true will be returned if the file is newer than \a timeStamp. Returns whether at least one file in \a filePath has a newer date than \a timeStamp. */ -bool FileUtils::isFileNewerThan(const FileName &filePath, const QDateTime &timeStamp) +bool FilePath::isNewerThan(const QDateTime &timeStamp) const { - QFileInfo fileInfo = filePath.toFileInfo(); + const QFileInfo fileInfo = toFileInfo(); if (!fileInfo.exists() || fileInfo.lastModified() >= timeStamp) return true; if (fileInfo.isDir()) { - const QStringList dirContents = QDir(filePath.toString()) + const QStringList dirContents = QDir(toString()) .entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); - foreach (const QString &curFileName, dirContents) { - if (isFileNewerThan(FileName(filePath).appendPath(curFileName), timeStamp)) + for (const QString &curFileName : dirContents) { + if (pathAppended(curFileName).isNewerThan(timeStamp)) return true; } } @@ -218,30 +251,30 @@ bool FileUtils::isFileNewerThan(const FileName &filePath, const QDateTime &timeS Returns the symlink target file path. */ -FileName FileUtils::resolveSymlinks(const FileName &path) +FilePath FileUtils::resolveSymlinks(const FilePath &path) { QFileInfo f = path.toFileInfo(); int links = 16; while (links-- && f.isSymLink()) f.setFile(f.dir(), f.symLinkTarget()); if (links <= 0) - return FileName(); - return FileName::fromString(f.filePath()); + return FilePath(); + return FilePath::fromString(f.filePath()); } /*! - Recursively resolves possibly present symlinks in \a filePath. + Recursively resolves possibly present symlinks in this file name. Unlike QFileInfo::canonicalFilePath(), this function will not return an empty string if path doesn't exist. Returns the canonical path. */ -FileName FileUtils::canonicalPath(const FileName &path) +FilePath FilePath::canonicalPath() const { - const QString result = path.toFileInfo().canonicalFilePath(); + const QString result = toFileInfo().canonicalFilePath(); if (result.isEmpty()) - return path; - return FileName::fromString(result); + return *this; + return FilePath::fromString(result); } /*! @@ -250,16 +283,16 @@ FileName FileUtils::canonicalPath(const FileName &path) Returns the possibly shortened path with native separators. */ -QString FileUtils::shortNativePath(const FileName &path) +QString FilePath::shortNativePath() const { if (HostOsInfo::isAnyUnixHost()) { - const FileName home = FileName::fromString(QDir::cleanPath(QDir::homePath())); - if (path.isChildOf(home)) { + const FilePath home = FilePath::fromString(QDir::cleanPath(QDir::homePath())); + if (isChildOf(home)) { return QLatin1Char('~') + QDir::separator() - + QDir::toNativeSeparators(path.relativeChildPath(home).toString()); + + QDir::toNativeSeparators(relativeChildPath(home).toString()); } } - return path.toUserOutput(); + return toUserOutput(); } QString FileUtils::fileSystemFriendlyName(const QString &name) @@ -293,10 +326,10 @@ QString FileUtils::qmakeFriendlyName(const QString &name) return fileSystemFriendlyName(result); } -bool FileUtils::makeWritable(const FileName &path) +bool FileUtils::makeWritable(const FilePath &path) { - const QString fileName = path.toString(); - return QFile::setPermissions(fileName, QFile::permissions(fileName) | QFile::WriteUser); + const QString filePath = path.toString(); + return QFile::setPermissions(filePath, QFile::permissions(filePath) | QFile::WriteUser); } // makes sure that capitalization of directories is canonical on Windows and OS X. @@ -347,12 +380,80 @@ QString FileUtils::resolvePath(const QString &baseDir, const QString &fileName) return QDir::cleanPath(baseDir + QLatin1Char('/') + fileName); } -FileName FileUtils::commonPath(const FileName &oldCommonPath, const FileName &fileName) +FilePath FileUtils::commonPath(const FilePath &oldCommonPath, const FilePath &filePath) { - FileName newCommonPath = oldCommonPath; - while (!newCommonPath.isEmpty() && !fileName.isChildOf(newCommonPath)) + FilePath newCommonPath = oldCommonPath; + while (!newCommonPath.isEmpty() && !filePath.isChildOf(newCommonPath)) newCommonPath = newCommonPath.parentDir(); - return canonicalPath(newCommonPath); + return newCommonPath.canonicalPath(); +} + +// Copied from qfilesystemengine_win.cpp +#ifdef Q_OS_WIN + +// File ID for Windows up to version 7. +static inline QByteArray fileIdWin7(HANDLE handle) +{ + BY_HANDLE_FILE_INFORMATION info; + if (GetFileInformationByHandle(handle, &info)) { + char buffer[sizeof "01234567:0123456701234567\0"]; + qsnprintf(buffer, sizeof(buffer), "%lx:%08lx%08lx", + info.dwVolumeSerialNumber, + info.nFileIndexHigh, + info.nFileIndexLow); + return QByteArray(buffer); + } + return QByteArray(); +} + +// File ID for Windows starting from version 8. +static QByteArray fileIdWin8(HANDLE handle) +{ + QByteArray result; + FILE_ID_INFO infoEx; + if (GetFileInformationByHandleEx(handle, + static_cast<FILE_INFO_BY_HANDLE_CLASS>(18), // FileIdInfo in Windows 8 + &infoEx, sizeof(FILE_ID_INFO))) { + result = QByteArray::number(infoEx.VolumeSerialNumber, 16); + result += ':'; + // Note: MinGW-64's definition of FILE_ID_128 differs from the MSVC one. + result += QByteArray(reinterpret_cast<const char *>(&infoEx.FileId), int(sizeof(infoEx.FileId))).toHex(); + } + return result; +} + +static QByteArray fileIdWin(HANDLE fHandle) +{ + return QOperatingSystemVersion::current() >= QOperatingSystemVersion::Windows8 ? + fileIdWin8(HANDLE(fHandle)) : fileIdWin7(HANDLE(fHandle)); +} +#endif + +QByteArray FileUtils::fileId(const FilePath &fileName) +{ + QByteArray result; + +#ifdef Q_OS_WIN + const HANDLE handle = + CreateFile((wchar_t*)fileName.toUserOutput().utf16(), 0, + FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, NULL); + if (handle != INVALID_HANDLE_VALUE) { + result = fileIdWin(handle); + CloseHandle(handle); + } +#else // Copied from qfilesystemengine_unix.cpp + if (Q_UNLIKELY(fileName.isEmpty())) + return result; + + QT_STATBUF statResult; + if (QT_STAT(fileName.toString().toLocal8Bit().constData(), &statResult)) + return result; + result = QByteArray::number(quint64(statResult.st_dev), 16); + result += ':'; + result += QByteArray::number(quint64(statResult.st_ino)); +#endif + return result; } QByteArray FileReader::fetchQrc(const QString &fileName) @@ -553,114 +654,131 @@ TempFileSaver::~TempFileSaver() QFile::remove(m_fileName); } -/*! \class Utils::FileName +/*! \class Utils::FilePath - \brief The FileName class is a light-weight convenience class for filenames. + \brief The FilePath class is a light-weight convenience class for filenames. On windows filenames are compared case insensitively. */ -FileName::FileName() - : QString() +FilePath::FilePath() { - } -/// Constructs a FileName from \a info -FileName::FileName(const QFileInfo &info) - : QString(info.absoluteFilePath()) +/// Constructs a FilePath from \a info +FilePath FilePath::fromFileInfo(const QFileInfo &info) { + return FilePath::fromString(info.absoluteFilePath()); } /// \returns a QFileInfo -QFileInfo FileName::toFileInfo() const +QFileInfo FilePath::toFileInfo() const { - return QFileInfo(*this); + return QFileInfo(m_data); +} + +FilePath FilePath::fromUrl(const QUrl &url) +{ + FilePath fn; + fn.m_url = url; + fn.m_data = url.path(); + return fn; } /// \returns a QString for passing on to QString based APIs -const QString &FileName::toString() const +const QString &FilePath::toString() const +{ + return m_data; +} + +QUrl FilePath::toUrl() const { - return *this; + return m_url; } /// \returns a QString to display to the user /// Converts the separators to the native format -QString FileName::toUserOutput() const +QString FilePath::toUserOutput() const { - return QDir::toNativeSeparators(toString()); + if (m_url.isEmpty()) + return QDir::toNativeSeparators(toString()); + return m_url.toString(); } -QString FileName::fileName(int pathComponents) const +QString FilePath::fileName(int pathComponents) const { if (pathComponents < 0) - return *this; + return m_data; const QChar slash = QLatin1Char('/'); - int i = lastIndexOf(slash); + int i = m_data.lastIndexOf(slash); if (pathComponents == 0 || i == -1) - return mid(i + 1); + return m_data.mid(i + 1); int component = i + 1; // skip adjacent slashes - while (i > 0 && at(--i) == slash); + while (i > 0 && m_data.at(--i) == slash) + ; while (i >= 0 && --pathComponents >= 0) { - i = lastIndexOf(slash, i); + i = m_data.lastIndexOf(slash, i); component = i + 1; - while (i > 0 && at(--i) == slash); + while (i > 0 && m_data.at(--i) == slash) + ; } // If there are no more slashes before the found one, return the entire string - if (i > 0 && lastIndexOf(slash, i) != -1) - return mid(component); - return *this; + if (i > 0 && m_data.lastIndexOf(slash, i) != -1) + return m_data.mid(component); + return m_data; } /// \returns a bool indicating whether a file with this -/// FileName exists. -bool FileName::exists() const +/// FilePath exists. +bool FilePath::exists() const { - return !isEmpty() && QFileInfo::exists(*this); + return !isEmpty() && QFileInfo::exists(m_data); } /// Find the parent directory of a given directory. -/// Returns an empty FileName if the current directory is already +/// Returns an empty FilePath if the current directory is already /// a root level directory. -/// \returns \a FileName with the last segment removed. -FileName FileName::parentDir() const +/// \returns \a FilePath with the last segment removed. +FilePath FilePath::parentDir() const { const QString basePath = toString(); if (basePath.isEmpty()) - return FileName(); + return FilePath(); const QDir base(basePath); if (base.isRoot()) - return FileName(); + return FilePath(); const QString path = basePath + QLatin1String("/.."); const QString parent = QDir::cleanPath(path); - QTC_ASSERT(parent != path, return FileName()); + QTC_ASSERT(parent != path, return FilePath()); - return FileName::fromString(parent); + return FilePath::fromString(parent); } -/// Constructs a FileName from \a filename +/// Constructs a FilePath from \a filename /// \a filename is not checked for validity. -FileName FileName::fromString(const QString &filename) +FilePath FilePath::fromString(const QString &filename) { - return FileName(filename); + FilePath fn; + fn.m_data = filename; + return fn; } -/// Constructs a FileName from \a fileName. The \a defaultExtension is appended +/// Constructs a FilePath from \a filePath. The \a defaultExtension is appended /// to \a filename if that does not have an extension already. -/// \a fileName is not checked for validity. -FileName FileName::fromString(const QString &filename, const QString &defaultExtension) +/// \a filePath is not checked for validity. +FilePath FilePath::fromStringWithExtension(const QString &filepath, const QString &defaultExtension) { - if (filename.isEmpty() || defaultExtension.isEmpty()) - return filename; + if (filepath.isEmpty() || defaultExtension.isEmpty()) + return FilePath::fromString(filepath); - QString rc = filename; - QFileInfo fi(filename); + QString rc = filepath; + QFileInfo fi(filepath); // Add extension unless user specified something else const QChar dot = QLatin1Char('.'); if (!fi.fileName().contains(dot)) { @@ -668,138 +786,148 @@ FileName FileName::fromString(const QString &filename, const QString &defaultExt rc += dot; rc += defaultExtension; } - return rc; -} - -/// Constructs a FileName from \a fileName -/// \a fileName is not checked for validity. -FileName FileName::fromLatin1(const QByteArray &filename) -{ - return FileName(QString::fromLatin1(filename)); + return FilePath::fromString(rc); } -/// Constructs a FileName from \a fileName -/// \a fileName is only passed through QDir::cleanPath -FileName FileName::fromUserInput(const QString &filename) +/// Constructs a FilePath from \a filePath +/// \a filePath is only passed through QDir::cleanPath +FilePath FilePath::fromUserInput(const QString &filePath) { - QString clean = QDir::cleanPath(filename); + QString clean = QDir::cleanPath(filePath); if (clean.startsWith(QLatin1String("~/"))) clean = QDir::homePath() + clean.mid(1); - return FileName(clean); + return FilePath::fromString(clean); } -/// Constructs a FileName from \a fileName, which is encoded as UTF-8. -/// \a fileName is not checked for validity. -FileName FileName::fromUtf8(const char *filename, int filenameSize) +/// Constructs a FilePath from \a filePath, which is encoded as UTF-8. +/// \a filePath is not checked for validity. +FilePath FilePath::fromUtf8(const char *filename, int filenameSize) { - return FileName(QString::fromUtf8(filename, filenameSize)); + return FilePath::fromString(QString::fromUtf8(filename, filenameSize)); } -FileName::FileName(const QString &string) - : QString(string) +FilePath FilePath::fromVariant(const QVariant &variant) { + if (variant.type() == QVariant::Url) + return FilePath::fromUrl(variant.toUrl()); + return FilePath::fromString(variant.toString()); +} +QVariant FilePath::toVariant() const +{ + if (!m_url.isEmpty()) + return m_url; + return m_data; } -bool FileName::operator==(const FileName &other) const +bool FilePath::operator==(const FilePath &other) const { - return QString::compare(*this, other, HostOsInfo::fileNameCaseSensitivity()) == 0; + if (!m_url.isEmpty()) + return m_url == other.m_url; + return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) == 0; } -bool FileName::operator!=(const FileName &other) const +bool FilePath::operator!=(const FilePath &other) const { return !(*this == other); } -bool FileName::operator<(const FileName &other) const +bool FilePath::operator<(const FilePath &other) const { - return QString::compare(*this, other, HostOsInfo::fileNameCaseSensitivity()) < 0; + if (!m_url.isEmpty()) + return m_url < other.m_url; + return QString::compare(m_data, other.m_data, HostOsInfo::fileNameCaseSensitivity()) < 0; } -bool FileName::operator<=(const FileName &other) const +bool FilePath::operator<=(const FilePath &other) const { - return QString::compare(*this, other, HostOsInfo::fileNameCaseSensitivity()) <= 0; + return !(other < *this); } -bool FileName::operator>(const FileName &other) const +bool FilePath::operator>(const FilePath &other) const { return other < *this; } -bool FileName::operator>=(const FileName &other) const +bool FilePath::operator>=(const FilePath &other) const { - return other <= *this; + return !(*this < other); } -FileName FileName::operator+(const QString &s) const +FilePath FilePath::operator+(const QString &s) const { - FileName result(*this); - result.appendString(s); - return result; + return FilePath::fromString(m_data + s); } -/// \returns whether FileName is a child of \a s -bool FileName::isChildOf(const FileName &s) const +/// \returns whether FilePath is a child of \a s +bool FilePath::isChildOf(const FilePath &s) const { if (s.isEmpty()) return false; - if (!QString::startsWith(s, HostOsInfo::fileNameCaseSensitivity())) + if (!m_data.startsWith(s.m_data, HostOsInfo::fileNameCaseSensitivity())) return false; - if (size() <= s.size()) + if (m_data.size() <= s.m_data.size()) return false; // s is root, '/' was already tested in startsWith - if (s.QString::endsWith(QLatin1Char('/'))) + if (s.m_data.endsWith(QLatin1Char('/'))) return true; // s is a directory, next character should be '/' (/tmpdir is NOT a child of /tmp) - return at(s.size()) == QLatin1Char('/'); + return m_data.at(s.m_data.size()) == QLatin1Char('/'); } /// \overload -bool FileName::isChildOf(const QDir &dir) const +bool FilePath::isChildOf(const QDir &dir) const { - return isChildOf(FileName::fromString(dir.absolutePath())); + return isChildOf(FilePath::fromString(dir.absolutePath())); } -/// \returns whether FileName endsWith \a s -bool FileName::endsWith(const QString &s) const +/// \returns whether FilePath endsWith \a s +bool FilePath::endsWith(const QString &s) const { - return QString::endsWith(s, HostOsInfo::fileNameCaseSensitivity()); + return m_data.endsWith(s, HostOsInfo::fileNameCaseSensitivity()); } -/// \returns the relativeChildPath of FileName to parent if FileName is a child of parent -/// \note returns a empty FileName if FileName is not a child of parent +bool FilePath::isLocal() const +{ + return m_url.isEmpty() || m_url.isLocalFile(); +} + +/// \returns the relativeChildPath of FilePath to parent if FilePath is a child of parent +/// \note returns a empty FilePath if FilePath is not a child of parent /// That is, this never returns a path starting with "../" -FileName FileName::relativeChildPath(const FileName &parent) const +FilePath FilePath::relativeChildPath(const FilePath &parent) const { if (!isChildOf(parent)) - return FileName(); - return FileName(QString::mid(parent.size() + 1, -1)); + return FilePath(); + return FilePath::fromString(m_data.mid(parent.m_data.size() + 1, -1)); } -/// Appends \a s, ensuring a / between the parts -FileName &FileName::appendPath(const QString &s) +FilePath FilePath::pathAppended(const QString &str) const { - if (s.isEmpty()) - return *this; - if (!isEmpty() && !QString::endsWith(QLatin1Char('/'))) - appendString(QLatin1Char('/')); - appendString(s); - return *this; + FilePath fn = *this; + if (str.isEmpty()) + return fn; + if (!isEmpty() && !m_data.endsWith(QLatin1Char('/'))) + fn.m_data.append('/'); + fn.m_data.append(str); + return fn; } -FileName &FileName::appendString(const QString &str) +FilePath FilePath::stringAppended(const QString &str) const { - QString::append(str); - return *this; + FilePath fn = *this; + fn.m_data.append(str); + return fn; } -FileName &FileName::appendString(QChar str) +uint FilePath::hash(uint seed) const { - QString::append(str); - return *this; + if (Utils::HostOsInfo::fileNameCaseSensitivity() == Qt::CaseInsensitive) + return qHash(m_data.toUpper(), seed); + return qHash(m_data, seed); } -QTextStream &operator<<(QTextStream &s, const FileName &fn) +QTextStream &operator<<(QTextStream &s, const FilePath &fn) { return s << fn.toString(); } @@ -813,14 +941,4 @@ void withNtfsPermissions(const std::function<void()> &task) qt_ntfs_permission_lookup--; } #endif - } // namespace Utils - -QT_BEGIN_NAMESPACE -uint qHash(const Utils::FileName &a) -{ - if (Utils::HostOsInfo::fileNameCaseSensitivity() == Qt::CaseInsensitive) - return qHash(a.toString().toUpper()); - return qHash(a.toString()); -} -QT_END_NAMESPACE diff --git a/src/libs/utils/fileutils.h b/src/libs/utils/fileutils.h index aa6310c760..ec018b9c3b 100644 --- a/src/libs/utils/fileutils.h +++ b/src/libs/utils/fileutils.h @@ -33,11 +33,12 @@ #include <QXmlStreamWriter> // Mac. #include <QMetaType> #include <QStringList> +#include <QUrl> #include <functional> #include <memory> -namespace Utils {class FileName; } +namespace Utils { class FilePath; } QT_BEGIN_NAMESPACE class QDataStream; @@ -49,7 +50,7 @@ class QTemporaryFile; class QTextStream; class QWidget; -QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug dbg, const Utils::FileName &c); +QTCREATOR_UTILS_EXPORT QDebug operator<<(QDebug dbg, const Utils::FilePath &c); // for withNtfsPermissions #ifdef Q_OS_WIN @@ -60,76 +61,114 @@ QT_END_NAMESPACE namespace Utils { -class QTCREATOR_UTILS_EXPORT FileName : private QString +class QTCREATOR_UTILS_EXPORT FilePath { public: - FileName(); - explicit FileName(const QFileInfo &info); - QFileInfo toFileInfo() const; - static FileName fromString(const QString &filename); - static FileName fromString(const QString &filename, const QString &defaultExtension); - static FileName fromLatin1(const QByteArray &filename); - static FileName fromUserInput(const QString &filename); - static FileName fromUtf8(const char *filename, int filenameSize = -1); + FilePath(); + + static FilePath fromString(const QString &filepath); + static FilePath fromFileInfo(const QFileInfo &info); + static FilePath fromStringWithExtension(const QString &filepath, const QString &defaultExtension); + static FilePath fromUserInput(const QString &filepath); + static FilePath fromUtf8(const char *filepath, int filepathSize = -1); + static FilePath fromVariant(const QVariant &variant); + const QString &toString() const; + QFileInfo toFileInfo() const; + QVariant toVariant() const; + QString toUserOutput() const; + QString shortNativePath() const; + QString fileName(int pathComponents = 0) const; bool exists() const; - FileName parentDir() const; + FilePath parentDir() const; - bool operator==(const FileName &other) const; - bool operator!=(const FileName &other) const; - bool operator<(const FileName &other) const; - bool operator<=(const FileName &other) const; - bool operator>(const FileName &other) const; - bool operator>=(const FileName &other) const; - FileName operator+(const QString &s) const; + bool operator==(const FilePath &other) const; + bool operator!=(const FilePath &other) const; + bool operator<(const FilePath &other) const; + bool operator<=(const FilePath &other) const; + bool operator>(const FilePath &other) const; + bool operator>=(const FilePath &other) const; + FilePath operator+(const QString &s) const; - bool isChildOf(const FileName &s) const; + bool isChildOf(const FilePath &s) const; bool isChildOf(const QDir &dir) const; bool endsWith(const QString &s) const; + bool isLocal() const; + + bool isNewerThan(const QDateTime &timeStamp) const; + + FilePath relativeChildPath(const FilePath &parent) const; + FilePath pathAppended(const QString &str) const; + FilePath stringAppended(const QString &str) const; + + FilePath canonicalPath() const; + + void clear() { m_data.clear(); } + bool isEmpty() const { return m_data.isEmpty(); } + + uint hash(uint seed) const; + + // NOTE: FileName operations on FilePath created from URL currenly + // do not work except for .toVariant() and .toUrl(). + static FilePath fromUrl(const QUrl &url); + QUrl toUrl() const; - FileName relativeChildPath(const FileName &parent) const; - FileName &appendPath(const QString &s); - FileName &appendString(const QString &str); - FileName &appendString(QChar str); - - using QString::chop; - using QString::clear; - using QString::count; - using QString::isEmpty; - using QString::isNull; - using QString::length; - using QString::size; private: - FileName(const QString &string); + QString m_data; + QUrl m_url; }; -QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FileName &fn); +QTCREATOR_UTILS_EXPORT QTextStream &operator<<(QTextStream &s, const FilePath &fn); + +using FilePathList = QList<FilePath>; + +using FileName = FilePath; +using FileNameList = FilePathList; -using FileNameList = QList<FileName>; +class QTCREATOR_UTILS_EXPORT CommandLine +{ +public: + CommandLine() {} + + CommandLine(const FilePath &executable, const QString &arguments) + : m_executable(executable), m_arguments(arguments) + {} + + void addArg(const QString &arg, OsType osType = HostOsInfo::hostOs()); + void addArgs(const QStringList &inArgs, OsType osType = HostOsInfo::hostOs()); + void addArgs(const QString &inArgs); + + QString toUserOutput() const; + + FilePath executable() const { return m_executable; } + QString arguments() const { return m_arguments; } + +private: + FilePath m_executable; + QString m_arguments; +}; class QTCREATOR_UTILS_EXPORT FileUtils { public: - static bool removeRecursively(const FileName &filePath, QString *error = nullptr); + static bool removeRecursively(const FilePath &filePath, QString *error = nullptr); static bool copyRecursively( - const FileName &srcFilePath, const FileName &tgtFilePath, QString *error = nullptr, + const FilePath &srcFilePath, const FilePath &tgtFilePath, QString *error = nullptr, const std::function<bool (QFileInfo, QFileInfo, QString *)> ©Helper = nullptr); - static bool isFileNewerThan(const FileName &filePath, const QDateTime &timeStamp); - static FileName resolveSymlinks(const FileName &path); - static FileName canonicalPath(const FileName &path); - static QString shortNativePath(const FileName &path); + static FilePath resolveSymlinks(const FilePath &path); static QString fileSystemFriendlyName(const QString &name); static int indexOfQmakeUnfriendly(const QString &name, int startpos = 0); static QString qmakeFriendlyName(const QString &name); - static bool makeWritable(const FileName &path); + static bool makeWritable(const FilePath &path); static QString normalizePathName(const QString &name); static bool isRelativePath(const QString &fileName); static bool isAbsolutePath(const QString &fileName) { return !isRelativePath(fileName); } static QString resolvePath(const QString &baseDir, const QString &fileName); - static FileName commonPath(const FileName &oldCommonPath, const FileName &fileName); + static FilePath commonPath(const FilePath &oldCommonPath, const FilePath &fileName); + static QByteArray fileId(const FilePath &fileName); }; // for actually finding out if e.g. directories are writable on Windows @@ -240,16 +279,14 @@ private: bool m_autoRemove = true; }; -} // namespace Utils +inline uint qHash(const Utils::FilePath &a, uint seed = 0) { return a.hash(seed); } -QT_BEGIN_NAMESPACE -QTCREATOR_UTILS_EXPORT uint qHash(const Utils::FileName &a); -QT_END_NAMESPACE +} // namespace Utils namespace std { -template<> struct hash<Utils::FileName> +template<> struct hash<Utils::FilePath> { - using argument_type = Utils::FileName; + using argument_type = Utils::FilePath; using result_type = size_t; result_type operator()(const argument_type &fn) const { @@ -260,4 +297,4 @@ template<> struct hash<Utils::FileName> }; } // namespace std -Q_DECLARE_METATYPE(Utils::FileName) +Q_DECLARE_METATYPE(Utils::FilePath) diff --git a/src/libs/utils/genericconstants.h b/src/libs/utils/genericconstants.h new file mode 100644 index 0000000000..a380fc15ba --- /dev/null +++ b/src/libs/utils/genericconstants.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +namespace Utils { + +namespace Constants { + +const char BEAUTIFIER_SETTINGS_GROUP[] = "Beautifier"; +const char BEAUTIFIER_GENERAL_GROUP[] = "General"; +const char BEAUTIFIER_AUTO_FORMAT_ON_SAVE[] = "autoFormatOnSave"; + +} // namespace Constants +} // namespace Utils diff --git a/src/libs/utils/highlightingitemdelegate.cpp b/src/libs/utils/highlightingitemdelegate.cpp index e253689843..4874200e86 100644 --- a/src/libs/utils/highlightingitemdelegate.cpp +++ b/src/libs/utils/highlightingitemdelegate.cpp @@ -114,7 +114,8 @@ int HighlightingItemDelegate::drawLineNumber(QPainter *painter, const QStyleOpti const bool isSelected = option.state & QStyle::State_Selected; const QString lineText = QString::number(lineNumber); const int minimumLineNumberDigits = qMax(kMinimumLineNumberDigits, lineText.count()); - const int fontWidth = painter->fontMetrics().width(QString(minimumLineNumberDigits, '0')); + const int fontWidth = + painter->fontMetrics().horizontalAdvance(QString(minimumLineNumberDigits, '0')); const int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth + lineNumberAreaHorizontalPadding; QRect lineNumberAreaRect(rect); diff --git a/src/libs/utils/images/dir.png b/src/libs/utils/images/dir.png Binary files differnew file mode 100644 index 0000000000..57cec6bcd3 --- /dev/null +++ b/src/libs/utils/images/dir.png diff --git a/src/libs/utils/images/pinned.png b/src/libs/utils/images/pinned.png Binary files differnew file mode 100644 index 0000000000..dc96231ca4 --- /dev/null +++ b/src/libs/utils/images/pinned.png diff --git a/src/libs/utils/images/pinned@2x.png b/src/libs/utils/images/pinned@2x.png Binary files differnew file mode 100644 index 0000000000..d7c59e83ce --- /dev/null +++ b/src/libs/utils/images/pinned@2x.png diff --git a/src/libs/utils/images/settings.png b/src/libs/utils/images/settings.png Binary files differnew file mode 100644 index 0000000000..2621923499 --- /dev/null +++ b/src/libs/utils/images/settings.png diff --git a/src/libs/utils/images/settings@2x.png b/src/libs/utils/images/settings@2x.png Binary files differnew file mode 100644 index 0000000000..456f0cc56b --- /dev/null +++ b/src/libs/utils/images/settings@2x.png diff --git a/src/libs/utils/images/sort_alphabetically.png b/src/libs/utils/images/sort_alphabetically.png Binary files differnew file mode 100644 index 0000000000..c15eb56d50 --- /dev/null +++ b/src/libs/utils/images/sort_alphabetically.png diff --git a/src/libs/utils/images/sort_alphabetically@2x.png b/src/libs/utils/images/sort_alphabetically@2x.png Binary files differnew file mode 100644 index 0000000000..1a2e5d9520 --- /dev/null +++ b/src/libs/utils/images/sort_alphabetically@2x.png diff --git a/src/libs/utils/images/toggleprogressdetails.png b/src/libs/utils/images/toggleprogressdetails.png Binary files differnew file mode 100644 index 0000000000..b2353cd9d0 --- /dev/null +++ b/src/libs/utils/images/toggleprogressdetails.png diff --git a/src/libs/utils/images/toggleprogressdetails@2x.png b/src/libs/utils/images/toggleprogressdetails@2x.png Binary files differnew file mode 100644 index 0000000000..28a212491e --- /dev/null +++ b/src/libs/utils/images/toggleprogressdetails@2x.png diff --git a/src/libs/utils/images/unknownfile.png b/src/libs/utils/images/unknownfile.png Binary files differnew file mode 100644 index 0000000000..88f77592d1 --- /dev/null +++ b/src/libs/utils/images/unknownfile.png diff --git a/src/libs/utils/listmodel.h b/src/libs/utils/listmodel.h new file mode 100644 index 0000000000..5f4de811e6 --- /dev/null +++ b/src/libs/utils/listmodel.h @@ -0,0 +1,183 @@ +/**************************************************************************** +** +** Copyright (C) 2019 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include "utils_global.h" +#include "treemodel.h" + +namespace Utils { + +template <class ChildType> +class BaseListModel : public TreeModel<TypedTreeItem<ChildType>, ChildType> +{ +public: + using BaseModel = TreeModel<TypedTreeItem<ChildType>, ChildType>; + using BaseModel::rootItem; + + explicit BaseListModel(QObject *parent = nullptr) : BaseModel(parent) {} + + int itemCount() const { return rootItem()->childCount(); } + ChildType *itemAt(int row) const { return rootItem()->childAt(row); } + + void appendItem(ChildType *item) { rootItem()->appendChild(item); } + + template <class Predicate> + void forItems(const Predicate &pred) const + { + rootItem()->forFirstLevelChildren(pred); + } + + template <class Predicate> + ChildType *findItem(const Predicate &pred) const + { + return rootItem()->findFirstLevelChild(pred); + } + + void sortItems(const std::function<bool(const ChildType *, const ChildType *)> &lessThan) + { + return rootItem()->sortChildren([lessThan](const TreeItem *a, const TreeItem *b) { + return lessThan(static_cast<const ChildType *>(a), static_cast<const ChildType *>(b)); + }); + } + + int indexOf(const ChildType *item) const { return rootItem()->indexOf(item); } + + void clear() { rootItem()->removeChildren(); } + + using const_iterator = typename QVector<TreeItem *>::const_iterator; + const_iterator begin() const { return rootItem()->begin(); } + const_iterator end() const { return rootItem()->end(); } + +}; + +template <class ItemData> +class ListItem : public TreeItem +{ +public: + ItemData itemData; +}; + +template <class ItemData> +class ListModel : public BaseListModel<ListItem<ItemData>> +{ +public: + using ChildType = ListItem<ItemData>; + using BaseModel = BaseListModel<ChildType>; + + explicit ListModel(QObject *parent = nullptr) : BaseModel(parent) {} + + const ItemData &dataAt(int row) const + { + static const ItemData dummyData = {}; + auto item = BaseModel::itemAt(row); + return item ? item->itemData : dummyData; + } + + ChildType *findItemByData(const std::function<bool(const ItemData &)> &pred) const + { + return BaseModel::rootItem()->findFirstLevelChild([pred](ChildType *child) { + return pred(child->itemData); + }); + } + + void destroyItems(const std::function<bool(const ItemData &)> &pred) + { + QList<ChildType *> toDestroy; + BaseModel::rootItem()->forFirstLevelChildren([pred, &toDestroy](ChildType *item) { + if (pred(item->itemData)) + toDestroy.append(item); + }); + for (ChildType *item : toDestroy) + this->destroyItem(item); + } + + ItemData *findData(const std::function<bool(const ItemData &)> &pred) const + { + ChildType *item = findItemByData(pred); + return item ? &item->itemData : nullptr; + } + + void forItems(const std::function<void(ItemData &)> &func) const + { + BaseModel::rootItem()->forFirstLevelChildren([func](ChildType *child) { + func(child->itemData); + }); + } + + ChildType *appendItem(const ItemData &data) + { + auto item = new ChildType; + item->itemData = data; + BaseModel::rootItem()->appendChild(item); + return item; + } + + QVariant data(const QModelIndex &idx, int role) const override + { + TreeItem *item = BaseModel::itemForIndex(idx); + if (item && item->parent() == BaseModel::rootItem()) + return itemData(static_cast<ChildType *>(item)->itemData, idx.column(), role); + return {}; + } + + Qt::ItemFlags flags(const QModelIndex &idx) const override + { + TreeItem *item = BaseModel::itemForIndex(idx); + if (item && item->parent() == BaseModel::rootItem()) + return itemFlags(static_cast<ChildType *>(item)->itemData, idx.column()); + return {}; + } + + virtual QVariant itemData(const ItemData &idata, int column, int role) const + { + if (m_dataAccessor) + return m_dataAccessor(idata, column, role); + return {}; + } + + virtual Qt::ItemFlags itemFlags(const ItemData &idata, int column) const + { + if (m_flagsAccessor) + return m_flagsAccessor(idata, column); + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + } + + void setDataAccessor(const std::function<QVariant(const ItemData &, int, int)> &accessor) + { + m_dataAccessor = accessor; + } + + void setFlagsAccessor(const std::function<Qt::ItemFlags(const ItemData &, int)> &accessor) + { + m_flagsAccessor = accessor; + } + +private: + std::function<QVariant(const ItemData &, int, int)> m_dataAccessor; + std::function<Qt::ItemFlags(const ItemData &, int)> m_flagsAccessor; +}; + +} // namespace Utils diff --git a/src/libs/utils/macroexpander.cpp b/src/libs/utils/macroexpander.cpp index eb3f05e175..a41fbde31d 100644 --- a/src/libs/utils/macroexpander.cpp +++ b/src/libs/utils/macroexpander.cpp @@ -290,11 +290,39 @@ QString MacroExpander::expand(const QString &stringWithVariables) const return res; } +FilePath MacroExpander::expand(const FilePath &fileNameWithVariables) const +{ + return FilePath::fromString(expand(fileNameWithVariables.toString())); +} + QByteArray MacroExpander::expand(const QByteArray &stringWithVariables) const { return expand(QString::fromLatin1(stringWithVariables)).toLatin1(); } +QVariant MacroExpander::expandVariant(const QVariant &v) const +{ + const auto type = QMetaType::Type(v.type()); + if (type == QMetaType::QString) { + return expand(v.toString()); + } else if (type == QMetaType::QStringList) { + return Utils::transform(v.toStringList(), + [this](const QString &s) -> QVariant { return expand(s); }); + } else if (type == QMetaType::QVariantList) { + return Utils::transform(v.toList(), [this](const QVariant &v) { return expandVariant(v); }); + } else if (type == QMetaType::QVariantMap) { + const auto map = v.toMap(); + QVariantMap result; + QMapIterator<QString, QVariant> it(map); + while (it.hasNext()) { + it.next(); + result.insert(it.key(), expandVariant(it.value())); + } + return result; + } + return v; +} + QString MacroExpander::expandProcessArgs(const QString &argsWithVariables) const { return QtcProcess::expandMacros(argsWithVariables, d); @@ -394,7 +422,7 @@ void MacroExpander::registerFileVariables(const QByteArray &prefix, registerVariable(prefix + kFileNamePostfix, tr("%1: File name without path.").arg(heading), - [base]() -> QString { QString tmp = base(); return tmp.isEmpty() ? QString() : FileName::fromString(tmp).fileName(); }, + [base]() -> QString { QString tmp = base(); return tmp.isEmpty() ? QString() : FilePath::fromString(tmp).fileName(); }, visibleInChooser); registerVariable(prefix + kFileBaseNamePostfix, diff --git a/src/libs/utils/macroexpander.h b/src/libs/utils/macroexpander.h index f17ae86b8c..70448069c5 100644 --- a/src/libs/utils/macroexpander.h +++ b/src/libs/utils/macroexpander.h @@ -37,6 +37,7 @@ namespace Utils { namespace Internal { class MacroExpanderPrivate; } +class FilePath; class MacroExpander; using MacroExpanderProvider = std::function<MacroExpander *()>; using MacroExpanderProviders = QVector<MacroExpanderProvider>; @@ -55,7 +56,9 @@ public: QString value(const QByteArray &variable, bool *found = nullptr) const; QString expand(const QString &stringWithVariables) const; + FilePath expand(const FilePath &fileNameWithVariables) const; QByteArray expand(const QByteArray &stringWithVariables) const; + QVariant expandVariant(const QVariant &v) const; QString expandProcessArgs(const QString &argsWithVariables) const; diff --git a/src/libs/utils/mimetypes/mimeprovider.cpp b/src/libs/utils/mimetypes/mimeprovider.cpp index d444aa899c..46d3eb71bd 100644 --- a/src/libs/utils/mimetypes/mimeprovider.cpp +++ b/src/libs/utils/mimetypes/mimeprovider.cpp @@ -803,11 +803,7 @@ void MimeXMLProvider::ensureLoaded() // if (!fdoXmlFound) { // // We could instead install the file as part of installing Qt? -#if (QT_VERSION >= QT_VERSION_CHECK(5, 11, 0)) const char freedesktopOrgXml[] = ":/qt-project.org/qmime/packages/freedesktop.org.xml"; -#else - const char freedesktopOrgXml[] = ":/qt-project.org/qmime/freedesktop.org.xml"; -#endif allFiles.prepend(QLatin1String(freedesktopOrgXml)); // } diff --git a/src/libs/utils/newclasswidget.cpp b/src/libs/utils/newclasswidget.cpp index 8700b8204c..9205d435dc 100644 --- a/src/libs/utils/newclasswidget.cpp +++ b/src/libs/utils/newclasswidget.cpp @@ -88,7 +88,7 @@ NewClassWidget::NewClassWidget(QWidget *parent) : connect(d->m_ui.classLineEdit, &QLineEdit::textEdited, this, &NewClassWidget::classNameEdited); connect(d->m_ui.baseClassComboBox, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + QOverload<int>::of(&QComboBox::currentIndexChanged), this, &NewClassWidget::suggestClassNameFromBase); connect(d->m_ui.baseClassComboBox, &QComboBox::editTextChanged, this, &NewClassWidget::slotValidChanged); diff --git a/src/libs/utils/outputformatter.cpp b/src/libs/utils/outputformatter.cpp index ec85a748a6..7926422da6 100644 --- a/src/libs/utils/outputformatter.cpp +++ b/src/libs/utils/outputformatter.cpp @@ -78,7 +78,8 @@ void OutputFormatter::appendMessage(const QString &text, OutputFormat format) void OutputFormatter::appendMessage(const QString &text, const QTextCharFormat &format) { - foreach (const FormattedText &output, parseAnsi(text, format)) + const QList<FormattedText> formattedTextList = parseAnsi(text, format); + for (const FormattedText &output : formattedTextList) append(output.text, output.format); } diff --git a/src/libs/utils/outputformatter.h b/src/libs/utils/outputformatter.h index c516ca14e2..1db42453d5 100644 --- a/src/libs/utils/outputformatter.h +++ b/src/libs/utils/outputformatter.h @@ -62,6 +62,9 @@ public: virtual void clear() {} void setBoldFontEnabled(bool enabled); +signals: + void contentChanged(); + protected: void initFormats(); virtual void clearLastLine(); diff --git a/src/libs/utils/pathchooser.cpp b/src/libs/utils/pathchooser.cpp index e29b0c7663..f08391d6a6 100644 --- a/src/libs/utils/pathchooser.cpp +++ b/src/libs/utils/pathchooser.cpp @@ -193,14 +193,14 @@ QString PathChooserPrivate::expandedPath(const QString &input) const if (m_macroExpander) expandedInput = m_macroExpander->expand(expandedInput); - const QString path = FileName::fromUserInput(expandedInput).toString(); + const QString path = FilePath::fromUserInput(expandedInput).toString(); if (path.isEmpty()) return path; switch (m_acceptingKind) { case PathChooser::Command: case PathChooser::ExistingCommand: { - const FileName expanded = m_environment.searchInPath(path, {FileName::fromString(m_baseDirectory)}); + const FilePath expanded = m_environment.searchInPath(path, {FilePath::fromString(m_baseDirectory)}); return expanded.isEmpty() ? path : expanded.toString(); } case PathChooser::Any: @@ -293,12 +293,12 @@ void PathChooser::setBaseDirectory(const QString &directory) triggerChanged(); } -FileName PathChooser::baseFileName() const +FilePath PathChooser::baseFileName() const { - return FileName::fromString(d->m_baseDirectory); + return FilePath::fromString(d->m_baseDirectory); } -void PathChooser::setBaseFileName(const FileName &base) +void PathChooser::setBaseFileName(const FilePath &base) { setBaseDirectory(base.toString()); } @@ -323,14 +323,14 @@ QString PathChooser::path() const return fileName().toString(); } -FileName PathChooser::rawFileName() const +FilePath PathChooser::rawFileName() const { - return FileName::fromString(QDir::fromNativeSeparators(d->m_lineEdit->text())); + return FilePath::fromString(QDir::fromNativeSeparators(d->m_lineEdit->text())); } -FileName PathChooser::fileName() const +FilePath PathChooser::fileName() const { - return FileName::fromUserInput(d->expandedPath(rawFileName().toString())); + return FilePath::fromUserInput(d->expandedPath(rawFileName().toString())); } // FIXME: try to remove again @@ -352,7 +352,7 @@ void PathChooser::setPath(const QString &path) d->m_lineEdit->setTextKeepingActiveCursor(QDir::toNativeSeparators(path)); } -void PathChooser::setFileName(const FileName &fn) +void PathChooser::setFileName(const FilePath &fn) { d->m_lineEdit->setTextKeepingActiveCursor(fn.toUserOutput()); } diff --git a/src/libs/utils/pathchooser.h b/src/libs/utils/pathchooser.h index 73939e1bf4..09d98e71c8 100644 --- a/src/libs/utils/pathchooser.h +++ b/src/libs/utils/pathchooser.h @@ -55,8 +55,8 @@ class QTCREATOR_UTILS_EXPORT PathChooser : public QWidget Q_PROPERTY(QStringList commandVersionArguments READ commandVersionArguments WRITE setCommandVersionArguments) Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly DESIGNABLE true) // Designer does not know this type, so force designable to false: - Q_PROPERTY(Utils::FileName fileName READ fileName WRITE setFileName DESIGNABLE false) - Q_PROPERTY(Utils::FileName baseFileName READ baseFileName WRITE setBaseFileName DESIGNABLE false) + Q_PROPERTY(Utils::FilePath fileName READ fileName WRITE setFileName DESIGNABLE false) + Q_PROPERTY(Utils::FilePath baseFileName READ baseFileName WRITE setBaseFileName DESIGNABLE false) Q_PROPERTY(QColor errorColor READ errorColor WRITE setErrorColor DESIGNABLE true) Q_PROPERTY(QColor okColor READ okColor WRITE setOkColor DESIGNABLE true) @@ -93,8 +93,8 @@ public: QString path() const; QString rawPath() const; // The raw unexpanded input. - FileName rawFileName() const; // The raw unexpanded input. - FileName fileName() const; + FilePath rawFileName() const; // The raw unexpanded input. + FilePath fileName() const; static QString expandedDirectory(const QString &input, const Environment &env, const QString &baseDir); @@ -102,8 +102,8 @@ public: QString baseDirectory() const; void setBaseDirectory(const QString &directory); - FileName baseFileName() const; - void setBaseFileName(const FileName &base); + FilePath baseFileName() const; + void setBaseFileName(const FilePath &base); void setEnvironment(const Environment &env); @@ -172,7 +172,7 @@ signals: public slots: void setPath(const QString &); - void setFileName(const FileName &); + void setFileName(const FilePath &); void setErrorColor(const QColor &errorColor); void setOkColor(const QColor &okColor); diff --git a/src/libs/utils/persistentsettings.cpp b/src/libs/utils/persistentsettings.cpp index db0f8d0db5..92d32109ea 100644 --- a/src/libs/utils/persistentsettings.cpp +++ b/src/libs/utils/persistentsettings.cpp @@ -341,7 +341,7 @@ QVariantMap PersistentSettingsReader::restoreValues() const return m_valueMap; } -bool PersistentSettingsReader::load(const FileName &fileName) +bool PersistentSettingsReader::load(const FilePath &fileName) { m_valueMap.clear(); @@ -409,7 +409,7 @@ static void writeVariantValue(QXmlStreamWriter &w, const Context &ctx, } } -PersistentSettingsWriter::PersistentSettingsWriter(const FileName &fileName, const QString &docType) : +PersistentSettingsWriter::PersistentSettingsWriter(const FilePath &fileName, const QString &docType) : m_fileName(fileName), m_docType(docType) { } @@ -438,7 +438,7 @@ bool PersistentSettingsWriter::save(const QVariantMap &data, QWidget *parent) co } #endif // QT_GUI_LIB -FileName PersistentSettingsWriter::fileName() const +FilePath PersistentSettingsWriter::fileName() const { return m_fileName; } //** * @brief Set contents of file (e.g. from data read from it). */ diff --git a/src/libs/utils/persistentsettings.h b/src/libs/utils/persistentsettings.h index a7255dcb8a..92dbf0d8c9 100644 --- a/src/libs/utils/persistentsettings.h +++ b/src/libs/utils/persistentsettings.h @@ -42,7 +42,7 @@ public: PersistentSettingsReader(); QVariant restoreValue(const QString &variable, const QVariant &defaultValue = QVariant()) const; QVariantMap restoreValues() const; - bool load(const FileName &fileName); + bool load(const FilePath &fileName); private: QMap<QString, QVariant> m_valueMap; @@ -51,7 +51,7 @@ private: class QTCREATOR_UTILS_EXPORT PersistentSettingsWriter { public: - PersistentSettingsWriter(const FileName &fileName, const QString &docType); + PersistentSettingsWriter(const FilePath &fileName, const QString &docType); ~PersistentSettingsWriter(); bool save(const QVariantMap &data, QString *errorString) const; @@ -59,14 +59,14 @@ public: bool save(const QVariantMap &data, QWidget *parent) const; #endif - FileName fileName() const; + FilePath fileName() const; void setContents(const QVariantMap &data); private: bool write(const QVariantMap &data, QString *errorString) const; - const FileName m_fileName; + const FilePath m_fileName; const QString m_docType; mutable QMap<QString, QVariant> m_savedData; }; diff --git a/src/libs/utils/projectintropage.cpp b/src/libs/utils/projectintropage.cpp index a449fe1285..56127ac479 100644 --- a/src/libs/utils/projectintropage.cpp +++ b/src/libs/utils/projectintropage.cpp @@ -101,7 +101,7 @@ ProjectIntroPage::ProjectIntroPage(QWidget *parent) : connect(d->m_ui.nameLineEdit, &FancyLineEdit::validReturnPressed, this, &ProjectIntroPage::slotActivated); connect(d->m_ui.projectComboBox, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + QOverload<int>::of(&QComboBox::currentIndexChanged), this, &ProjectIntroPage::slotChanged); setProperty(SHORT_TITLE_PROPERTY, tr("Location")); diff --git a/src/libs/utils/qrcparser.cpp b/src/libs/utils/qrcparser.cpp new file mode 100644 index 0000000000..21904d3716 --- /dev/null +++ b/src/libs/utils/qrcparser.cpp @@ -0,0 +1,556 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "qrcparser.h" + +#include <utils/qtcassert.h> + +#include <QCoreApplication> +#include <QDir> +#include <QDomDocument> +#include <QFile> +#include <QFileInfo> +#include <QLocale> +#include <QLoggingCategory> +#include <QMultiHash> +#include <QMutex> +#include <QMutexLocker> +#include <QSet> +#include <QStringList> + +Q_LOGGING_CATEGORY(qrcParserLog, "qtc.qrcParser", QtWarningMsg) + +namespace Utils { + +namespace Internal { +/*! + * \class QrcParser + * \brief Parses one or more qrc files, and keeps their content cached + * + * A Qrc resource contains files read from the filesystem but organized in a possibly different way. + * + * To easily describe that with a simple structure we use a map from qrc paths to the paths in the + * filesystem. + * By using a map we can easily find all qrc paths that start with a given prefix, and thus loop + * on a qrc directory. + * + * Qrc files also support languages, those are mapped to a prefix of the qrc path. + * For example the french /image/bla.png (lang=fr) will have the path "fr/image/bla.png". + * The empty language represent the default resource. + * Languages are looked up using the locale uiLanguages() property + * + * For a single qrc a given path maps to a single file, but when one has multiple + * (platform specific exclusive) qrc files, then multiple files match, so QStringList are used. + * + * Especially the collect* functions are thought as low level interface. + */ +class QrcParserPrivate +{ + Q_DECLARE_TR_FUNCTIONS(QmlJS::QrcParser) +public: + typedef QMap<QString,QStringList> SMap; + QrcParserPrivate(QrcParser *q); + bool parseFile(const QString &path, const QString &contents); + QString firstFileAtPath(const QString &path, const QLocale &locale) const; + void collectFilesAtPath(const QString &path, QStringList *res, const QLocale *locale = 0) const; + bool hasDirAtPath(const QString &path, const QLocale *locale = 0) const; + void collectFilesInPath(const QString &path, QMap<QString,QStringList> *res, bool addDirs = false, + const QLocale *locale = 0) const; + void collectResourceFilesForSourceFile(const QString &sourceFile, QStringList *res, + const QLocale *locale = 0) const; + + QStringList errorMessages() const; + QStringList languages() const; +private: + static QString fixPrefix(const QString &prefix); + const QStringList allUiLanguages(const QLocale *locale) const; + + SMap m_resources; + SMap m_files; + QStringList m_languages; + QStringList m_errorMessages; +}; + +class QrcCachePrivate +{ + Q_DECLARE_TR_FUNCTIONS(QmlJS::QrcCachePrivate) +public: + QrcCachePrivate(QrcCache *q); + QrcParser::Ptr addPath(const QString &path, const QString &contents); + void removePath(const QString &path); + QrcParser::Ptr updatePath(const QString &path, const QString &contents); + QrcParser::Ptr parsedPath(const QString &path); + void clear(); +private: + QHash<QString, QPair<QrcParser::Ptr,int> > m_cache; + QMutex m_mutex; +}; +} // namespace Internal + +/*! \brief normalizes the path to a file in a qrc resource by dropping the "qrc:/" or ":" and + * any extra slashes at the beginning + */ +QString QrcParser::normalizedQrcFilePath(const QString &path) { + QString normPath = path; + int endPrefix = 0; + if (path.startsWith(QLatin1String("qrc:/"))) + endPrefix = 4; + else if (path.startsWith(QLatin1String(":/"))) + endPrefix = 1; + if (endPrefix < path.size() && path.at(endPrefix) == QLatin1Char('/')) + while (endPrefix + 1 < path.size() && path.at(endPrefix+1) == QLatin1Char('/')) + ++endPrefix; + normPath = path.right(path.size()-endPrefix); + if (!normPath.startsWith(QLatin1Char('/'))) + normPath.insert(0, QLatin1Char('/')); + return normPath; +} + +/*! \brief normalizes the path to a directory in a qrc resource by dropping the "qrc:/" or ":" and + * any extra slashes at the beginning, and ensuring it ends with a slash + */ +QString QrcParser::normalizedQrcDirectoryPath(const QString &path) { + QString normPath = normalizedQrcFilePath(path); + if (!normPath.endsWith(QLatin1Char('/'))) + normPath.append(QLatin1Char('/')); + return normPath; +} + +QString QrcParser::qrcDirectoryPathForQrcFilePath(const QString &file) +{ + return file.left(file.lastIndexOf(QLatin1Char('/'))); +} + +QrcParser::QrcParser() +{ + d = new Internal::QrcParserPrivate(this); +} + +QrcParser::~QrcParser() +{ + delete d; +} + +bool QrcParser::parseFile(const QString &path, const QString &contents) +{ + return d->parseFile(path, contents); +} + +/*! \brief returns fs path of the first (active) file at the given qrc path + */ +QString QrcParser::firstFileAtPath(const QString &path, const QLocale &locale) const +{ + return d->firstFileAtPath(path, locale); +} + +/*! \brief adds al the fs paths for the given qrc path to *res + * If locale is null all possible files are added, otherwise just the first match + * using that locale. + */ +void QrcParser::collectFilesAtPath(const QString &path, QStringList *res, const QLocale *locale) const +{ + d->collectFilesAtPath(path, res, locale); +} + +/*! \brief returns true if the given path is a non empty directory + */ +bool QrcParser::hasDirAtPath(const QString &path, const QLocale *locale) const +{ + return d->hasDirAtPath(path, locale); +} + +/*! \brief adds the directory contents of the given qrc path to res + * + * adds the qrcFileName => fs paths associations contained in the given qrc path + * to res. If addDirs is true directories are also added. + * If locale is null all possible files are added, otherwise just the first match + * using that locale. + */ +void QrcParser::collectFilesInPath(const QString &path, QMap<QString,QStringList> *res, bool addDirs, + const QLocale *locale) const +{ + d->collectFilesInPath(path, res, addDirs, locale); +} + +void QrcParser::collectResourceFilesForSourceFile(const QString &sourceFile, QStringList *res, + const QLocale *locale) const +{ + d->collectResourceFilesForSourceFile(sourceFile, res, locale); +} + +/*! \brief returns the errors found while parsing + */ +QStringList QrcParser::errorMessages() const +{ + return d->errorMessages(); +} + +/*! \brief returns all languages used in this qrc resource + */ +QStringList QrcParser::languages() const +{ + return d->languages(); +} + +/*! \brief if the contents are valid + */ +bool QrcParser::isValid() const +{ + return errorMessages().isEmpty(); +} + +QrcParser::Ptr QrcParser::parseQrcFile(const QString &path, const QString &contents) +{ + Ptr res(new QrcParser); + if (!path.isEmpty()) + res->parseFile(path, contents); + return res; +} + +// ---------------- + +QrcCache::QrcCache() +{ + d = new Internal::QrcCachePrivate(this); +} + +QrcCache::~QrcCache() +{ + delete d; +} + +QrcParser::ConstPtr QrcCache::addPath(const QString &path, const QString &contents) +{ + return d->addPath(path, contents); +} + +void QrcCache::removePath(const QString &path) +{ + d->removePath(path); +} + +QrcParser::ConstPtr QrcCache::updatePath(const QString &path, const QString &contents) +{ + return d->updatePath(path, contents); +} + +QrcParser::ConstPtr QrcCache::parsedPath(const QString &path) +{ + return d->parsedPath(path); +} + +void QrcCache::clear() +{ + d->clear(); +} + +// -------------------- + +namespace Internal { + +QrcParserPrivate::QrcParserPrivate(QrcParser *) +{ } + +bool QrcParserPrivate::parseFile(const QString &path, const QString &contents) +{ + QDomDocument doc; + QDir baseDir(QFileInfo(path).path()); + + if (contents.isEmpty()) { + // Regular file + QFile file(path); + if (!file.open(QIODevice::ReadOnly)) { + m_errorMessages.append(file.errorString()); + return false; + } + + QString error_msg; + int error_line, error_col; + if (!doc.setContent(&file, &error_msg, &error_line, &error_col)) { + m_errorMessages.append(tr("XML error on line %1, col %2: %3") + .arg(error_line).arg(error_col).arg(error_msg)); + return false; + } + } else { + // Virtual file from qmake evaluator + QString error_msg; + int error_line, error_col; + if (!doc.setContent(contents, &error_msg, &error_line, &error_col)) { + m_errorMessages.append(tr("XML error on line %1, col %2: %3") + .arg(error_line).arg(error_col).arg(error_msg)); + return false; + } + } + + QDomElement root = doc.firstChildElement(QLatin1String("RCC")); + if (root.isNull()) { + m_errorMessages.append(tr("The <RCC> root element is missing.")); + return false; + } + + QDomElement relt = root.firstChildElement(QLatin1String("qresource")); + for (; !relt.isNull(); relt = relt.nextSiblingElement(QLatin1String("qresource"))) { + + QString prefix = fixPrefix(relt.attribute(QLatin1String("prefix"))); + const QString language = relt.attribute(QLatin1String("lang")); + if (!m_languages.contains(language)) + m_languages.append(language); + + QDomElement felt = relt.firstChildElement(QLatin1String("file")); + for (; !felt.isNull(); felt = felt.nextSiblingElement(QLatin1String("file"))) { + const QString fileName = felt.text(); + const QString alias = felt.attribute(QLatin1String("alias")); + QString filePath = baseDir.absoluteFilePath(fileName); + QString accessPath; + if (!alias.isEmpty()) + accessPath = language + prefix + alias; + else + accessPath = language + prefix + fileName; + QStringList &resources = m_resources[accessPath]; + if (!resources.contains(filePath)) + resources.append(filePath); + QStringList &files = m_files[filePath]; + if (!files.contains(accessPath)) + files.append(accessPath); + } + } + return true; +} + +// path is assumed to be a normalized absolute path +QString QrcParserPrivate::firstFileAtPath(const QString &path, const QLocale &locale) const +{ + QTC_CHECK(path.startsWith(QLatin1Char('/'))); + for (const QString &language : allUiLanguages(&locale)) { + if (m_languages.contains(language)) { + SMap::const_iterator res = m_resources.find(language + path); + if (res != m_resources.end()) + return res.value().at(0); + } + } + return QString(); +} + +void QrcParserPrivate::collectFilesAtPath(const QString &path, QStringList *files, + const QLocale *locale) const +{ + QTC_CHECK(path.startsWith(QLatin1Char('/'))); + for (const QString &language : allUiLanguages(locale)) { + if (m_languages.contains(language)) { + SMap::const_iterator res = m_resources.find(language + path); + if (res != m_resources.end()) + (*files) << res.value(); + } + } +} + +// path is expected to be normalized and start and end with a slash +bool QrcParserPrivate::hasDirAtPath(const QString &path, const QLocale *locale) const +{ + QTC_CHECK(path.startsWith(QLatin1Char('/'))); + QTC_CHECK(path.endsWith(QLatin1Char('/'))); + for (const QString &language : allUiLanguages(locale)) { + if (m_languages.contains(language)) { + QString key = language + path; + SMap::const_iterator res = m_resources.lowerBound(key); + if (res != m_resources.end() && res.key().startsWith(key)) + return true; + } + } + return false; +} + +void QrcParserPrivate::collectFilesInPath(const QString &path, QMap<QString,QStringList> *contents, + bool addDirs, const QLocale *locale) const +{ + QTC_CHECK(path.startsWith(QLatin1Char('/'))); + QTC_CHECK(path.endsWith(QLatin1Char('/'))); + SMap::const_iterator end = m_resources.end(); + for (const QString &language : allUiLanguages(locale)) { + QString key = language + path; + SMap::const_iterator res = m_resources.lowerBound(key); + while (res != end && res.key().startsWith(key)) { + const QString &actualKey = res.key(); + int endDir = actualKey.indexOf(QLatin1Char('/'), key.size()); + if (endDir == -1) { + QString fileName = res.key().right(res.key().size()-key.size()); + QStringList &els = (*contents)[fileName]; + for (const QString &val : res.value()) + if (!els.contains(val)) + els << val; + ++res; + } else { + QString dirName = res.key().mid(key.size(), endDir - key.size() + 1); + if (addDirs) + contents->insert(dirName, QStringList()); + QString key2 = key + dirName; + do { + ++res; + } while (res != end && res.key().startsWith(key2)); + } + } + } +} + +void QrcParserPrivate::collectResourceFilesForSourceFile(const QString &sourceFile, + QStringList *results, + const QLocale *locale) const +{ + // TODO: use FileName from fileutils for file paths + + const QStringList langs = allUiLanguages(locale); + SMap::const_iterator file = m_files.find(sourceFile); + if (file == m_files.end()) + return; + for (const QString &resource : file.value()) { + for (const QString &language : langs) { + if (resource.startsWith(language) && !results->contains(resource)) + results->append(resource); + } + } +} + +QStringList QrcParserPrivate::errorMessages() const +{ + return m_errorMessages; +} + +QStringList QrcParserPrivate::languages() const +{ + return m_languages; +} + +QString QrcParserPrivate::fixPrefix(const QString &prefix) +{ + const QChar slash = QLatin1Char('/'); + QString result = QString(slash); + for (int i = 0; i < prefix.size(); ++i) { + const QChar c = prefix.at(i); + if (c == slash && result.at(result.size() - 1) == slash) + continue; + result.append(c); + } + + if (!result.endsWith(slash)) + result.append(slash); + + return result; +} + +const QStringList QrcParserPrivate::allUiLanguages(const QLocale *locale) const +{ + if (!locale) + return languages(); + QStringList langs = locale->uiLanguages(); + foreach (const QString &language, langs) { // qt4 support + if (language.contains(QLatin1Char('_')) || language.contains(QLatin1Char('-'))) { + QStringList splits = QString(language).replace(QLatin1Char('_'), QLatin1Char('-')) + .split(QLatin1Char('-')); + if (splits.size() > 1 && !langs.contains(splits.at(0))) + langs.append(splits.at(0)); + } + } + if (!langs.contains(QString())) + langs.append(QString()); + return langs; +} + +// ---------------- + +QrcCachePrivate::QrcCachePrivate(QrcCache *) +{ } + +QrcParser::Ptr QrcCachePrivate::addPath(const QString &path, const QString &contents) +{ + QPair<QrcParser::Ptr,int> currentValue; + { + QMutexLocker l(&m_mutex); + currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); + currentValue.second += 1; + if (currentValue.second > 1) { + m_cache.insert(path, currentValue); + return currentValue.first; + } + } + QrcParser::Ptr newParser = QrcParser::parseQrcFile(path, contents); + if (!newParser->isValid()) + qCWarning(qrcParserLog) << "adding invalid qrc " << path << " to the cache:" << newParser->errorMessages(); + { + QMutexLocker l(&m_mutex); + QPair<QrcParser::Ptr,int> currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); + if (currentValue.first.isNull()) + currentValue.first = newParser; + currentValue.second += 1; + m_cache.insert(path, currentValue); + return currentValue.first; + } +} + +void QrcCachePrivate::removePath(const QString &path) +{ + QPair<QrcParser::Ptr,int> currentValue; + { + QMutexLocker l(&m_mutex); + currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); + if (currentValue.second == 1) { + m_cache.remove(path); + } else if (currentValue.second > 1) { + currentValue.second -= 1; + m_cache.insert(path, currentValue); + } else { + QTC_CHECK(!m_cache.contains(path)); + } + } +} + +QrcParser::Ptr QrcCachePrivate::updatePath(const QString &path, const QString &contents) +{ + QrcParser::Ptr newParser = QrcParser::parseQrcFile(path, contents); + { + QMutexLocker l(&m_mutex); + QPair<QrcParser::Ptr,int> currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); + currentValue.first = newParser; + if (currentValue.second == 0) + currentValue.second = 1; // add qrc files that are not in the resources of a project + m_cache.insert(path, currentValue); + return currentValue.first; + } +} + +QrcParser::Ptr QrcCachePrivate::parsedPath(const QString &path) +{ + QMutexLocker l(&m_mutex); + QPair<QrcParser::Ptr,int> currentValue = m_cache.value(path, {QrcParser::Ptr(0), 0}); + return currentValue.first; +} + +void QrcCachePrivate::clear() +{ + QMutexLocker l(&m_mutex); + m_cache.clear(); +} + +} // namespace Internal +} // namespace QmlJS diff --git a/src/libs/utils/qrcparser.h b/src/libs/utils/qrcparser.h new file mode 100644 index 0000000000..b5a7be761b --- /dev/null +++ b/src/libs/utils/qrcparser.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once +#include "utils_global.h" + +#include <QMap> +#include <QSharedPointer> +#include <QString> +#include <QStringList> + +QT_FORWARD_DECLARE_CLASS(QLocale) + +namespace Utils { + +namespace Internal { +class QrcParserPrivate; +class QrcCachePrivate; +} + +class QTCREATOR_UTILS_EXPORT QrcParser +{ +public: + typedef QSharedPointer<QrcParser> Ptr; + typedef QSharedPointer<const QrcParser> ConstPtr; + ~QrcParser(); + bool parseFile(const QString &path, const QString &contents); + QString firstFileAtPath(const QString &path, const QLocale &locale) const; + void collectFilesAtPath(const QString &path, QStringList *res, const QLocale *locale = 0) const; + bool hasDirAtPath(const QString &path, const QLocale *locale = 0) const; + void collectFilesInPath(const QString &path, QMap<QString,QStringList> *res, bool addDirs = false, + const QLocale *locale = 0) const; + void collectResourceFilesForSourceFile(const QString &sourceFile, QStringList *results, + const QLocale *locale = 0) const; + + QStringList errorMessages() const; + QStringList languages() const; + bool isValid() const; + + static Ptr parseQrcFile(const QString &path, const QString &contents); + static QString normalizedQrcFilePath(const QString &path); + static QString normalizedQrcDirectoryPath(const QString &path); + static QString qrcDirectoryPathForQrcFilePath(const QString &file); +private: + QrcParser(); + QrcParser(const QrcParser &); + Internal::QrcParserPrivate *d; +}; + +class QTCREATOR_UTILS_EXPORT QrcCache +{ +public: + QrcCache(); + ~QrcCache(); + QrcParser::ConstPtr addPath(const QString &path, const QString &contents); + void removePath(const QString &path); + QrcParser::ConstPtr updatePath(const QString &path, const QString &contents); + QrcParser::ConstPtr parsedPath(const QString &path); + void clear(); +private: + Internal::QrcCachePrivate *d; +}; +} diff --git a/src/libs/utils/qtcprocess.cpp b/src/libs/utils/qtcprocess.cpp index dd4fe16f49..992e1817c0 100644 --- a/src/libs/utils/qtcprocess.cpp +++ b/src/libs/utils/qtcprocess.cpp @@ -595,12 +595,12 @@ static QString quoteArgWin(const QString &arg) } QtcProcess::Arguments QtcProcess::prepareArgs(const QString &cmd, SplitError *err, OsType osType, - const Environment *env, const QString *pwd) + const Environment *env, const QString *pwd, bool abortOnMeta) { if (osType == OsTypeWindows) return prepareArgsWin(cmd, err, env, pwd); else - return Arguments::createUnixArgs(splitArgs(cmd, osType, true, err, env, pwd)); + return Arguments::createUnixArgs(splitArgs(cmd, osType, abortOnMeta, err, env, pwd)); } @@ -694,7 +694,8 @@ void QtcProcess::start() const OsType osType = HostOsInfo::hostOs(); if (m_haveEnv) { if (m_environment.size() == 0) - qWarning("QtcProcess::start: Empty environment set when running '%s'.", qPrintable(m_command)); + qWarning("QtcProcess::start: Empty environment set when running '%s'.", + qPrintable(m_commandLine.executable().toString())); env = m_environment; QProcess::setEnvironment(env.toStringList()); @@ -705,7 +706,9 @@ void QtcProcess::start() const QString &workDir = workingDirectory(); QString command; QtcProcess::Arguments arguments; - bool success = prepareCommand(m_command, m_arguments, &command, &arguments, osType, &env, &workDir); + bool success = prepareCommand(m_commandLine.executable().toString(), + m_commandLine.arguments(), + &command, &arguments, osType, &env, &workDir); if (osType == OsTypeWindows) { QString args; if (m_useCtrlCStub) { diff --git a/src/libs/utils/qtcprocess.h b/src/libs/utils/qtcprocess.h index 44c5786661..5eeacf666e 100644 --- a/src/libs/utils/qtcprocess.h +++ b/src/libs/utils/qtcprocess.h @@ -38,10 +38,9 @@ class QTCREATOR_UTILS_EXPORT QtcProcess : public QProcess public: QtcProcess(QObject *parent = nullptr); - void setEnvironment(const Environment &env) - { m_environment = env; m_haveEnv = true; } - void setCommand(const QString &command, const QString &arguments) - { m_command = command; m_arguments = arguments; } + + void setEnvironment(const Environment &env) { m_environment = env; m_haveEnv = true; } + void setCommand(const CommandLine &cmdLine) { m_commandLine = cmdLine; } void setUseCtrlCStub(bool enabled); void start(); void terminate(); @@ -80,7 +79,8 @@ public: //! Prepare argument of a shell command for feeding into QProcess static Arguments prepareArgs(const QString &cmd, SplitError *err, OsType osType = HostOsInfo::hostOs(), - const Environment *env = nullptr, const QString *pwd = nullptr); + const Environment *env = nullptr, const QString *pwd = nullptr, + bool abortOnMeta = true); //! Prepare a shell command for feeding into QProcess static bool prepareCommand(const QString &command, const QString &arguments, QString *outCmd, Arguments *outArgs, OsType osType = HostOsInfo::hostOs(), @@ -141,8 +141,7 @@ public: }; private: - QString m_command; - QString m_arguments; + CommandLine m_commandLine; Environment m_environment; bool m_haveEnv = false; bool m_useCtrlCStub = false; diff --git a/src/libs/utils/reloadpromptutils.cpp b/src/libs/utils/reloadpromptutils.cpp index 2270053be0..09a9855784 100644 --- a/src/libs/utils/reloadpromptutils.cpp +++ b/src/libs/utils/reloadpromptutils.cpp @@ -33,7 +33,7 @@ namespace Utils { -QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName, +QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FilePath &fileName, bool modified, bool enableDiffOption, QWidget *parent) @@ -50,7 +50,10 @@ QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName, msg = QCoreApplication::translate("Utils::reloadPrompt", "The file <i>%1</i> has been changed on disk. Do you want to reload it?"); } - msg = msg.arg(fileName.fileName()); + msg = "<p>" + msg.arg(fileName.fileName()) + "</p><p>" + + QCoreApplication::translate("Utils::reloadPrompt", + "The default behavior can be set in Tools > Options > Environment > System.") + + "</p>"; return reloadPrompt(title, msg, fileName.toUserOutput(), enableDiffOption, parent); } diff --git a/src/libs/utils/reloadpromptutils.h b/src/libs/utils/reloadpromptutils.h index 8991d52b59..82aa409cf5 100644 --- a/src/libs/utils/reloadpromptutils.h +++ b/src/libs/utils/reloadpromptutils.h @@ -33,7 +33,7 @@ class QWidget; QT_END_NAMESPACE namespace Utils { -class FileName; +class FilePath; enum ReloadPromptAnswer { ReloadCurrent, @@ -44,7 +44,7 @@ enum ReloadPromptAnswer { CloseCurrent }; -QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FileName &fileName, +QTCREATOR_UTILS_EXPORT ReloadPromptAnswer reloadPrompt(const FilePath &fileName, bool modified, bool enableDiffOption, QWidget *parent); diff --git a/src/libs/utils/runextensions.h b/src/libs/utils/runextensions.h index 6925504693..eacbd8dc4c 100644 --- a/src/libs/utils/runextensions.h +++ b/src/libs/utils/runextensions.h @@ -600,4 +600,59 @@ const QFuture<T> &onResultReady(const QFuture<T> &future, Function f) return future; } -} // Utils +/*! + Adds a handler for when the future is finished. + This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions + or create a QFutureWatcher already for other reasons. +*/ +template<typename R, typename T> +const QFuture<T> &onFinished(const QFuture<T> &future, + R *receiver, + void (R::*member)(const QFuture<T> &)) +{ + auto watcher = new QFutureWatcher<T>(); + QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); + QObject::connect(watcher, + &QFutureWatcherBase::finished, + receiver, + [receiver, member, watcher]() { (receiver->*member)(watcher->future()); }); + watcher->setFuture(future); + return future; +} + +/*! + Adds a handler for when the future is finished. The guard object determines the lifetime of + the connection. + This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions + or create a QFutureWatcher already for other reasons. +*/ +template<typename T, typename Function> +const QFuture<T> &onFinished(const QFuture<T> &future, QObject *guard, Function f) +{ + auto watcher = new QFutureWatcher<T>(); + QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); + QObject::connect(watcher, &QFutureWatcherBase::finished, guard, [f, watcher]() { + f(watcher->future()); + }); + watcher->setFuture(future); + return future; +} + +/*! + Adds a handler for when the future is finished. + This creates a new QFutureWatcher. Do not use if you intend to react on multiple conditions + or create a QFutureWatcher already for other reasons. +*/ +template<typename T, typename Function> +const QFuture<T> &onFinished(const QFuture<T> &future, Function f) +{ + auto watcher = new QFutureWatcher<T>(); + QObject::connect(watcher, &QFutureWatcherBase::finished, watcher, &QObject::deleteLater); + QObject::connect(watcher, &QFutureWatcherBase::finished, [f, watcher]() { + f(watcher->future()); + }); + watcher->setFuture(future); + return future; +} + +} // namespace Utils diff --git a/src/libs/utils/savedaction.cpp b/src/libs/utils/savedaction.cpp index 0000e54d31..87a785f6db 100644 --- a/src/libs/utils/savedaction.cpp +++ b/src/libs/utils/savedaction.cpp @@ -235,7 +235,7 @@ void SavedAction::connectWidget(QWidget *widget, ApplyMode applyMode) } else if (auto spinBox = qobject_cast<QSpinBox *>(widget)) { spinBox->setValue(m_value.toInt()); if (applyMode == ImmediateApply) { - connect(spinBox, static_cast<void(QSpinBox::*)(int)>(&QSpinBox::valueChanged), + connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, [this, spinBox]() { setValue(spinBox->value()); }); } } else if (auto lineEdit = qobject_cast<QLineEdit *>(widget)) { diff --git a/src/libs/utils/savefile.cpp b/src/libs/utils/savefile.cpp index cac25546db..b25432a280 100644 --- a/src/libs/utils/savefile.cpp +++ b/src/libs/utils/savefile.cpp @@ -119,7 +119,7 @@ bool SaveFile::commit() } QString finalFileName - = FileUtils::resolveSymlinks(FileName::fromString(m_finalFileName)).toString(); + = FileUtils::resolveSymlinks(FilePath::fromString(m_finalFileName)).toString(); #ifdef Q_OS_WIN // Release the file lock diff --git a/src/libs/utils/settingsaccessor.cpp b/src/libs/utils/settingsaccessor.cpp index 662ad4bd43..f6d90df7e0 100644 --- a/src/libs/utils/settingsaccessor.cpp +++ b/src/libs/utils/settingsaccessor.cpp @@ -98,7 +98,7 @@ bool SettingsAccessor::saveSettings(const QVariantMap &data, QWidget *parent) co /*! * Read data from \a path. Do all the necessary postprocessing of the data. */ -SettingsAccessor::RestoreData SettingsAccessor::readData(const FileName &path, QWidget *parent) const +SettingsAccessor::RestoreData SettingsAccessor::readData(const FilePath &path, QWidget *parent) const { Q_UNUSED(parent); RestoreData result = readFile(path); @@ -111,13 +111,13 @@ SettingsAccessor::RestoreData SettingsAccessor::readData(const FileName &path, Q * Store the \a data in \a path on disk. Do all the necessary preprocessing of the data. */ optional<SettingsAccessor::Issue> -SettingsAccessor::writeData(const FileName &path, const QVariantMap &data, QWidget *parent) const +SettingsAccessor::writeData(const FilePath &path, const QVariantMap &data, QWidget *parent) const { Q_UNUSED(parent); return writeFile(path, prepareToWriteSettings(data)); } -QVariantMap SettingsAccessor::restoreSettings(const FileName &settingsPath, QWidget *parent) const +QVariantMap SettingsAccessor::restoreSettings(const FilePath &settingsPath, QWidget *parent) const { const RestoreData result = readData(settingsPath, parent); @@ -131,7 +131,7 @@ QVariantMap SettingsAccessor::restoreSettings(const FileName &settingsPath, QWid * * This method does not do *any* processing of the file contents. */ -SettingsAccessor::RestoreData SettingsAccessor::readFile(const FileName &path) const +SettingsAccessor::RestoreData SettingsAccessor::readFile(const FilePath &path) const { PersistentSettingsReader reader; if (!reader.load(path)) { @@ -156,7 +156,7 @@ SettingsAccessor::RestoreData SettingsAccessor::readFile(const FileName &path) c * This method does not do *any* processing of the file contents. */ optional<SettingsAccessor::Issue> -SettingsAccessor::writeFile(const FileName &path, const QVariantMap &data) const +SettingsAccessor::writeFile(const FilePath &path, const QVariantMap &data) const { if (data.isEmpty()) { return Issue(QCoreApplication::translate("Utils::SettingsAccessor", "Failed to Write File"), @@ -176,7 +176,7 @@ SettingsAccessor::writeFile(const FileName &path, const QVariantMap &data) const } SettingsAccessor::ProceedInfo -SettingsAccessor::reportIssues(const SettingsAccessor::Issue &issue, const FileName &path, +SettingsAccessor::reportIssues(const SettingsAccessor::Issue &issue, const FilePath &path, QWidget *parent) const { if (!path.exists()) @@ -217,14 +217,14 @@ QVariantMap SettingsAccessor::prepareToWriteSettings(const QVariantMap &data) co // BackingUpSettingsAccessor: // -------------------------------------------------------------------- -FileNameList BackUpStrategy::readFileCandidates(const FileName &baseFileName) const +FilePathList BackUpStrategy::readFileCandidates(const FilePath &baseFileName) const { const QFileInfo pfi = baseFileName.toFileInfo(); const QStringList filter(pfi.fileName() + '*'); const QFileInfoList list = QDir(pfi.dir()).entryInfoList(filter, QDir::Files | QDir::Hidden | QDir::System); - return Utils::transform(list, [](const QFileInfo &fi) { return FileName::fromString(fi.absoluteFilePath()); }); + return Utils::transform(list, [](const QFileInfo &fi) { return FilePath::fromString(fi.absoluteFilePath()); }); } int BackUpStrategy::compare(const SettingsAccessor::RestoreData &data1, @@ -239,14 +239,12 @@ int BackUpStrategy::compare(const SettingsAccessor::RestoreData &data1, return 0; } -optional<FileName> -BackUpStrategy::backupName(const QVariantMap &oldData, const FileName &path, const QVariantMap &data) const +optional<FilePath> +BackUpStrategy::backupName(const QVariantMap &oldData, const FilePath &path, const QVariantMap &data) const { if (oldData == data) return nullopt; - FileName backup = path; - backup.appendString(".bak"); - return backup; + return path.stringAppended(".bak"); } BackingUpSettingsAccessor::BackingUpSettingsAccessor(const QString &docType, @@ -264,9 +262,9 @@ BackingUpSettingsAccessor::BackingUpSettingsAccessor(std::unique_ptr<BackUpStrat { } SettingsAccessor::RestoreData -BackingUpSettingsAccessor::readData(const FileName &path, QWidget *parent) const +BackingUpSettingsAccessor::readData(const FilePath &path, QWidget *parent) const { - const FileNameList fileList = readFileCandidates(path); + const FilePathList fileList = readFileCandidates(path); if (fileList.isEmpty()) // No settings found at all. return RestoreData(path, QVariantMap()); @@ -289,7 +287,7 @@ BackingUpSettingsAccessor::readData(const FileName &path, QWidget *parent) const } optional<SettingsAccessor::Issue> -BackingUpSettingsAccessor::writeData(const FileName &path, const QVariantMap &data, +BackingUpSettingsAccessor::writeData(const FilePath &path, const QVariantMap &data, QWidget *parent) const { if (data.isEmpty()) @@ -300,9 +298,9 @@ BackingUpSettingsAccessor::writeData(const FileName &path, const QVariantMap &da return SettingsAccessor::writeData(path, data, parent); } -FileNameList BackingUpSettingsAccessor::readFileCandidates(const FileName &path) const +FilePathList BackingUpSettingsAccessor::readFileCandidates(const FilePath &path) const { - FileNameList result = Utils::filteredUnique(m_strategy->readFileCandidates(path)); + FilePathList result = Utils::filteredUnique(m_strategy->readFileCandidates(path)); if (result.removeOne(baseFilePath())) result.prepend(baseFilePath()); @@ -310,10 +308,10 @@ FileNameList BackingUpSettingsAccessor::readFileCandidates(const FileName &path) } SettingsAccessor::RestoreData -BackingUpSettingsAccessor::bestReadFileData(const FileNameList &candidates, QWidget *parent) const +BackingUpSettingsAccessor::bestReadFileData(const FilePathList &candidates, QWidget *parent) const { SettingsAccessor::RestoreData bestMatch; - for (const FileName &c : candidates) { + for (const FilePath &c : candidates) { RestoreData cData = SettingsAccessor::readData(c, parent); if (m_strategy->compare(bestMatch, cData) > 0) bestMatch = cData; @@ -321,7 +319,7 @@ BackingUpSettingsAccessor::bestReadFileData(const FileNameList &candidates, QWid return bestMatch; } -void BackingUpSettingsAccessor::backupFile(const FileName &path, const QVariantMap &data, +void BackingUpSettingsAccessor::backupFile(const FilePath &path, const QVariantMap &data, QWidget *parent) const { RestoreData oldSettings = SettingsAccessor::readData(path, parent); @@ -330,7 +328,7 @@ void BackingUpSettingsAccessor::backupFile(const FileName &path, const QVariantM // Do we need to do a backup? const QString origName = path.toString(); - optional<FileName> backupFileName = m_strategy->backupName(oldSettings.data, path, data); + optional<FilePath> backupFileName = m_strategy->backupName(oldSettings.data, path, data); if (backupFileName) QFile::copy(origName, backupFileName.value().toString()); } @@ -361,22 +359,23 @@ int VersionedBackUpStrategy::compare(const SettingsAccessor::RestoreData &data1, return -1; } -optional<FileName> -VersionedBackUpStrategy::backupName(const QVariantMap &oldData, const FileName &path, const QVariantMap &data) const +optional<FilePath> +VersionedBackUpStrategy::backupName(const QVariantMap &oldData, const FilePath &path, const QVariantMap &data) const { Q_UNUSED(data); - FileName backupName = path; + FilePath backupName = path; const QByteArray oldEnvironmentId = settingsIdFromMap(oldData); const int oldVersion = versionFromMap(oldData); if (!oldEnvironmentId.isEmpty() && oldEnvironmentId != m_accessor->settingsId()) - backupName.appendString('.' + QString::fromLatin1(oldEnvironmentId).mid(1, 7)); + backupName = backupName.stringAppended + ('.' + QString::fromLatin1(oldEnvironmentId).mid(1, 7)); if (oldVersion != m_accessor->currentVersion()) { VersionUpgrader *upgrader = m_accessor->upgrader(oldVersion); if (upgrader) - backupName.appendString('.' + upgrader->backupExtension()); + backupName = backupName.stringAppended('.' + upgrader->backupExtension()); else - backupName.appendString('.' + QString::number(oldVersion)); + backupName = backupName.stringAppended('.' + QString::number(oldVersion)); } if (backupName == path) return nullopt; @@ -464,7 +463,7 @@ bool UpgradingSettingsAccessor::isValidVersionAndId(const int version, const QBy && (id.isEmpty() || id == m_id || m_id.isEmpty()); } -SettingsAccessor::RestoreData UpgradingSettingsAccessor::readData(const FileName &path, +SettingsAccessor::RestoreData UpgradingSettingsAccessor::readData(const FilePath &path, QWidget *parent) const { return upgradeSettings(BackingUpSettingsAccessor::readData(path, parent), currentVersion()); @@ -606,7 +605,7 @@ MergingSettingsAccessor::MergingSettingsAccessor(std::unique_ptr<BackUpStrategy> UpgradingSettingsAccessor(std::move(strategy), docType, displayName, applicationDisplayName) { } -SettingsAccessor::RestoreData MergingSettingsAccessor::readData(const FileName &path, +SettingsAccessor::RestoreData MergingSettingsAccessor::readData(const FilePath &path, QWidget *parent) const { RestoreData mainData = UpgradingSettingsAccessor::readData(path, parent); // FULLY upgraded! diff --git a/src/libs/utils/settingsaccessor.h b/src/libs/utils/settingsaccessor.h index 10675cbf4c..fd5cf87ef7 100644 --- a/src/libs/utils/settingsaccessor.h +++ b/src/libs/utils/settingsaccessor.h @@ -99,7 +99,7 @@ public: class RestoreData { public: RestoreData() = default; - RestoreData(const FileName &path, const QVariantMap &data) : path{path}, data{data} { } + RestoreData(const FilePath &path, const QVariantMap &data) : path{path}, data{data} { } RestoreData(const QString &title, const QString &message, const Issue::Type type) : RestoreData(Issue(title, message, type)) { } @@ -109,7 +109,7 @@ public: bool hasError() const { return hasIssue() && issue.value().type == Issue::Type::ERROR; } bool hasWarning() const { return hasIssue() && issue.value().type == Issue::Type::WARNING; } - FileName path; + FilePath path; QVariantMap data; optional<Issue> issue; }; @@ -121,26 +121,26 @@ public: const QString displayName; const QString applicationDisplayName; - void setBaseFilePath(const FileName &baseFilePath) { m_baseFilePath = baseFilePath; } + void setBaseFilePath(const FilePath &baseFilePath) { m_baseFilePath = baseFilePath; } void setReadOnly() { m_readOnly = true; } - FileName baseFilePath() const { return m_baseFilePath; } + FilePath baseFilePath() const { return m_baseFilePath; } - virtual RestoreData readData(const FileName &path, QWidget *parent) const; - virtual optional<Issue> writeData(const FileName &path, const QVariantMap &data, QWidget *parent) const; + virtual RestoreData readData(const FilePath &path, QWidget *parent) const; + virtual optional<Issue> writeData(const FilePath &path, const QVariantMap &data, QWidget *parent) const; protected: // Report errors: - QVariantMap restoreSettings(const FileName &settingsPath, QWidget *parent) const; - ProceedInfo reportIssues(const Issue &issue, const FileName &path, QWidget *parent) const; + QVariantMap restoreSettings(const FilePath &settingsPath, QWidget *parent) const; + ProceedInfo reportIssues(const Issue &issue, const FilePath &path, QWidget *parent) const; virtual QVariantMap preprocessReadSettings(const QVariantMap &data) const; virtual QVariantMap prepareToWriteSettings(const QVariantMap &data) const; - virtual RestoreData readFile(const FileName &path) const; - virtual optional<Issue> writeFile(const FileName &path, const QVariantMap &data) const; + virtual RestoreData readFile(const FilePath &path) const; + virtual optional<Issue> writeFile(const FilePath &path, const QVariantMap &data) const; private: - FileName m_baseFilePath; + FilePath m_baseFilePath; mutable std::unique_ptr<PersistentSettingsWriter> m_writer; bool m_readOnly = false; }; @@ -154,14 +154,14 @@ class QTCREATOR_UTILS_EXPORT BackUpStrategy public: virtual ~BackUpStrategy() = default; - virtual FileNameList readFileCandidates(const FileName &baseFileName) const; + virtual FilePathList readFileCandidates(const FilePath &baseFileName) const; // Return -1 if data1 is better that data2, 0 if both are equally worthwhile // and 1 if data2 is better than data1 virtual int compare(const SettingsAccessor::RestoreData &data1, const SettingsAccessor::RestoreData &data2) const; - virtual optional<FileName> - backupName(const QVariantMap &oldData, const FileName &path, const QVariantMap &data) const; + virtual optional<FilePath> + backupName(const QVariantMap &oldData, const FilePath &path, const QVariantMap &data) const; }; class QTCREATOR_UTILS_EXPORT BackingUpSettingsAccessor : public SettingsAccessor @@ -172,16 +172,16 @@ public: BackingUpSettingsAccessor(std::unique_ptr<BackUpStrategy> &&strategy, const QString &docType, const QString &displayName, const QString &applicationDisplayName); - RestoreData readData(const FileName &path, QWidget *parent) const override; - optional<Issue> writeData(const FileName &path, const QVariantMap &data, + RestoreData readData(const FilePath &path, QWidget *parent) const override; + optional<Issue> writeData(const FilePath &path, const QVariantMap &data, QWidget *parent) const override; BackUpStrategy *strategy() const { return m_strategy.get(); } private: - FileNameList readFileCandidates(const FileName &path) const; - RestoreData bestReadFileData(const FileNameList &candidates, QWidget *parent) const; - void backupFile(const FileName &path, const QVariantMap &data, QWidget *parent) const; + FilePathList readFileCandidates(const FilePath &path) const; + RestoreData bestReadFileData(const FilePathList &candidates, QWidget *parent) const; + void backupFile(const FilePath &path, const QVariantMap &data, QWidget *parent) const; std::unique_ptr<BackUpStrategy> m_strategy; }; @@ -202,8 +202,8 @@ public: int compare(const SettingsAccessor::RestoreData &data1, const SettingsAccessor::RestoreData &data2) const override; - optional<FileName> - backupName(const QVariantMap &oldData, const FileName &path, const QVariantMap &data) const override; + optional<FilePath> + backupName(const QVariantMap &oldData, const FilePath &path, const QVariantMap &data) const override; const UpgradingSettingsAccessor *accessor() const { return m_accessor; } @@ -251,7 +251,7 @@ public: bool isValidVersionAndId(const int version, const QByteArray &id) const; VersionUpgrader *upgrader(const int version) const; - RestoreData readData(const FileName &path, QWidget *parent) const override; + RestoreData readData(const FilePath &path, QWidget *parent) const override; protected: QVariantMap prepareToWriteSettings(const QVariantMap &data) const override; @@ -284,7 +284,7 @@ public: const QString &docType, const QString &displayName, const QString &applicationDisplayName); - RestoreData readData(const FileName &path, QWidget *parent) const final; + RestoreData readData(const FilePath &path, QWidget *parent) const final; void setSecondaryAccessor(std::unique_ptr<SettingsAccessor> &&secondary); diff --git a/src/libs/utils/settingsselector.cpp b/src/libs/utils/settingsselector.cpp index ddfa7a137c..6ae5bff43c 100644 --- a/src/libs/utils/settingsselector.cpp +++ b/src/libs/utils/settingsselector.cpp @@ -73,7 +73,7 @@ SettingsSelector::SettingsSelector(QWidget *parent) : connect(m_renameButton, &QAbstractButton::clicked, this, &SettingsSelector::renameButtonClicked); connect(m_configurationCombo, - static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + QOverload<int>::of(&QComboBox::currentIndexChanged), this, &SettingsSelector::currentChanged); } diff --git a/src/libs/utils/shellcommand.cpp b/src/libs/utils/shellcommand.cpp index 14f52d2539..5a84c92f15 100644 --- a/src/libs/utils/shellcommand.cpp +++ b/src/libs/utils/shellcommand.cpp @@ -68,11 +68,11 @@ class ShellCommandPrivate { public: struct Job { - explicit Job(const QString &wd, const FileName &b, const QStringList &a, int t, + explicit Job(const QString &wd, const FilePath &b, const QStringList &a, int t, const ExitCodeInterpreter &interpreter); QString workingDirectory; - FileName binary; + FilePath binary; QStringList arguments; ExitCodeInterpreter exitCodeInterpreter; int timeoutS; @@ -113,7 +113,7 @@ ShellCommandPrivate::~ShellCommandPrivate() delete m_progressParser; } -ShellCommandPrivate::Job::Job(const QString &wd, const FileName &b, const QStringList &a, +ShellCommandPrivate::Job::Job(const QString &wd, const FilePath &b, const QStringList &a, int t, const ExitCodeInterpreter &interpreter) : workingDirectory(wd), binary(b), @@ -195,13 +195,13 @@ void ShellCommand::addFlags(unsigned f) d->m_flags |= f; } -void ShellCommand::addJob(const FileName &binary, const QStringList &arguments, +void ShellCommand::addJob(const FilePath &binary, const QStringList &arguments, const QString &workingDirectory, const ExitCodeInterpreter &interpreter) { addJob(binary, arguments, defaultTimeoutS(), workingDirectory, interpreter); } -void ShellCommand::addJob(const FileName &binary, const QStringList &arguments, int timeoutS, +void ShellCommand::addJob(const FilePath &binary, const QStringList &arguments, int timeoutS, const QString &workingDirectory, const ExitCodeInterpreter &interpreter) { d->m_jobs.push_back(Internal::ShellCommandPrivate::Job(workDirectory(workingDirectory), binary, @@ -319,7 +319,7 @@ void ShellCommand::run(QFutureInterface<void> &future) this->deleteLater(); } -SynchronousProcessResponse ShellCommand::runCommand(const FileName &binary, +SynchronousProcessResponse ShellCommand::runCommand(const FilePath &binary, const QStringList &arguments, int timeoutS, const QString &workingDirectory, const ExitCodeInterpreter &interpreter) @@ -359,7 +359,7 @@ SynchronousProcessResponse ShellCommand::runCommand(const FileName &binary, return response; } -SynchronousProcessResponse ShellCommand::runFullySynchronous(const FileName &binary, +SynchronousProcessResponse ShellCommand::runFullySynchronous(const FilePath &binary, const QStringList &arguments, QSharedPointer<OutputProxy> proxy, int timeoutS, @@ -399,7 +399,7 @@ SynchronousProcessResponse ShellCommand::runFullySynchronous(const FileName &bin return resp; } -SynchronousProcessResponse ShellCommand::runSynchronous(const FileName &binary, +SynchronousProcessResponse ShellCommand::runSynchronous(const FilePath &binary, const QStringList &arguments, QSharedPointer<OutputProxy> proxy, int timeoutS, diff --git a/src/libs/utils/shellcommand.h b/src/libs/utils/shellcommand.h index 22ff4a8df2..f0c427ef53 100644 --- a/src/libs/utils/shellcommand.h +++ b/src/libs/utils/shellcommand.h @@ -46,7 +46,7 @@ QT_END_NAMESPACE namespace Utils { -class FileName; +class FilePath; namespace Internal { class ShellCommandPrivate; } class QTCREATOR_UTILS_EXPORT ProgressParser @@ -79,7 +79,7 @@ signals: void append(const QString &text); void appendSilently(const QString &text); void appendError(const QString &text); - void appendCommand(const QString &workingDirectory, const Utils::FileName &binary, + void appendCommand(const QString &workingDirectory, const Utils::FilePath &binary, const QStringList &args); void appendMessage(const QString &text); }; @@ -112,9 +112,9 @@ public: QString displayName() const; void setDisplayName(const QString &name); - void addJob(const FileName &binary, const QStringList &arguments, + void addJob(const FilePath &binary, const QStringList &arguments, const QString &workingDirectory = QString(), const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter); - void addJob(const FileName &binary, const QStringList &arguments, int timeoutS, + void addJob(const FilePath &binary, const QStringList &arguments, int timeoutS, const QString &workingDirectory = QString(), const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter); void execute(); // Execute tasks asynchronously! void abort(); @@ -145,7 +145,7 @@ public: // This is called once per job in a thread. // When called from the UI thread it will execute fully synchronously, so no signals will // be triggered! - virtual SynchronousProcessResponse runCommand(const FileName &binary, const QStringList &arguments, + virtual SynchronousProcessResponse runCommand(const FilePath &binary, const QStringList &arguments, int timeoutS, const QString &workingDirectory = QString(), const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter); @@ -171,12 +171,12 @@ private: void run(QFutureInterface<void> &future); // Run without a event loop in fully blocking mode. No signals will be delivered. - SynchronousProcessResponse runFullySynchronous(const FileName &binary, const QStringList &arguments, + SynchronousProcessResponse runFullySynchronous(const FilePath &binary, const QStringList &arguments, QSharedPointer<OutputProxy> proxy, int timeoutS, const QString &workingDirectory, const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter); // Run with an event loop. Signals will be delivered. - SynchronousProcessResponse runSynchronous(const FileName &binary, const QStringList &arguments, + SynchronousProcessResponse runSynchronous(const FilePath &binary, const QStringList &arguments, QSharedPointer<OutputProxy> proxy, int timeoutS, const QString &workingDirectory, const ExitCodeInterpreter &interpreter = defaultExitCodeInterpreter); diff --git a/src/libs/utils/smallstringlayout.h b/src/libs/utils/smallstringlayout.h index dc6867af5d..c381fbd44b 100644 --- a/src/libs/utils/smallstringlayout.h +++ b/src/libs/utils/smallstringlayout.h @@ -198,13 +198,6 @@ struct StringDataLayout { shortString.string[0] = '\0'; } - StringDataLayout &operator=(const StringDataLayout &other) - { - this->shortString = other.shortString; - - return *this; - } - union { AllocatedLayout<MaximumShortStringDataAreaSize> allocated; ReferenceLayout<MaximumShortStringDataAreaSize> reference; diff --git a/src/libs/utils/stringutils.cpp b/src/libs/utils/stringutils.cpp index b39af367d5..161cf44e74 100644 --- a/src/libs/utils/stringutils.cpp +++ b/src/libs/utils/stringutils.cpp @@ -143,7 +143,7 @@ bool AbstractMacroExpander::expandNestedMacros(const QString &str, int *pos, QSt varName.reserve(strLen - i); for (; i < strLen; prev = c) { c = str.at(i++); - if (c == '\\' && i < strLen && validateVarName(varName)) { + if (c == '\\' && i < strLen) { c = str.at(i++); // For the replacement, do not skip the escape sequence when followed by a digit. // This is needed for enabling convenient capture group replacement, diff --git a/src/libs/utils/stylehelper.cpp b/src/libs/utils/stylehelper.cpp index e44037f5a1..388da7eeba 100644 --- a/src/libs/utils/stylehelper.cpp +++ b/src/libs/utils/stylehelper.cpp @@ -209,9 +209,9 @@ static void verticalGradientHelper(QPainter *p, const QRect &spanRect, const QRe void StyleHelper::verticalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect, bool lightColored) { if (StyleHelper::usePixmapCache()) { - QString key; + QColor keyColor = baseColor(lightColored); - key.sprintf("mh_vertical %d %d %d %d %d", + const QString key = QString::asprintf("mh_vertical %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), keyColor.rgb()); @@ -267,9 +267,9 @@ QRect &rect, bool lightColored) void StyleHelper::horizontalGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect, bool lightColored) { if (StyleHelper::usePixmapCache()) { - QString key; + QColor keyColor = baseColor(lightColored); - key.sprintf("mh_horizontal %d %d %d %d %d %d", + const QString key = QString::asprintf("mh_horizontal %d %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), keyColor.rgb(), spanRect.x()); @@ -309,8 +309,7 @@ void StyleHelper::drawArrow(QStyle::PrimitiveElement element, QPainter *painter, QRect r = option->rect; int size = qMin(r.height(), r.width()); QPixmap pixmap; - QString pixmapName; - pixmapName.sprintf("StyleHelper::drawArrow-%d-%d-%d-%f", + const QString pixmapName = QString::asprintf("StyleHelper::drawArrow-%d-%d-%d-%f", element, size, enabled, devicePixelRatio); if (!QPixmapCache::find(pixmapName, &pixmap)) { QImage image(size * devicePixelRatio, size * devicePixelRatio, QImage::Format_ARGB32_Premultiplied); @@ -351,8 +350,7 @@ void StyleHelper::drawArrow(QStyle::PrimitiveElement element, QPainter *painter, void StyleHelper::menuGradient(QPainter *painter, const QRect &spanRect, const QRect &clipRect) { if (StyleHelper::usePixmapCache()) { - QString key; - key.sprintf("mh_menu %d %d %d %d %d", + const QString key = QString::asprintf("mh_menu %d %d %d %d %d", spanRect.width(), spanRect.height(), clipRect.width(), clipRect.height(), StyleHelper::baseColor().rgb()); diff --git a/src/libs/utils/synchronousprocess.cpp b/src/libs/utils/synchronousprocess.cpp index a153e6ac91..bfb9d0d296 100644 --- a/src/libs/utils/synchronousprocess.cpp +++ b/src/libs/utils/synchronousprocess.cpp @@ -297,8 +297,7 @@ SynchronousProcess::SynchronousProcess() : { d->m_timer.setInterval(1000); connect(&d->m_timer, &QTimer::timeout, this, &SynchronousProcess::slotTimeout); - connect(&d->m_process, - static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished), + connect(&d->m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished), this, &SynchronousProcess::finished); connect(&d->m_process, &QProcess::errorOccurred, this, &SynchronousProcess::error); connect(&d->m_process, &QProcess::readyReadStandardOutput, diff --git a/src/libs/utils/textfieldcombobox.cpp b/src/libs/utils/textfieldcombobox.cpp index db940ad53a..aad64d2e7d 100644 --- a/src/libs/utils/textfieldcombobox.cpp +++ b/src/libs/utils/textfieldcombobox.cpp @@ -43,7 +43,7 @@ TextFieldComboBox::TextFieldComboBox(QWidget *parent) : QComboBox(parent) { setEditable(false); - connect(this, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), + connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &TextFieldComboBox::slotCurrentIndexChanged); } diff --git a/src/libs/utils/touchbar/touchbar.pri b/src/libs/utils/touchbar/touchbar.pri index 8d60ee423b..5f18595785 100644 --- a/src/libs/utils/touchbar/touchbar.pri +++ b/src/libs/utils/touchbar/touchbar.pri @@ -9,7 +9,6 @@ macos { $$PWD/touchbar_mac.mm \ $$PWD/touchbar_appdelegate_mac.mm - QT += macextras LIBS += -framework Foundation -framework AppKit } else { SOURCES += $$PWD/touchbar.cpp diff --git a/src/libs/utils/touchbar/touchbar_mac.mm b/src/libs/utils/touchbar/touchbar_mac.mm index 67c83aec0c..ee5a45bf72 100644 --- a/src/libs/utils/touchbar/touchbar_mac.mm +++ b/src/libs/utils/touchbar/touchbar_mac.mm @@ -29,8 +29,6 @@ #include <utils/utilsicons.h> -#include <QtMac> - #import <AppKit/NSButton.h> #import <AppKit/NSCustomTouchBarItem.h> #import <AppKit/NSImage.h> @@ -145,8 +143,12 @@ static NSImage *iconToTemplateNSImage(const QIcon &icon) { // touch bar icons are max 18-22 pts big. our are always 22 pts big. const QPixmap pixmap = icon.pixmap(22); - NSImage *image = QtMac::toNSImage(pixmap); - // toNSImage ignores devicePixelRatio, so fixup after the fact + + CGImageRef cgImage = pixmap.toImage().toCGImage(); + NSImage *image = [[NSImage alloc] initWithCGImage:cgImage size:NSZeroSize]; + CFRelease(cgImage); + + // The above code ignores devicePixelRatio, so fixup after the fact const CGFloat userWidth = pixmap.width() / pixmap.devicePixelRatio(); const CGFloat userHeight = pixmap.height() / pixmap.devicePixelRatio(); image.size = CGSizeMake(userWidth, userHeight); // scales the image diff --git a/src/libs/utils/unixutils.cpp b/src/libs/utils/unixutils.cpp index 7db05c22ea..77daae645c 100644 --- a/src/libs/utils/unixutils.cpp +++ b/src/libs/utils/unixutils.cpp @@ -77,7 +77,7 @@ QString UnixUtils::substituteFileBrowserParameters(const QString &pre, const QSt } else if (c == QLatin1Char('f')) { s = QLatin1Char('"') + file + QLatin1Char('"'); } else if (c == QLatin1Char('n')) { - s = QLatin1Char('"') + FileName::fromString(file).fileName() + QLatin1Char('"'); + s = QLatin1Char('"') + FilePath::fromString(file).fileName() + QLatin1Char('"'); } else if (c == QLatin1Char('%')) { s = c; } else { diff --git a/src/libs/utils/utils-lib.pri b/src/libs/utils/utils-lib.pri index 5a2712cc3f..c7fef47c97 100644 --- a/src/libs/utils/utils-lib.pri +++ b/src/libs/utils/utils-lib.pri @@ -13,7 +13,7 @@ shared { } } -QT += widgets gui network qml +QT += widgets gui network qml xml CONFIG += exceptions # used by portlist.cpp, textfileformat.cpp, and ssh/* @@ -27,6 +27,7 @@ SOURCES += \ $$PWD/environment.cpp \ $$PWD/environmentmodel.cpp \ $$PWD/environmentdialog.cpp \ + $$PWD/qrcparser.cpp \ $$PWD/qtcprocess.cpp \ $$PWD/reloadpromptutils.cpp \ $$PWD/settingsaccessor.cpp \ @@ -131,12 +132,14 @@ win32:SOURCES += $$PWD/consoleprocess_win.cpp else:SOURCES += $$PWD/consoleprocess_unix.cpp HEADERS += \ + $$PWD/genericconstants.h \ $$PWD/globalfilechangeblocker.h \ $$PWD/benchmarker.h \ $$PWD/environment.h \ $$PWD/environmentmodel.h \ $$PWD/environmentdialog.h \ $$PWD/pointeralgorithm.h \ + $$PWD/qrcparser.h \ $$PWD/qtcprocess.h \ $$PWD/utils_global.h \ $$PWD/reloadpromptutils.h \ @@ -266,7 +269,8 @@ HEADERS += \ $$PWD/removefiledialog.h \ $$PWD/differ.h \ $$PWD/cpplanguage_details.h \ - $$PWD/jsontreeitem.h + $$PWD/jsontreeitem.h \ + $$PWD/listmodel.h FORMS += $$PWD/filewizardpage.ui \ $$PWD/newclasswidget.ui \ diff --git a/src/libs/utils/utils.qbs b/src/libs/utils/utils.qbs index d50f7f41f6..694045e290 100644 --- a/src/libs/utils/utils.qbs +++ b/src/libs/utils/utils.qbs @@ -33,7 +33,7 @@ Project { cpp.frameworks: ["Foundation", "AppKit"] } - Depends { name: "Qt"; submodules: ["concurrent", "network", "qml", "widgets"] } + Depends { name: "Qt"; submodules: ["concurrent", "network", "qml", "widgets", "xml"] } Depends { name: "Qt.macextras"; condition: qbs.targetOS.contains("macos") } Depends { name: "app_version_header" } @@ -146,6 +146,7 @@ Project { "jsontreeitem.h", "linecolumn.h", "link.h", + "listmodel.h", "listutils.h", "macroexpander.cpp", "macroexpander.h", @@ -191,6 +192,8 @@ Project { "proxycredentialsdialog.cpp", "proxycredentialsdialog.h", "proxycredentialsdialog.ui", + "qrcparser.cpp", + "qrcparser.h", "qtcassert.cpp", "qtcassert.h", "qtcolorbutton.cpp", diff --git a/src/libs/utils/utils.qrc b/src/libs/utils/utils.qrc index 6ad7dd3196..a8731b6b20 100644 --- a/src/libs/utils/utils.qrc +++ b/src/libs/utils/utils.qrc @@ -34,6 +34,8 @@ <file>images/locked@2x.png</file> <file>images/unlocked.png</file> <file>images/unlocked@2x.png</file> + <file>images/pinned.png</file> + <file>images/pinned@2x.png</file> <file>images/broken.png</file> <file>images/broken@2x.png</file> <file>images/notloaded.png</file> @@ -213,6 +215,14 @@ <file>images/macos_touchbar_bookmark@2x.png</file> <file>images/macos_touchbar_clear.png</file> <file>images/macos_touchbar_clear@2x.png</file> + <file>images/settings.png</file> + <file>images/settings@2x.png</file> + <file>images/sort_alphabetically.png</file> + <file>images/sort_alphabetically@2x.png</file> + <file>images/toggleprogressdetails.png</file> + <file>images/toggleprogressdetails@2x.png</file> + <file>images/unknownfile.png</file> + <file>images/dir.png</file> </qresource> <qresource prefix="/codemodel"> <file>images/enum.png</file> diff --git a/src/libs/utils/utilsicons.cpp b/src/libs/utils/utilsicons.cpp index 9855165c3e..2227f530c6 100644 --- a/src/libs/utils/utilsicons.cpp +++ b/src/libs/utils/utilsicons.cpp @@ -28,7 +28,6 @@ namespace Utils { namespace Icons { - const Icon HOME({ {QLatin1String(":/utils/images/home.png"), Theme::PanelTextColorDark}}, Icon::Tint); const Icon HOME_TOOLBAR({ @@ -43,6 +42,8 @@ const Icon LOCKED({ {QLatin1String(":/utils/images/locked.png"), Theme::PanelTextColorDark}}, Icon::Tint); const Icon UNLOCKED_TOOLBAR({ {QLatin1String(":/utils/images/unlocked.png"), Theme::IconsBaseColor}}); +const Icon PINNED({ + {QLatin1String(":/utils/images/pinned.png"), Theme::PanelTextColorDark}}, Icon::Tint); const Icon NEXT({ {QLatin1String(":/utils/images/next.png"), Theme::IconsWarningColor}}, Icon::MenuTintedStyle); const Icon NEXT_TOOLBAR({ @@ -85,6 +86,8 @@ const Icon SNAPSHOT_TOOLBAR({ const Icon NEWSEARCH_TOOLBAR({ {QLatin1String(":/utils/images/zoom.png"), Theme::IconsBaseColor}, {QLatin1String(":/utils/images/iconoverlay_add_small.png"), Theme::IconsRunColor}}); +const Icon SETTINGS_TOOLBAR({ + {QLatin1String(":/utils/images/settings.png"), Theme::IconsBaseColor}}); const Icon NEWFILE({ {QLatin1String(":/utils/images/filenew.png"), Theme::PanelTextColorMid}}, Icon::Tint); @@ -102,6 +105,11 @@ const Icon EXPORTFILE_TOOLBAR({ const Icon MULTIEXPORTFILE_TOOLBAR({ {QLatin1String(":/utils/images/filemultiexport.png"), Theme::IconsBaseColor}}); +const Icon UNKNOWN_FILE({ + {QLatin1String(":/utils/images/unknownfile.png"), Theme::IconsBaseColor}}); +const Icon DIR({ + {QLatin1String(":/utils/images/dir.png"), Theme::IconsBaseColor}}); + const Icon UNDO({ {QLatin1String(":/utils/images/undo.png"), Theme::PanelTextColorMid}}, Icon::Tint); const Icon UNDO_TOOLBAR({ @@ -182,6 +190,11 @@ const Icon LINK({ {QLatin1String(":/utils/images/linkicon.png"), Theme::PanelTextColorMid}}, Icon::Tint); const Icon LINK_TOOLBAR({ {QLatin1String(":/utils/images/linkicon.png"), Theme::IconsBaseColor}}); +const Icon SORT_ALPHABETICALLY_TOOLBAR({ + {QLatin1String(":/utils/images/sort_alphabetically.png"), Theme::IconsBaseColor}}); +const Icon TOGGLE_PROGRESSDETAILS_TOOLBAR({ + {QLatin1String(":/utils/images/toggleprogressdetails.png"), Theme::IconsBaseColor}}); + const Icon WARNING({ {QLatin1String(":/utils/images/warningfill.png"), Theme::BackgroundColorNormal}, {QLatin1String(":/utils/images/warning.png"), Theme::IconsWarningColor}}, Icon::Tint); @@ -444,6 +457,10 @@ QIcon CodeModelIcon::iconForType(CodeModelIcon::Type type) }, Icon::Tint).icon()); return icon; } + case Unknown: { + const static QIcon icon(Icons::EMPTY16.icon()); + return icon; + } default: break; } diff --git a/src/libs/utils/utilsicons.h b/src/libs/utils/utilsicons.h index b229523911..ae6cd4d1dc 100644 --- a/src/libs/utils/utilsicons.h +++ b/src/libs/utils/utilsicons.h @@ -38,6 +38,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon EDIT_CLEAR_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon LOCKED_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon LOCKED; QTCREATOR_UTILS_EXPORT extern const Icon UNLOCKED_TOOLBAR; +QTCREATOR_UTILS_EXPORT extern const Icon PINNED; QTCREATOR_UTILS_EXPORT extern const Icon NEXT; QTCREATOR_UTILS_EXPORT extern const Icon NEXT_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon PREV; @@ -56,6 +57,7 @@ QTCREATOR_UTILS_EXPORT extern const Icon BOOKMARK_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon BOOKMARK_TEXTEDITOR; QTCREATOR_UTILS_EXPORT extern const Icon SNAPSHOT_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon NEWSEARCH_TOOLBAR; +QTCREATOR_UTILS_EXPORT extern const Icon SETTINGS_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon NEWFILE; QTCREATOR_UTILS_EXPORT extern const Icon OPENFILE; @@ -66,6 +68,9 @@ QTCREATOR_UTILS_EXPORT extern const Icon SAVEFILE_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon EXPORTFILE_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon MULTIEXPORTFILE_TOOLBAR; +QTCREATOR_UTILS_EXPORT extern const Icon UNKNOWN_FILE; +QTCREATOR_UTILS_EXPORT extern const Icon DIR; + QTCREATOR_UTILS_EXPORT extern const Icon UNDO; QTCREATOR_UTILS_EXPORT extern const Icon UNDO_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon REDO; @@ -106,6 +111,8 @@ QTCREATOR_UTILS_EXPORT extern const Icon CLOSE_SPLIT_RIGHT; QTCREATOR_UTILS_EXPORT extern const Icon FILTER; QTCREATOR_UTILS_EXPORT extern const Icon LINK; QTCREATOR_UTILS_EXPORT extern const Icon LINK_TOOLBAR; +QTCREATOR_UTILS_EXPORT extern const Icon SORT_ALPHABETICALLY_TOOLBAR; +QTCREATOR_UTILS_EXPORT extern const Icon TOGGLE_PROGRESSDETAILS_TOOLBAR; QTCREATOR_UTILS_EXPORT extern const Icon INFO; QTCREATOR_UTILS_EXPORT extern const Icon INFO_TOOLBAR; diff --git a/src/libs/utils/variant.h b/src/libs/utils/variant.h index 00e9eaf252..371b9312d3 100644 --- a/src/libs/utils/variant.h +++ b/src/libs/utils/variant.h @@ -30,10 +30,25 @@ */ // TODO: replace by #include <(experimental/)variant> depending on compiler and C++ version -#include <3rdparty/variant/variant.hpp> +#if __cplusplus >= 201703L +#error Please delete variant.hpp and the #else section below, then remove this error +#include <variant> namespace Utils { +using std::get; +using std::get_if; +using std::holds_alternative; +using std::variant; +} // namespace Utils -using namespace mpark; +#else +#include <3rdparty/variant/variant.hpp> +namespace Utils { +using mpark::get; +using mpark::get_if; +using mpark::holds_alternative; +using mpark::variant; } // namespace Utils + +#endif diff --git a/src/libs/utils/wizardpage.h b/src/libs/utils/wizardpage.h index fa8c60b052..abeccfce13 100644 --- a/src/libs/utils/wizardpage.h +++ b/src/libs/utils/wizardpage.h @@ -29,6 +29,7 @@ #include <QSet> #include <QString> +#include <QVariant> #include <QWizardPage> #include <functional> @@ -41,30 +42,30 @@ namespace Internal { class QTCREATOR_UTILS_EXPORT ObjectToFieldWidgetConverter : public QWidget { Q_OBJECT - Q_PROPERTY(QString text READ text NOTIFY textChanged) + Q_PROPERTY(QVariant value READ value NOTIFY valueChanged) public: - template <class T, typename... Arguments> - static ObjectToFieldWidgetConverter *create(T *sender, void (T::*member)(Arguments...), const std::function<QString()> &toTextFunction) + template<class T, typename... Arguments> + static ObjectToFieldWidgetConverter *create(T *sender, + void (T::*member)(Arguments...), + const std::function<QVariant()> &toVariantFunction) { auto widget = new ObjectToFieldWidgetConverter(); - widget->toTextFunction = toTextFunction; + widget->toVariantFunction = toVariantFunction; connect(sender, &QObject::destroyed, widget, &QObject::deleteLater); - connect(sender, member, widget, [widget] () { - emit widget->textChanged(widget->text()); - }); + connect(sender, member, widget, [widget]() { emit widget->valueChanged(widget->value()); }); return widget; } signals: - void textChanged(const QString&); + void valueChanged(const QVariant &); private: ObjectToFieldWidgetConverter () = default; - // is used by the property text - QString text() {return toTextFunction();} - std::function<QString()> toTextFunction; + // is used by the property value + QVariant value() { return toVariantFunction(); } + std::function<QVariant()> toVariantFunction; }; } // Internal @@ -79,10 +80,17 @@ public: virtual void pageWasAdded(); // called when this page was added to a Utils::Wizard template<class T, typename... Arguments> - void registerObjectAsFieldWithName(const QString &name, T *sender, void (T::*changeSignal)(Arguments...), - const std::function<QString()> &senderToString) + void registerObjectAsFieldWithName(const QString &name, + T *sender, + void (T::*changeSignal)(Arguments...), + const std::function<QVariant()> &senderToVariant) { - registerFieldWithName(name, Internal::ObjectToFieldWidgetConverter::create(sender, changeSignal, senderToString), "text", SIGNAL(textChanged(QString))); + registerFieldWithName(name, + Internal::ObjectToFieldWidgetConverter::create(sender, + changeSignal, + senderToVariant), + "value", + SIGNAL(valueChanged(QValue))); } void registerFieldWithName(const QString &name, QWidget *widget, |