diff --git a/mkspecs/features/create_cmake.prf b/mkspecs/features/create_cmake.prf index f9747bb329..8beeba3f39 100644 --- a/mkspecs/features/create_cmake.prf +++ b/mkspecs/features/create_cmake.prf @@ -133,11 +133,16 @@ contains(CONFIG, plugin) { CMAKE_PLUGIN_NAME = $$PLUGIN_CLASS_NAME win32 { - isEmpty(CMAKE_STATIC_TYPE): CMAKE_PlUGIN_EXT = .dll - else: CMAKE_PlUGIN_EXT = .lib - - CMAKE_PLUGIN_LOCATION_RELEASE = $$PLUGIN_TYPE/$${TARGET}$${CMAKE_PlUGIN_EXT} - CMAKE_PLUGIN_LOCATION_DEBUG = $$PLUGIN_TYPE/$${TARGET}d$${CMAKE_PlUGIN_EXT} + isEmpty(CMAKE_STATIC_TYPE) { + CMAKE_PLUGIN_LOCATION_RELEASE = $$PLUGIN_TYPE/$${TARGET}.dll + CMAKE_PLUGIN_LOCATION_DEBUG = $$PLUGIN_TYPE/$${TARGET}d.dll + } else:mingw { + CMAKE_PLUGIN_LOCATION_RELEASE = $$PLUGIN_TYPE/lib$${TARGET}.a + CMAKE_PLUGIN_LOCATION_DEBUG = $$PLUGIN_TYPE/lib$${TARGET}d.a + } else { # MSVC static + CMAKE_PLUGIN_LOCATION_RELEASE = $$PLUGIN_TYPE/$${TARGET}.lib + CMAKE_PLUGIN_LOCATION_DEBUG = $$PLUGIN_TYPE/$${TARGET}d.lib + } } else { mac { isEmpty(CMAKE_STATIC_TYPE): CMAKE_PlUGIN_EXT = .dylib diff --git a/mkspecs/features/java.prf b/mkspecs/features/java.prf index 1c55b8974e..1b9754da8a 100644 --- a/mkspecs/features/java.prf +++ b/mkspecs/features/java.prf @@ -30,7 +30,7 @@ CONFIG += plugin no_plugin_name_prefix javac.input = JAVASOURCES javac.output = $$CLASS_DIR javac.CONFIG += combine -javac.commands = javac -source 6 -target 6 -Xlint:unchecked -bootclasspath $$ANDROID_JAR_FILE -cp $$shell_quote($$system_path($$join(JAVACLASSPATH, $$DIRLIST_SEP))) -d $$shell_quote($$CLASS_DIR) ${QMAKE_FILE_IN} +javac.commands = javac -source 6 -target 6 -Xlint:unchecked -bootclasspath $$ANDROID_JAR_FILE -cp $$shell_quote($$system_path($$join(JAVACLASSPATH, $$DIRLIST_SEPARATOR))) -d $$shell_quote($$CLASS_DIR) ${QMAKE_FILE_IN} # Force rebuild every time, because we don't know the paths of the destination files # as they depend on the code. javac.depends = FORCE diff --git a/mkspecs/features/qt_module.prf b/mkspecs/features/qt_module.prf index 6ab826771d..8599a47ecd 100644 --- a/mkspecs/features/qt_module.prf +++ b/mkspecs/features/qt_module.prf @@ -101,8 +101,10 @@ mac:CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) { FRAMEWORK_HEADERS.version = Versions FRAMEWORK_HEADERS.files = $$SYNCQT.HEADER_FILES $$SYNCQT.HEADER_CLASSES FRAMEWORK_HEADERS.path = Headers + FRAMEWORK_PRIVATE_HEADERS.version = Versions FRAMEWORK_PRIVATE_HEADERS.files = $$SYNCQT.PRIVATE_HEADER_FILES FRAMEWORK_PRIVATE_HEADERS.path = Headers/$$VERSION/$$MODULE_INCNAME/private + FRAMEWORK_QPA_HEADERS.version = Versions FRAMEWORK_QPA_HEADERS.files = $$SYNCQT.QPA_HEADER_FILES FRAMEWORK_QPA_HEADERS.path = Headers/$$VERSION/$$MODULE_INCNAME/qpa QMAKE_BUNDLE_DATA += FRAMEWORK_HEADERS FRAMEWORK_PRIVATE_HEADERS FRAMEWORK_QPA_HEADERS diff --git a/mkspecs/features/resolve_config.prf b/mkspecs/features/resolve_config.prf index 7835fe4f7c..b712a2b58e 100644 --- a/mkspecs/features/resolve_config.prf +++ b/mkspecs/features/resolve_config.prf @@ -25,10 +25,13 @@ CONFIG(static, static|shared) { !macx-xcode: \ addExclusiveBuilds(shared, static) -CONFIG(debug, debug|release): \ +CONFIG(debug, debug|release) { CONFIG -= release -else: \ + !force_debug_plist:debug_and_release: \ + CONFIG += no_plist +} else { CONFIG -= debug +} !macx-xcode { addExclusiveBuilds(debug, release) diff --git a/mkspecs/macx-clang-32/Info.plist.lib b/mkspecs/macx-clang-32/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-clang-32/Info.plist.lib +++ b/mkspecs/macx-clang-32/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/mkspecs/macx-clang/Info.plist.lib b/mkspecs/macx-clang/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-clang/Info.plist.lib +++ b/mkspecs/macx-clang/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/mkspecs/macx-g++-32/Info.plist.lib b/mkspecs/macx-g++-32/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-g++-32/Info.plist.lib +++ b/mkspecs/macx-g++-32/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/mkspecs/macx-g++/Info.plist.lib b/mkspecs/macx-g++/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-g++/Info.plist.lib +++ b/mkspecs/macx-g++/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/mkspecs/macx-g++40/Info.plist.lib b/mkspecs/macx-g++40/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-g++40/Info.plist.lib +++ b/mkspecs/macx-g++40/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/mkspecs/macx-g++42/Info.plist.lib b/mkspecs/macx-g++42/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-g++42/Info.plist.lib +++ b/mkspecs/macx-g++42/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/mkspecs/macx-icc/Info.plist.lib b/mkspecs/macx-icc/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-icc/Info.plist.lib +++ b/mkspecs/macx-icc/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/mkspecs/macx-ios-clang/Info.plist.lib b/mkspecs/macx-ios-clang/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-ios-clang/Info.plist.lib +++ b/mkspecs/macx-ios-clang/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/mkspecs/macx-llvm/Info.plist.lib b/mkspecs/macx-llvm/Info.plist.lib index 63f1a945c2..2a44d1721e 100644 --- a/mkspecs/macx-llvm/Info.plist.lib +++ b/mkspecs/macx-llvm/Info.plist.lib @@ -6,6 +6,8 @@ FMWK CFBundleShortVersionString @SHORT_VERSION@ + CFBundleVersion + @FULL_VERSION@ CFBundleGetInfoString Created by Qt/QMake CFBundleSignature diff --git a/qmake/generators/unix/unixmake.cpp b/qmake/generators/unix/unixmake.cpp index 4c262125a2..e54ddf8ad7 100644 --- a/qmake/generators/unix/unixmake.cpp +++ b/qmake/generators/unix/unixmake.cpp @@ -306,8 +306,6 @@ UnixMakefileGenerator::init() } if(!bundle.isEmpty()) { project->values("QMAKE_BUNDLE") = ProStringList(bundle); - project->values("ALL_DEPS") += project->first("QMAKE_PKGINFO"); - project->values("ALL_DEPS") += project->first("QMAKE_BUNDLE_RESOURCE_FILE"); } else { project->values("QMAKE_BUNDLE").clear(); project->values("QMAKE_BUNDLE_LOCATION").clear(); diff --git a/qmake/generators/unix/unixmake2.cpp b/qmake/generators/unix/unixmake2.cpp index 989ec626d3..d9b0e10057 100644 --- a/qmake/generators/unix/unixmake2.cpp +++ b/qmake/generators/unix/unixmake2.cpp @@ -430,6 +430,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) } } } + QString allDeps; if (!project->values("QMAKE_APP_FLAG").isEmpty() || project->first("TEMPLATE") == "aux") { QString destdir = project->first("DESTDIR").toQString(); if(!project->isEmpty("QMAKE_BUNDLE")) { @@ -490,8 +491,6 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) deps.prepend(incr_target_dir + " "); incr_deps = "$(OBJECTS)"; } - t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << "$(TARGET)" - << endl << endl; //real target t << var("TARGET") << ": " << var("PRE_TARGETDEPS") << " " << incr_deps << " " << target_deps @@ -505,9 +504,6 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) t << "\n\t" << var("QMAKE_POST_LINK"); t << endl << endl; } else { - t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << "$(TARGET)" - << endl << endl; - t << "$(TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) " << target_deps << " " << var("POST_TARGETDEPS") << "\n\t"; if (project->first("TEMPLATE") != "aux") { @@ -521,6 +517,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) } t << endl << endl; } + allDeps = " $(TARGET)"; } else if(!project->isActiveConfig("staticlib")) { QString destdir = unescapeFilePath(project->first("DESTDIR").toQString()), incr_deps; if(!project->isEmpty("QMAKE_BUNDLE")) { @@ -580,19 +577,15 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) incr_deps = "$(OBJECTS)"; } - t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") - << " " << destdir << "$(TARGET)\n\n"; - //real target t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") << " " << incr_deps << " $(SUBLIBS) " << target_deps << " " << var("POST_TARGETDEPS"); } else { - t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << " " << - destdir << "$(TARGET)\n\n"; t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) $(SUBLIBS) $(OBJCOMP) " << target_deps << " " << var("POST_TARGETDEPS"); } + allDeps = ' ' + destdir + "$(TARGET)"; if(!destdir.isEmpty()) t << "\n\t" << mkdir_p_asstring(destdir, false); if(!project->isEmpty("QMAKE_PRE_LINK")) @@ -695,9 +688,9 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) } } else { QString destdir = project->first("DESTDIR").toQString(); - t << "all: " << escapeDependencyPath(deps) << " " << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")),""," "," ") << destdir << "$(TARGET) " - << varGlue("QMAKE_AR_SUBLIBS", destdir, " " + destdir, "") << "\n\n" - << "staticlib: " << destdir << "$(TARGET)\n\n"; + allDeps = ' ' + destdir + "$(TARGET)" + + varGlue("QMAKE_AR_SUBLIBS", ' ' + destdir, ' ' + destdir, ""); + t << "staticlib: " << destdir << "$(TARGET)\n\n"; if(project->isEmpty("QMAKE_AR_SUBLIBS")) { t << destdir << "$(TARGET): " << var("PRE_TARGETDEPS") << " $(OBJECTS) $(OBJCOMP) " << var("POST_TARGETDEPS") << "\n\t"; @@ -763,85 +756,111 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) << "@$(QMAKE) -prl " << buildArgs() << " " << project->projectFile() << endl; } - if(!project->first("QMAKE_PKGINFO").isEmpty()) { - ProString pkginfo = escapeFilePath(project->first("QMAKE_PKGINFO")); - QString destdir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents"; - t << pkginfo << ": \n\t"; - if(!destdir.isEmpty()) - t << mkdir_p_asstring(destdir) << "\n\t"; - t << "@$(DEL_FILE) " << pkginfo << "\n\t" - << "@echo \"APPL" - << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) - << "\" >" << pkginfo << endl; - } - if(!project->first("QMAKE_BUNDLE_RESOURCE_FILE").isEmpty()) { - ProString resources = escapeFilePath(project->first("QMAKE_BUNDLE_RESOURCE_FILE")); - bundledFiles << resources; - QString destdir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources"; - t << resources << ": \n\t"; - t << mkdir_p_asstring(destdir) << "\n\t"; - t << "@touch " << resources << "\n\t\n"; - } - if(!project->isEmpty("QMAKE_BUNDLE")) { - //copy the plist - QString info_plist = escapeFilePath(fileFixify(project->first("QMAKE_INFO_PLIST").toQString())), - info_plist_out = escapeFilePath(project->first("QMAKE_INFO_PLIST_OUT").toQString()); - if (info_plist.isEmpty()) - info_plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"); - bundledFiles << info_plist_out; - QString destdir = info_plist_out.section(Option::dir_sep, 0, -2); - t << info_plist_out << ": \n\t"; - if(!destdir.isEmpty()) - t << mkdir_p_asstring(destdir, false) << "\n\t"; - ProStringList commonSedArgs; - if (!project->values("VERSION").isEmpty()) - commonSedArgs << "-e \"s,@SHORT_VERSION@," << project->first("VER_MAJ") << "." << project->first("VER_MIN") << ",g\" "; - commonSedArgs << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? - QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" "; - if(project->first("TEMPLATE") == "app") { - QString icon = fileFixify(var("ICON")); - QString bundlePrefix = project->first("QMAKE_TARGET_BUNDLE_PREFIX").toQString(); - if (bundlePrefix.isEmpty()) - bundlePrefix = "com.yourcompany"; - if (bundlePrefix.endsWith(".")) - bundlePrefix.chop(1); - QString bundleIdentifier = bundlePrefix + "." + var("QMAKE_BUNDLE"); - if (bundleIdentifier.endsWith(".app")) - bundleIdentifier.chop(4); - t << "@$(DEL_FILE) " << info_plist_out << "\n\t" - << "@sed "; - foreach (const ProString &arg, commonSedArgs) - t << arg; - t << "-e \"s,@ICON@," << icon.section(Option::dir_sep, -1) << ",g\" " - << "-e \"s,@BUNDLEIDENTIFIER@," << bundleIdentifier << ",g\" " - << "-e \"s,@EXECUTABLE@," << var("QMAKE_ORIG_TARGET") << ",g\" " - << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? - QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" " - << "" << info_plist << " >" << info_plist_out << endl; - //copy the icon - if(!project->isEmpty("ICON")) { - QString dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/Contents/Resources/"; - const QString icon_path = escapeFilePath(dir + icon.section(Option::dir_sep, -1)); - bundledFiles << icon_path; - t << icon_path << ": " << icon << "\n\t" - << mkdir_p_asstring(dir) << "\n\t" - << "@$(DEL_FILE) " << icon_path << "\n\t" - << "@$(COPY_FILE) " << escapeFilePath(icon) << " " << icon_path << endl; - } - } else { - t << "@$(DEL_FILE) " << info_plist_out << "\n\t" - << "@sed "; - foreach (const ProString &arg, commonSedArgs) - t << arg; - t << "-e \"s,@LIBRARY@," << var("QMAKE_ORIG_TARGET") << ",g\" " - << "-e \"s,@TYPEINFO@," - << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? - QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" " - << "" << info_plist << " >" << info_plist_out << endl; + if (!project->isEmpty("QMAKE_BUNDLE")) { + QHash symlinks; + ProStringList &alldeps = project->values("ALL_DEPS"); + QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/"; + if (!project->first("QMAKE_PKGINFO").isEmpty()) { + ProString pkginfo = escapeFilePath(project->first("QMAKE_PKGINFO")); + bundledFiles << pkginfo; + alldeps << pkginfo; + QString destdir = bundle_dir + "Contents"; + t << pkginfo << ": \n\t"; + if (!destdir.isEmpty()) + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "@$(DEL_FILE) " << pkginfo << "\n\t" + << "@echo \"APPL" + << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") + ? QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) + << "\" >" << pkginfo << endl; } + if (!project->first("QMAKE_BUNDLE_RESOURCE_FILE").isEmpty()) { + ProString resources = escapeFilePath(project->first("QMAKE_BUNDLE_RESOURCE_FILE")); + bundledFiles << resources; + alldeps << resources; + QString destdir = bundle_dir + "Contents/Resources"; + t << resources << ": \n\t"; + t << mkdir_p_asstring(destdir) << "\n\t"; + t << "@touch " << resources << "\n\t\n"; + } + //copy the plist + while (!project->isActiveConfig("no_plist")) { // 'while' just to be able to 'break' + QString info_plist = escapeFilePath(fileFixify(project->first("QMAKE_INFO_PLIST").toQString())); + if (info_plist.isEmpty()) + info_plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"); + if (!exists(Option::fixPathToLocalOS(info_plist))) { + warn_msg(WarnLogic, "Could not resolve Info.plist: '%s'. Check if QMAKE_INFO_PLIST points to a valid file.", + info_plist.toLatin1().constData()); + break; + } + bool isApp = (project->first("TEMPLATE") == "app"); + QString info_plist_out = escapeFilePath( + bundle_dir + (isApp ? "Contents/Info.plist" + : "Versions/" + project->first("QMAKE_FRAMEWORK_VERSION") + + "/Resources/Info.plist")); + bundledFiles << info_plist_out; + alldeps << info_plist_out; + QString destdir = info_plist_out.section(Option::dir_sep, 0, -2); + t << info_plist_out << ": \n\t"; + if (!destdir.isEmpty()) + t << mkdir_p_asstring(destdir, false) << "\n\t"; + ProStringList commonSedArgs; + if (!project->values("VERSION").isEmpty()) { + commonSedArgs << "-e \"s,@SHORT_VERSION@," << project->first("VER_MAJ") << "." + << project->first("VER_MIN") << ",g\" "; + commonSedArgs << "-e \"s,@FULL_VERSION@," << project->first("VER_MAJ") << "." + << project->first("VER_MIN") << "." + << project->first("VER_PAT") << ",g\" "; + } + commonSedArgs << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" "; + if (isApp) { + QString icon = fileFixify(var("ICON")); + QString bundlePrefix = project->first("QMAKE_TARGET_BUNDLE_PREFIX").toQString(); + if (bundlePrefix.isEmpty()) + bundlePrefix = "com.yourcompany"; + if (bundlePrefix.endsWith(".")) + bundlePrefix.chop(1); + QString bundleIdentifier = bundlePrefix + "." + var("QMAKE_BUNDLE"); + if (bundleIdentifier.endsWith(".app")) + bundleIdentifier.chop(4); + t << "@$(DEL_FILE) " << info_plist_out << "\n\t" + << "@sed "; + foreach (const ProString &arg, commonSedArgs) + t << arg; + t << "-e \"s,@ICON@," << icon.section(Option::dir_sep, -1) << ",g\" " + << "-e \"s,@BUNDLEIDENTIFIER@," << bundleIdentifier << ",g\" " + << "-e \"s,@EXECUTABLE@," << var("QMAKE_ORIG_TARGET") << ",g\" " + << "-e \"s,@TYPEINFO@,"<< (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" " + << "" << info_plist << " >" << info_plist_out << endl; + //copy the icon + if (!project->isEmpty("ICON")) { + QString dir = bundle_dir + "Contents/Resources/"; + const QString icon_path = escapeFilePath(dir + icon.section(Option::dir_sep, -1)); + bundledFiles << icon_path; + alldeps << icon_path; + t << icon_path << ": " << icon << "\n\t" + << mkdir_p_asstring(dir) << "\n\t" + << "@$(DEL_FILE) " << icon_path << "\n\t" + << "@$(COPY_FILE) " << escapeFilePath(icon) << " " << icon_path << endl; + } + } else { + symlinks[bundle_dir + "Resources"] = "Versions/Current/Resources"; + t << "@$(DEL_FILE) " << info_plist_out << "\n\t" + << "@sed "; + foreach (const ProString &arg, commonSedArgs) + t << arg; + t << "-e \"s,@LIBRARY@," << var("QMAKE_ORIG_TARGET") << ",g\" " + << "-e \"s,@TYPEINFO@," + << (project->isEmpty("QMAKE_PKGINFO_TYPEINFO") ? + QString::fromLatin1("????") : project->first("QMAKE_PKGINFO_TYPEINFO").left(4)) << ",g\" " + << "" << info_plist << " >" << info_plist_out << endl; + } + break; + } // project->isActiveConfig("no_plist") //copy other data if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { - QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/"; const ProStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA"); for(int i = 0; i < bundle_data.count(); i++) { const ProStringList &files = project->values(ProKey(bundle_data[i] + ".files")); @@ -851,11 +870,12 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) if (!project->isEmpty(vkey)) { QString version = project->first(vkey) + "/" + project->first("QMAKE_FRAMEWORK_VERSION") + "/"; - QString link = Option::fixPathToLocalOS(path + project->first(pkey)); - bundledFiles << link; - t << link << ": \n\t" - << mkdir_p_asstring(path) << "\n\t" - << "@$(SYMLINK) " << version << project->first(pkey) << " " << path << endl; + ProString name = project->first(pkey); + int pos = name.indexOf('/'); + if (pos > 0) + name = name.mid(0, pos); + symlinks[Option::fixPathToLocalOS(path + name)] = + project->first(vkey) + "/Current/" + name; path += version; } path += project->first(pkey).toQString(); @@ -868,6 +888,7 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) src = escapeFilePath(src); const QString dst = escapeFilePath(path + Option::dir_sep + fileInfo(fn).fileName()); bundledFiles << dst; + alldeps << dst; t << dst << ": " << src << "\n\t" << mkdir_p_asstring(path) << "\n\t"; QFileInfo fi(fileInfo(fn)); @@ -880,8 +901,21 @@ UnixMakefileGenerator::writeMakeParts(QTextStream &t) } } } + QHash::ConstIterator symIt = symlinks.constBegin(), + symEnd = symlinks.constEnd(); + for (; symIt != symEnd; ++symIt) { + bundledFiles << symIt.key(); + alldeps << symIt.key(); + t << symIt.key() << ":\n\t" + << mkdir_p_asstring(bundle_dir) << "\n\t" + << "@$(SYMLINK) " << symIt.value() << " " << bundle_dir << endl; + } } + t << endl << "all: " << escapeDependencyPath(deps) + << valGlue(escapeDependencyPaths(project->values("ALL_DEPS")), " \\\n\t\t", " \\\n\t\t", "") + << allDeps << endl << endl; + t << "dist: distdir FORCE\n\t"; t << "(cd `dirname $(DISTDIR)` && $(TAR) $(DISTNAME).tar $(DISTNAME) && $(COMPRESS) $(DISTNAME).tar)" " && $(MOVE) `dirname $(DISTDIR)`" << Option::dir_sep << "$(DISTNAME).tar.gz ." @@ -1315,44 +1349,6 @@ void UnixMakefileGenerator::init2() project->values("QMAKE_CFLAGS") += MD_flag; project->values("QMAKE_CXXFLAGS") += MD_flag; } - - if(!project->isEmpty("QMAKE_BUNDLE")) { - QString plist = fileFixify(project->first("QMAKE_INFO_PLIST").toQString(), qmake_getpwd()); - if(plist.isEmpty()) - plist = specdir() + QDir::separator() + "Info.plist." + project->first("TEMPLATE"); - if(exists(Option::fixPathToLocalOS(plist))) { - project->values("QMAKE_INFO_PLIST_OUT").append(project->first("DESTDIR") + - project->first("QMAKE_BUNDLE") + - "/Contents/Info.plist"); - project->values("ALL_DEPS") += project->first("QMAKE_INFO_PLIST_OUT"); - if(!project->isEmpty("ICON") && project->first("TEMPLATE") == "app") - project->values("ALL_DEPS") += project->first("DESTDIR") + - project->first("QMAKE_BUNDLE") + - "/Contents/Resources/" + project->first("ICON").toQString().section('/', -1); - if(!project->isEmpty("QMAKE_BUNDLE_DATA")) { - QString bundle_dir = project->first("DESTDIR") + project->first("QMAKE_BUNDLE") + "/"; - ProStringList &alldeps = project->values("ALL_DEPS"); - const ProStringList &bundle_data = project->values("QMAKE_BUNDLE_DATA"); - for(int i = 0; i < bundle_data.count(); i++) { - const ProStringList &files = project->values(ProKey(bundle_data[i] + ".files")); - QString path = bundle_dir; - const ProKey vkey(bundle_data[i] + ".version"); - const ProKey pkey(bundle_data[i] + ".path"); - if (!project->isEmpty(vkey)) { - alldeps += Option::fixPathToLocalOS(path + Option::dir_sep + project->first(pkey)); - path += project->first(vkey) + "/" + - project->first("QMAKE_FRAMEWORK_VERSION") + "/"; - } - path += project->first(pkey); - path = Option::fixPathToLocalOS(path); - for(int file = 0; file < files.count(); file++) - alldeps += path + Option::dir_sep + fileInfo(files[file].toQString()).fileName(); - } - } - } else { - warn_msg(WarnLogic, "Could not resolve Info.plist: '%s'. Check if QMAKE_INFO_PLIST points to a valid file.", plist.toLatin1().constData()); - } - } } QString diff --git a/src/corelib/doc/snippets/code/src_corelib_tools_qbitarray.cpp b/src/corelib/doc/snippets/code/src_corelib_tools_qbitarray.cpp index 4b2916a619..7fe4ea191d 100644 --- a/src/corelib/doc/snippets/code/src_corelib_tools_qbitarray.cpp +++ b/src/corelib/doc/snippets/code/src_corelib_tools_qbitarray.cpp @@ -174,3 +174,10 @@ a[0] = 1; a[1] = 0; a[2] = 1; // a: [ 1, 0, 1 ] b[0] = 1; b[1] = 1; // b: [ 1, 1 ] c = a ^ b; // c: [ 0, 1, 1 ] //! [14] + +//! [15] +QBitArray ba(4); +ba.fill(true, 1, 2); // ba: [ 0, 1, 0, 0 ] +ba.fill(true, 1, 3); // ba: [ 0, 1, 1, 0 ] +ba.fill(true, 1, 4); // ba: [ 0, 1, 1, 1 ] +//! [15] diff --git a/src/corelib/tools/qbitarray.cpp b/src/corelib/tools/qbitarray.cpp index 62c77cf10f..5c391d7e4f 100644 --- a/src/corelib/tools/qbitarray.cpp +++ b/src/corelib/tools/qbitarray.cpp @@ -282,11 +282,18 @@ void QBitArray::resize(int size) /*! \overload - Sets bits at index positions \a begin up to and excluding \a end + Sets bits at index positions \a begin up to (but not including) \a end to \a value. - \a begin and \a end must be a valid index position in the bit - array (i.e., 0 <= \a begin <= size() and 0 <= \a end <= size()). + \a begin must be a valid index position in the bit array + (0 <= \a begin < size()). + + \a end must be either a valid index position or equal to size(), in + which case the fill operation runs until the end of the array + (0 <= \a end <= size()). + + Example: + \snippet code/src_corelib_tools_qbitarray.cpp 15 */ void QBitArray::fill(bool value, int begin, int end) diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index f54ac0f277..a12946e23c 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -159,8 +159,8 @@ Q_STATIC_ASSERT_X(sizeof(qunicodechar) == 2, Q_STATIC_STRING_DATA_HEADER_INITIALIZER(Size), \ QT_UNICODE_LITERAL(str) }; \ QStringDataPtr holder = { qstring_literal.data_ptr() }; \ - const QString s(holder); \ - return s; \ + const QString qstring_literal_temp(holder); \ + return qstring_literal_temp; \ }()) \ /**/ diff --git a/src/plugins/platforms/cocoa/qcocoamenu.h b/src/plugins/platforms/cocoa/qcocoamenu.h index adaeeabd9c..85913a67e1 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.h +++ b/src/plugins/platforms/cocoa/qcocoamenu.h @@ -86,6 +86,10 @@ public: QList merged() const; void setMenuBar(QCocoaMenuBar *menuBar); QCocoaMenuBar *menuBar() const; + + void setContainingMenuItem(QCocoaMenuItem *menuItem); + QCocoaMenuItem *containingMenuItem() const; + private: QCocoaMenuItem *itemOrNull(int index) const; void insertNative(QCocoaMenuItem *item, QCocoaMenuItem *beforeItem); @@ -98,6 +102,7 @@ private: bool m_visible; quintptr m_tag; QCocoaMenuBar *m_menuBar; + QCocoaMenuItem *m_containingMenuItem; }; QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamenu.mm b/src/plugins/platforms/cocoa/qcocoamenu.mm index 84acf79517..736e02a3ca 100644 --- a/src/plugins/platforms/cocoa/qcocoamenu.mm +++ b/src/plugins/platforms/cocoa/qcocoamenu.mm @@ -222,7 +222,8 @@ QCocoaMenu::QCocoaMenu() : m_enabled(true), m_visible(true), m_tag(0), - m_menuBar(0) + m_menuBar(0), + m_containingMenuItem(0) { m_delegate = [[QCocoaMenuDelegate alloc] initWithMenu:this]; m_nativeItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; @@ -238,6 +239,10 @@ QCocoaMenu::~QCocoaMenu() if (COCOA_MENU_ANCESTOR(item) == this) SET_COCOA_MENU_ANCESTOR(item, 0); } + + if (m_containingMenuItem) + m_containingMenuItem->clearMenu(this); + QCocoaAutoReleasePool pool; [m_nativeItem setSubmenu:nil]; [m_nativeMenu release]; @@ -568,4 +573,14 @@ QCocoaMenuBar *QCocoaMenu::menuBar() const return m_menuBar; } +void QCocoaMenu::setContainingMenuItem(QCocoaMenuItem *menuItem) +{ + m_containingMenuItem = menuItem; +} + +QCocoaMenuItem *QCocoaMenu::containingMenuItem() const +{ + return m_containingMenuItem; +} + QT_END_NAMESPACE diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.h b/src/plugins/platforms/cocoa/qcocoamenuitem.h index 0ad8eada2b..4b94fa0020 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.h +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.h @@ -94,6 +94,7 @@ public: inline bool isSeparator() const { return m_isSeparator; } QCocoaMenu *menu() const { return m_menu; } + void clearMenu(QCocoaMenu *menu); MenuRole effectiveRole() const; private: diff --git a/src/plugins/platforms/cocoa/qcocoamenuitem.mm b/src/plugins/platforms/cocoa/qcocoamenuitem.mm index d0d1e7e8b8..3d3b6bf598 100644 --- a/src/plugins/platforms/cocoa/qcocoamenuitem.mm +++ b/src/plugins/platforms/cocoa/qcocoamenuitem.mm @@ -132,13 +132,19 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) { if (menu == m_menu) return; - if (m_menu && COCOA_MENU_ANCESTOR(m_menu) == this) - SET_COCOA_MENU_ANCESTOR(m_menu, 0); + + if (m_menu) { + if (COCOA_MENU_ANCESTOR(m_menu) == this) + SET_COCOA_MENU_ANCESTOR(m_menu, 0); + if (m_menu->containingMenuItem() == this) + m_menu->setContainingMenuItem(0); + } QCocoaAutoReleasePool pool; m_menu = static_cast(menu); if (m_menu) { SET_COCOA_MENU_ANCESTOR(m_menu, this); + m_menu->setContainingMenuItem(this); } else { // we previously had a menu, but no longer // clear out our item so the nexy sync() call builds a new one @@ -147,6 +153,12 @@ void QCocoaMenuItem::setMenu(QPlatformMenu *menu) } } +void QCocoaMenuItem::clearMenu(QCocoaMenu *menu) +{ + if (menu == m_menu) + m_menu = 0; +} + void QCocoaMenuItem::setVisible(bool isVisible) { m_isVisible = isVisible; diff --git a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp index 69541b4c1a..d0eb8485fa 100644 --- a/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp +++ b/src/plugins/platforms/windows/accessible/qwindowsaccessibility.cpp @@ -151,7 +151,7 @@ void QWindowsAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event) // An event has to be associated with a window, // so find the first parent that is a widget and that has a WId QAccessibleInterface *iface = event->accessibleInterface(); - if (!iface) // ### This should not happen, maybe make it an assert. + if (!iface || !iface->isValid()) return; QWindow *window = QWindowsAccessibility::windowHelper(iface); diff --git a/src/widgets/styles/qmacstyle_mac.mm b/src/widgets/styles/qmacstyle_mac.mm index 01a2f2b3bd..82718eea7f 100644 --- a/src/widgets/styles/qmacstyle_mac.mm +++ b/src/widgets/styles/qmacstyle_mac.mm @@ -93,6 +93,7 @@ #include #include #include +#include #include #include #include @@ -1728,7 +1729,7 @@ QMacStylePrivate::QMacStylePrivate() QMacStylePrivate::~QMacStylePrivate() { - Q_FOREACH (NSView *b, buttons) + Q_FOREACH (NSView *b, cocoaControls) [b release]; } @@ -1749,43 +1750,92 @@ ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags) return tds; } -NSView *QMacStylePrivate::buttonOfKind(ThemeButtonKind kind, QPoint *offset) const +static QCocoaWidget cocoaWidgetFromHIThemeButtonKind(ThemeButtonKind kind) { - NSView *bv = buttons[kind]; + QCocoaWidget w; + + switch (kind) { + case kThemePopupButton: + case kThemePopupButtonSmall: + case kThemePopupButtonMini: + w.first = QCocoaPopupButton; + break; + case kThemeComboBox: + w.first = QCocoaComboBox; + break; + case kThemeArrowButton: + w.first = QCocoaArrowButton; + break; + case kThemeCheckBox: + case kThemeCheckBoxSmall: + case kThemeCheckBoxMini: + w.first = QCocoaCheckBox; + break; + case kThemeRadioButton: + case kThemeRadioButtonSmall: + case kThemeRadioButtonMini: + w.first = QCocoaRadioButton; + break; + case kThemePushButton: + case kThemePushButtonSmall: + case kThemePushButtonMini: + w.first = QCocoaPushButton; + break; + default: + break; + } + + switch (kind) { + case kThemePushButtonSmall: + case kThemePopupButtonSmall: + case kThemeCheckBoxSmall: + case kThemeRadioButtonSmall: + w.second = QAquaSizeSmall; + break; + case kThemePushButtonMini: + case kThemePopupButtonMini: + case kThemeCheckBoxMini: + case kThemeRadioButtonMini: + w.second = QAquaSizeMini; + break; + default: + w.second = QAquaSizeLarge; + break; + } + + return w; +} + +NSView *QMacStylePrivate::cocoaControl(QCocoaWidget widget, QPoint *offset) const +{ + NSView *bv = cocoaControls[widget]; if (!bv) { - if (kind == kThemePopupButton - || kind == kThemePopupButtonSmall - || kind == kThemePopupButtonMini) + + if (widget.first == QCocoaPopupButton) bv = [[NSPopUpButton alloc] init]; - else if (kind == kThemeComboBox) + else if (widget.first == QCocoaComboBox) bv = [[NSComboBox alloc] init]; else bv = [[NSButton alloc] init]; - switch (kind) { - case kThemeArrowButton: { + switch (widget.first) { + case QCocoaArrowButton: { NSButton *bc = (NSButton *)bv; bc.buttonType = NSOnOffButton; bc.bezelStyle = NSDisclosureBezelStyle; break; } - case kThemeCheckBox: - case kThemeCheckBoxSmall: - case kThemeCheckBoxMini: { + case QCocoaCheckBox: { NSButton *bc = (NSButton *)bv; bc.buttonType = NSSwitchButton; break; } - case kThemeRadioButton: - case kThemeRadioButtonSmall: - case kThemeRadioButtonMini: { + case QCocoaRadioButton: { NSButton *bc = (NSButton *)bv; bc.buttonType = NSRadioButton; break; } - case kThemePushButton: - case kThemePushButtonSmall: - case kThemePushButtonMini: { + case QCocoaPushButton: { NSButton *bc = (NSButton *)bv; bc.buttonType = NSMomentaryPushButton; bc.bezelStyle = NSRoundedBezelStyle; @@ -1798,19 +1848,15 @@ NSView *QMacStylePrivate::buttonOfKind(ThemeButtonKind kind, QPoint *offset) con if ([bv isKindOfClass:[NSButton class]]) { NSButton *bc = (NSButton *)bv; bc.title = nil; + } - NSCell *bcell = bc.cell; - switch (kind) { - case kThemePushButtonSmall: - case kThemePopupButtonSmall: - case kThemeCheckBoxSmall: - case kThemeRadioButtonSmall: + if ([bv isKindOfClass:[NSControl class]]) { + NSCell *bcell = [(NSControl *)bv cell]; + switch (widget.second) { + case QAquaSizeSmall: bcell.controlSize = NSSmallControlSize; break; - case kThemePushButtonMini: - case kThemePopupButtonMini: - case kThemeCheckBoxMini: - case kThemeRadioButtonMini: + case QAquaSizeMini: bcell.controlSize = NSMiniControlSize; break; default: @@ -1818,36 +1864,25 @@ NSView *QMacStylePrivate::buttonOfKind(ThemeButtonKind kind, QPoint *offset) con } } - const_cast(this)->buttons.insert(kind, bv); + const_cast(this)->cocoaControls.insert(widget, bv); } if (offset) { - switch (kind) { - case kThemeRadioButton: + if (widget == QCocoaWidget(QCocoaRadioButton, QAquaSizeLarge)) offset->setY(2); - break; - case kThemeRadioButtonSmall: + else if (widget == QCocoaWidget(QCocoaRadioButton, QAquaSizeSmall)) *offset = QPoint(-1, 2); - break; - case kThemeRadioButtonMini: + else if (widget == QCocoaWidget(QCocoaRadioButton, QAquaSizeMini)) offset->setY(2); - break; - case kThemePopupButtonSmall: - case kThemeCheckBox: + else if (widget == QCocoaWidget(QCocoaPopupButton, QAquaSizeSmall) + || widget == QCocoaWidget(QCocoaCheckBox, QAquaSizeLarge)) offset->setY(1); - break; - case kThemeCheckBoxSmall: + else if (widget == QCocoaWidget(QCocoaCheckBox, QAquaSizeSmall)) offset->setX(-1); - break; - case kThemeCheckBoxMini: + else if (widget == QCocoaWidget(QCocoaCheckBox, QAquaSizeMini)) *offset = QPoint(7, 5); - break; - case kThemePopupButtonMini: + else if (widget == QCocoaWidget(QCocoaPopupButton, QAquaSizeMini)) *offset = QPoint(2, -1); - break; - default: - break; - } } return bv; @@ -1967,7 +2002,7 @@ void QMacStylePrivate::drawColorlessButton(const HIRect &macRect, HIThemeButtonD pm = QPixmap::fromImage(image); } else if ((usingYosemiteOrLater && combo && !editableCombo) || button) { QPoint offset; - NSButton *bc = (NSButton *)buttonOfKind(bdi->kind, &offset); + NSButton *bc = (NSButton *)cocoaControl(cocoaWidgetFromHIThemeButtonKind(bdi->kind), &offset); [bc highlight:pressed]; bc.enabled = bdi->state != kThemeStateUnavailable && bdi->state != kThemeStateUnavailableInactive; bc.allowsMixedState = YES; @@ -5792,7 +5827,25 @@ void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex drawToolbarButtonArrow(tb->rect, tds, cg); } if (tb->state & State_On) { - if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { + if (QSysInfo::MacintoshVersion > QSysInfo::MV_MAVERICKS) { + QWindow *window = 0; + if (widget && widget->window()) + window = widget->window()->windowHandle(); + else if (opt->styleObject) + window = opt->styleObject->property("_q_styleObjectWindow").value(); + + NSView *view = window ? (NSView *)window->winId() : nil; + bool isKey = false; + if (view) + isKey = [view.window isKeyWindow]; + + QBrush brush(isKey ? QColor(0, 0, 0, 28) + : QColor(0, 0, 0, 21)); + QPainterPath path; + path.addRoundedRect(QRectF(tb->rect.x(), tb->rect.y(), tb->rect.width(), tb->rect.height() + 4), 4, 4); + p->setRenderHint(QPainter::Antialiasing); + p->fillPath(path, brush); + } else if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) { static QPixmap pm(QLatin1String(":/qt-project.org/mac/style/images/leopard-unified-toolbar-on.png")); p->setRenderHint(QPainter::SmoothPixmapTransform); QStyleHelper::drawBorderPixmap(pm, p, tb->rect, 2, 2, 2, 2); diff --git a/src/widgets/styles/qmacstyle_mac_p_p.h b/src/widgets/styles/qmacstyle_mac_p_p.h index d145ff620b..b6267c43e6 100644 --- a/src/widgets/styles/qmacstyle_mac_p_p.h +++ b/src/widgets/styles/qmacstyle_mac_p_p.h @@ -85,6 +85,7 @@ #include #include #include +#include #include #include #include @@ -120,6 +121,17 @@ QT_BEGIN_NAMESPACE enum QAquaWidgetSize { QAquaSizeLarge = 0, QAquaSizeSmall = 1, QAquaSizeMini = 2, QAquaSizeUnknown = -1 }; +enum QCocoaWidgetKind { + QCocoaArrowButton, // Disclosure triangle, like in QTreeView + QCocoaCheckBox, + QCocoaComboBox, // Editable QComboBox + QCocoaPopupButton, // Non-editable QComboBox + QCocoaPushButton, + QCocoaRadioButton +}; + +typedef QPair QCocoaWidget; + #define SIZE(large, small, mini) \ (controlSize == QAquaSizeLarge ? (large) : controlSize == QAquaSizeSmall ? (small) : (mini)) @@ -187,7 +199,7 @@ public: void setAutoDefaultButton(QObject *button) const; - NSView *buttonOfKind(ThemeButtonKind kind, QPoint *offset) const; + NSView *cocoaControl(QCocoaWidget widget, QPoint *offset) const; void drawNSViewInRect(NSView *view, const QRect &rect, QPainter *p) const; void resolveCurrentNSView(QWindow *window); @@ -211,7 +223,7 @@ public: #endif void *indicatorBranchButtonCell; NSView *backingStoreNSView; - QHash buttons; + QHash cocoaControls; }; QT_END_NAMESPACE diff --git a/src/widgets/widgets/qplaintextedit.cpp b/src/widgets/widgets/qplaintextedit.cpp index d33ac5fd7b..b6a21f183a 100644 --- a/src/widgets/widgets/qplaintextedit.cpp +++ b/src/widgets/widgets/qplaintextedit.cpp @@ -281,7 +281,7 @@ void QPlainTextDocumentLayout::documentChanged(int from, int charsRemoved, int c Q_D(QPlainTextDocumentLayout); QTextDocument *doc = document(); int newBlockCount = doc->blockCount(); - int charsChanged = qMax(charsRemoved, charsAdded); + int charsChanged = charsRemoved + charsAdded; QTextBlock changeStartBlock = doc->findBlock(from); QTextBlock changeEndBlock = doc->findBlock(qMax(0, from + charsChanged - 1)); diff --git a/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp b/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp index cb085e77f3..d8e7fb7a99 100644 --- a/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp +++ b/tests/auto/widgets/widgets/qplaintextedit/tst_qplaintextedit.cpp @@ -147,6 +147,7 @@ private slots: void findWithRegExpReturnsFalseIfNoMoreResults(); #endif void layoutAfterMultiLineRemove(); + void undoCommandRemovesAndReinsertsBlock(); private: void createSelection(); @@ -1604,5 +1605,29 @@ void tst_QPlainTextEdit::layoutAfterMultiLineRemove() QCOMPARE(curs.blockNumber(), 3); } +void tst_QPlainTextEdit::undoCommandRemovesAndReinsertsBlock() +{ + ed->setVisible(true); + ed->setPlainText(QStringLiteral("line1\nline2")); + QCOMPARE(ed->document()->blockCount(), 2); + + QTextCursor cursor = ed->textCursor(); + cursor.movePosition(QTextCursor::Start); + cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor); + cursor.insertText(QStringLiteral("\n")); + QCOMPARE(ed->document()->blockCount(), 2); + + ed->undo(); + QCOMPARE(ed->document()->blockCount(), 2); + + QTextBlock block; + for (block = ed->document()->begin(); block != ed->document()->end(); block = block.next()) { + QVERIFY(block.isValid()); + QCOMPARE(block.length(), 6); + QVERIFY(block.layout()->lineForTextPosition(0).isValid()); + } + +} + QTEST_MAIN(tst_QPlainTextEdit) #include "tst_qplaintextedit.moc"